-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Session ID is lost during Token Exchange #1312
Comments
And this together with the slow token cleanup query is a bad combination #1304 |
Have you tried copying the session id into the custom claims? We don't automatically copy the sid forward into an exchanged token because depending on the semantics of the exchange that may or may not be appropriate. But I can't think of anything that would prevent that from working. |
I'm revisiting this. I think that you correctly identified the issue in the original post:
Token exchange swaps one access token for another. It should not involve any refresh token at all. The request does not include any refresh token from the original session, so (if such a refresh token exists) it shouldn't be updated. The result of the token exchange is another access token - it should not create any new refresh token. Do you have custom code that is used to create the token that is the result of the token exchange? |
We use custom implementations for IProfileService and ITokenExchangeGrantValidator. My original post has the relevant parts of the latter. Our IProfileService implementation only adds some custom claims. Otherwise, no. |
@tos-ilex - it's been a while but I took another look at this issue. Do you have the AllowOfflineAccess flag enabled for the client that is performing the token exchange? If so, do you actually need it on? Normally - or at least what I think of as "typical"/what our token exchange sample does - is that an API is exchanging one token for another. That api is configured as a client itself (since it makes token requests) and that client doesn't have offline_access enabled. If in your use case, you want a client to perform the exchange and separately obtain refresh tokens, I think there is a way to have that configuration and prevent refresh token creation (though it admittedly is a bit of a hack). Try adding this to your ITokenExchangeGrantValidator:
The underlying token generation code is checking for that flag to decide if it should issue a refresh token, so disabling it should prevent the unnecessary refresh token generation, if you need offline access on that client elsewhere. If that's the case, I'd love to hear more about your use case, because this is a hard thing to know that you ought to do and if enough folks are trying to do the same thing, it might be a good idea to change how IdentityServer interacts with refresh tokens and token exchange. Thanks in advance - and sorry for the long delay on this issue! |
The client for which I tested this behavior has OfflineAccessEnabled. The original (pre-exchange) token has the offline_access scope. We do need refresh tokens, as most of our applications are SPAs with a BFF and resource API of their own. We want to allow our users to work without entering their credentials or go back to the IdP at all again, at least for a day. So we use short-lived access tokens and longer-lived (8h) refresh tokens. Our main use case for token exchanges is this: Our SPAs link to each other and appear like "modules" of the same app to the user. When the user switches from one SPA to the next, they bring the token to the next app and it is exchanged for one that matches the scopes/audiences needed for that app's backend. We want a refresh token after the exchange as well, for the previously mentioned reasons. Each client has a BackchannelLogoutUrl to ensure that after a session that spanned multiple apps in our ecosystem, when they log out of one, they're logged out of all of them. I assume the "SessionId" in the persisted grants store is meant to be used here: so we can get rid of every grant that was part of that session. So essentially, we're using token exchanges to facilitate SSO between our apps which all use the same Duende IdentityServer as an IdP. And we like that we get refresh tokens when exchanging. Are we misusing token exchanges? I reviewed RFC8693 and it doesn't seem to be a default case to get refresh tokens from an exchange. Are we supposed to redirect back to the IdP on each app-switch instead? |
Thank you for the clarification. And the answer is: YES, you are trying to use token exchange for something it was never designed for. If you have multiple clients (with separate client ids) they are all supposed to redirect to the IdentityServer authorize endpoint to initiate their sessions. That will also enlist the clients in the IdentityServer's distributed session, which is what makes them receive logout notifications eventually. If all of your apps run on the same site as the IdentityServer you may use the BFF silent login feature to not have to do a top level redirect from each SPA. https://docs.duendesoftware.com/identityserver/v7/bff/session/management/silent-login/ |
Okay, for these flows, we will just switch to silent logins. Thank you for clarifying. For the remaining token exchanges we do, we don't need refresh tokens then. However, the token endpoint will still return refresh tokens and unnecessarily fill up the persisted grant store in the process, if the destination client has the offline_access scope allowed and no scopes are explicitly requested or offline_access is in the requested scopes. This is in line with your docs as it will be treated as requesting all allowed scopes for the client if none are specified. If I understand you correctly, there is no reason for refresh tokens to be issued during a token exchange. According to @AndersAbel 's original reply:
So perhaps it would make sense to stop the token endpoint from creating new refresh tokens if the grant type is token exchange? That is what we will do to improve performance on our end at least. If you see no need to change the default behavior on your end, I think this issue can be closed. |
There are two issues that are brought up here that we have now investigated.
The SID column not being populated is caused by the Even if I add a The correct way to add a
With this, the In a token exchange request, the new access token created will have the scopes listed in the token exchange request. If no scopes are listed, all scopes that the client has access to will be included. This also applies to the It is possible to disable the generation of a refresh from the extensions grant validator:
The result of our investigations is thus that there is no bug. The scenario is very specific and thus not well documented. It looks like you've found a solution by disabling the refresh token generation, so we'll close this issue now. |
Which version of Duende IdentityServer are you using?
7.0.5
Which version of .NET are you using?
8
Describe the bug
When calling
/connect/token
with grant_typeurn:ietf:params:oauth:grant-type:token-exchange
and an existing access token with asid
claim, the resulting access token does not have asid
claim. A resulting refresh token is not correlated with the session, and an entry with SessionId=null is added to the persisted grant store for each exchange.This is causing issues for us because we exchange tokens quite frequently and each one creates a fairly long-lived, orphaned refresh token entry in the persisted grant database that remains after logout because its session id is null.
To Reproduce
sid
claim and offline_access in its scopes.sid
claim.Expected behavior
sid
of given access token should be put into exchanged token. No extra refresh token should go into the persisted grant store, rather the existing one should be renewed or replaced.Additional context
Our TokenExchangeGrantValidator:
The text was updated successfully, but these errors were encountered: