Skip to content

Token Exchange does not support id_token token type #1880

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

Open
banterCZ opened this issue Jan 16, 2025 · 16 comments
Open

Token Exchange does not support id_token token type #1880

banterCZ opened this issue Jan 16, 2025 · 16 comments
Labels
type: enhancement A general enhancement

Comments

@banterCZ
Copy link

Spring Authorization Server supports the Exchange Token feature; introduced in #60

According to RFC 8693, the token type id_token is valid, but not supported by OAuth2TokenExchangeAuthenticationConverter, which accepts only jwt and access_token.

Moreover, the class is final without any extension points. Right now, the whole AuthenticationConverter has to be implemented.

The same situation is for OAuth2TokenExchangeAuthenticationProvider, that always issues an access token.

I already asked at StackOverflow, but it seems that it is more of a feature request than just a question.

@banterCZ banterCZ added the type: enhancement A general enhancement label Jan 16, 2025
@jgrandja
Copy link
Collaborator

jgrandja commented Jan 20, 2025

@banterCZ Please note that the initial implementation of OAuth 2.0 Token Exchange was intentionally implemented with minimal capability, hence the reason why id_token is not currently supported. Our goal for the initial implementation was to implement the use cases defined in Appendix A. Additional Token Exchange Examples.

Unfortunately, there were no examples provided for id_token and it wasn't clear to us how this would be used in the wild.

Our plan is to expand the support as we discover real-world use cases from our users.

We're open to enhancements, but we first need to understand the use case. Can you please provide details on your use case and the reason why you would need to exchange an id_token?

@jgrandja jgrandja added the status: waiting-for-feedback We need additional information before we can continue label Jan 20, 2025
@banterCZ
Copy link
Author

The main idea was to exchange a long-lived id_token for another id_token of limited scope. Adding @zcgandcomp too the loop.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Jan 21, 2025
@jgrandja
Copy link
Collaborator

@banterCZ

The main idea was to exchange a long-lived id_token for another id_token of limited scope

I'll need more detail as this doesn't appear to be a valid use case at this point.

FYI, if you need a shorter-lived id_token then you can override the default 30 mins as mentioned in this comment.

... for another id_token of limited scope

What do you mean by limited scope? Please provide specific details for your use case.

@jgrandja jgrandja added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Jan 29, 2025
@jgrandja jgrandja self-assigned this Jan 29, 2025
@chenzhenjia
Copy link

This is the issue I submitted, but it was closed.
#1866

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Feb 5, 2025
@jgrandja
Copy link
Collaborator

jgrandja commented Feb 5, 2025

@chenzhenjia I don't see how gh-1866 is related to this issue? The UserInfo endpoint accepts an access token NOT an ID Token. This issue is specific to exchanging an ID Token. If you have further comments, please comment in gh-1866.

@jgrandja jgrandja added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Feb 5, 2025
@zcgandcomp
Copy link

Hello @jgrandja ,

The scenario for the Token Exchange of id_token is as follows:

  • A user initiates standard login flow, e.g., via Code Authorization, and uses scope openid and profile.
  • After the flow, the user has an id_token with long validity (days) containing standard claims for profile scope.
  • The client wants to send an id_token to another system, let's say, for a newsletter subscription. But not with all claims but only with the email claim.
  • The client invokes the token exchange, with the requested scope email and receives exchanged id_token with reduced claims.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Feb 5, 2025
@jgrandja
Copy link
Collaborator

@zcgandcomp Thanks for the details.

The client wants to send an id_token to another system, let's say, for a newsletter subscription.

This does not appear to be a valid use case as clients are not intended to send ID tokens to a Resource Server (newsletter subscription service). I'm curious, why are you sending an ID Token instead of an access token to the resource server? Why can't you send an (exchanged) access token with limited scope?

@jgrandja jgrandja added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Feb 11, 2025
@zcgandcomp
Copy link

I see an ID token as my digitally signed identity, so the newsletter server receives some data signed by the authorization server that can be trusted for this purpose.
I cannot send the access token to the newsletter server, as it trusts my ID tokens but uses access tokens issued by another authorization server.

So, the ID tokens are used to share a limited scope of data between different systems. I'm signing up using my ID token, but after the signup, the authentication and authorization are handled by the newsletter authorization server).

True - the user can get a new ID token with limited claims (by using the issued access token), but the ID tokens usually have longer validity, refresh tokens might be expired or not used) so an easier solution is to exchange ID tokens directly.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Feb 14, 2025
@jgrandja
Copy link
Collaborator

jgrandja commented Mar 4, 2025

@zcgandcomp As per section 3. Authentication:

OpenID Connect performs authentication to log in the End-User or to determine that the End-User is already logged in. OpenID Connect returns the result of the Authentication performed by the Server to the Client in a secure manner so that the Client can rely on it. For this reason, the Client is called Relying Party (RP) in this case.

The Authentication result is returned in an ID Token, as defined in Section 2. It has Claims expressing such information as the Issuer, the Subject Identifier, when the authentication was performed, etc.

The ID Token is intended for the Client during the authentication flow and it relies on the ID Token to successfully authenticate the end user.

I see an ID token as my digitally signed identity

This does not align with the intended use of the ID Token. A more common use case would be an X.509 certificate to represent your digital identity.

Unfortunately, RFC 8693 OAuth 2.0 Token Exchange does not specify a use case for exchanging an ID Token.

At this point, this issue will likely get closed as I don't believe passing an ID Token to a resource server is a valid use case. If you can provide me a reference of this use case that is used by a well known OIDC provider then we can look into this further.

@jgrandja jgrandja removed the status: feedback-provided Feedback has been provided label Mar 4, 2025
@jgrandja jgrandja added the status: waiting-for-feedback We need additional information before we can continue label Mar 17, 2025
@spring-projects-issues
Copy link
Collaborator

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

@spring-projects-issues spring-projects-issues added the status: feedback-reminder We've sent a reminder that we need additional information before we can continue label Mar 24, 2025
@zcgandcomp
Copy link

Hello,

let's look at it this way:

If I want to have another IDToken with fewer claims I need to:

  • Log user with client A,
  • get access token for client A,
  • get ID token with client A,
  • ... access token expires
  • Log user with client A,
  • get access token for client A,
  • exchange the access token for a token for the client B
  • get ID token with client B (with appropriate claims for client B)

Our intention was:

  • Log user with client A,
  • get access token for client A,
  • get ID token with client A,
  • ... access token expires
  • exchange the ID token for a token for the client B (with appropriate claims)

The flow has fewer steps and is more robust, and is less frictionless (no user interaction is needed).
And needless to say, the first flow has to be workarounded as the Token exchange in the Spring Authorization server is returning an invalid scope claim - https://datatracker.ietf.org/doc/html/rfc8693#name-scope-scopes-claim.

The RFC is vague, I believe on purpose, as it doesn't want to restrict too much and enables solving real-life situations.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue status: feedback-reminder We've sent a reminder that we need additional information before we can continue labels Mar 25, 2025
@jgrandja
Copy link
Collaborator

@zcgandcomp

Our intention was:

  • Log user with client A,
  • get access token for client A,
  • get ID token with client A,
  • ... access token expires
  • exchange the ID token for a token for the client B (with appropriate claims)

An alternative to token exchange is you could use the refresh_token grant.

You need to ensure a refresh token is returned so in the above flow you need "get refresh token for client A" as well. Now client A has an access token, ID token and refresh token. Then you can perform a refresh_token flow and the response will contain a new ID token with a refreshed iat claim. During the refresh_token grant flow, you can configure a custom OAuth2TokenCustomizer to customize the ID Token however you need. The only thing is that it would still be associated to client A NOT client B.

The RFC is vague, I believe on purpose, as it doesn't want to restrict too much and enables solving real-life situations.

Maybe. Either way, the flows you have described are completely custom and this would be the responsibility of the consuming application.

The main responsibility of Spring Authorization Server is to provide implementations of the various specifications. If off-spec or custom behaviours are needed, then it's the responsibility of the consuming application. If there are hooks in the framework that are needed to implement these customizations then we can consider adding new hooks. Please look into this on your end and if you need another extension point then we can look into this further.

As of now, I don't see any changes on our end. I'll leave this issue open for now and we'll see if other users are looking for an ID Token exchange flow.

@jgrandja jgrandja removed the status: feedback-provided Feedback has been provided label Mar 28, 2025
@jgrandja jgrandja removed their assignment Mar 28, 2025
@zcgandcomp
Copy link

Hello,

The flow is nothing off-spec. The spec clearly allows to exchange ID Tokens: https://datatracker.ietf.org/doc/html/rfc8693#name-token-type-identifiers

We can call the exchange of urn:ietf:params:oauth:token-type:jwt as completely custom, exchange of urn:ietf:params:oauth:token-type:id_token is well within the specification.

I can accept you are not going to support it in core, but not the statement that this is off-spec.

Can you please check: #877 because the implementation of the scope claim is actually not according to the specification (for token exchange).

@jgrandja
Copy link
Collaborator

To be more clear, when I say off-spec, I'm referring to this specific part in your flow:

exchange the ID token for a token for the client B (with appropriate claims)

You're switching client A to client B, which is completely custom. Again, the spec allows ID Token exchange but does not provide any other information whatsoever on the requirements of the exchange. At a minimum, there should at least be some validation requirements before allowing the exchange to proceed. Furthermore, I would almost guarantee that switching client ID's would NOT be allowed.

Thanks for the reminder of gh-877, I will look into that shortly.

@zcgandcomp
Copy link

I'm sorry we did not provide consistent requests. We will try to put it together to avoid misunderstanding.

I haven't described the client switching clearly. I was trying to make an analogy to the https://datatracker.ietf.org/doc/html/rfc8693#name-example-token-exchange
There is an access token issued for client A. The resource server (i.e., client B) is exchanging the access token for a new access token.

But we can put together the same description as RFC to make it clear.

@jgrandja
Copy link
Collaborator

@zcgandcomp Yes please put together a minimal sample of the flow you are trying to achieve and then I can look at it to better understand and see if we can come up with a solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

5 participants