OAuth2 Token Exchange Lab
Last updated
Last updated
The goal of this lab is to demonstrate the OAuth2 Token Exchange feature in Spring Security and Spring Authorization Server.
According to , the Token Exchange extension defines a mechanism for a client to obtain its own tokens given a separate set of tokens. This has several different applications, including:
Single-sign-on between multiple mobile apps without launching a web browser
A resource server exchanging a client's tokens for its own tokens
The defines how to request and obtain security tokens from OAuth 2.0 authorization servers, including security tokens employing impersonation and delegation.
The analogy between impersonation and delegation is similar to the difference between a user impersonating another user and a user delegating their authority to another user.
Real-world metaphor
Pretending to be someone else
Acting with someone's permission
Actor identity
Hidden (disguised as the subject)
Transparent (both actor and subject known)
Audit logs
Show only the impersonated user
Show both the original and delegated user
Power analogy
Borrowing someone's ID
Acting under power of attorney
Common association
βLogin as userβ in admin panels
Service A calling Service B for User X
The extension defines a mechanism for a client to obtain its own tokens given a separate set of tokens. This has several different applications, including:
Definition
Acting as another user
Acting on behalf of another user
Identity in token
Subject only (sub = user
)
Subject + Actor (sub = user
, act = caller
)
Who appears to act
The impersonated user
The original caller
Access Rights
Fully inherit target user's rights
Target user's rights, caller identity preserved
Audit Visibility
Limited β only the impersonated user
Full β both subject and actor are visible
Use Case Example
Admin logs in as user
API calling downstream with user context
Security Risk
Higher (hides true actor)
Lower (identity chain is preserved)
This repository directory token-exchange
contains all required component apps of the OAuth2 Token Exchange lab implemented with Spring Security and Spring
Authorization Server.
It includes four modules:
spring-authorization-server: A Spring Boot application that acts as an OAuth2 authorization server. It is pre-configured with the required endpoints and client registrations.
token-exchange-client: A Spring Boot application that acts as an OAuth2 client.
token-exchange-resource-server: A Spring Boot application that acts as an OAuth2 resource server performing the token exchange and then calls the final target resource server.
target-resource-server: A Spring Boot application that acts as a target resource server that is called with the exchanged token.
To implement OAuth2 Token Exchange, you require at least:
The scenario that will be implemented is as follows:
The client application (http://localhost:8080) requests an access token from the authorization server (http://localhost:9000).
The client application uses the access token to call the first resource server (http://localhost:9091).
The first resource server performs a token exchange with the authorization server (http://localhost:9000) to get a new access token for the target resource server (http://localhost:9092).
The target resource server (http://localhost:9092) when called with the exchanged access token, will check for expected audience claim value.
http://localhost:9000
Spring Authorization Server
The Authorization Server issuing all tokens
http://localhost:8080/client
OAuth2 Client Application
The client application triggering the flow by logging in to the Authorization Server and calling the first resource server
http://localhost:9091
Token Exchange Resource Server
The first resource server being called by the client and performing the token exchange before calling the target resource server
http://localhost:9092
Target Resource Server
The final target resource server being called with the exchanged token
The Spring Authorization Server is pre-configured with the required endpoints and client registrations.
The following OAuth2 client registrations are configured:
token-exchange-client: This client is used by the first resource server to perform the token exchange with the authorization server
You can look up the client registrations in the SingleTenantClientConfiguration
class:
The OAuth2 Token Exchange is this lab performs an impersonation of the user adding access rights for the API of the target resource server (see the audience
claim).
The audience
claim is used to identify the target resource server that is being called with the exchanged token. The audience
claim is a list and is set to the API resources of the target resource server.
The audience
claim is also set in the SingleTenantClientConfiguration
class (in addition, it also sets the roles
claim):
Start the Spring Authorization Server by running the SpringAuthorizationServerApplication
class in your IDE or using the Maven Spring Boot plugin.
In case you are using IntelliJ IDEA, you can also use the provided Http Client to test the authorization server located in folder labs/token-exchange/spring-authorization-server/requests
.
The OAuth2 Client Application is a Spring Boot application that acts as an OAuth2 client. It is responsible for authenticating the user and retrieving an access token for the first resource server (token exchange resource server).
The client application is pre-configured with the required endpoints and client registrations.
The following OAuth2 client registrations are configured in the application.yml
file:
The corresponding OAuth2 client authentication is configured in the WebSecurityConfiguration
class:
The client application is pre-configured with the following endpoints:
/api/hello
: The login page for the client application. This page is used to authenticate the user and retrieve an access token for the first resource server.
The corresponding rest controller is implemented in the ClientApi
class:
This is an implementation of the OAuth 2.0 βToken Relayβ pattern, where:
The client authenticates the user
@RegisteredOAuth2AuthorizedClient
Retrieves current user's OAuth2 access token. If no token is available it triggers the OAuth2 Authorization Code
+ PKCE
flow using the messaging-client-oidc
client
RestClient
with Bearer
token
Sends the token to another secured downstream service (the token exchange resource server)
Endpoint /api/hello
Returns the response from the protected API
Start the OAuth2 Client Application by running the TokenExchangeClientApplication
class in your IDE or using the Maven Spring Boot plugin.
The target resource server is a Spring Boot application that acts as a target resource server that is called with the exchanged token.
The target resource server is configured to validate the exchanged token with the public key from the issuer at http://localhost:9000
and check for the expected audience claim value http://localhost:9092/api/messages
. This is done in the application.yml
file:
The corresponding configuration code to enable JWT validation is implemented in the WebSecurityConfiguration
class:
In summary, the configuration of the target resource server is as follows:
Configures the application as a resource server that validates JWTs.
issuer-uri: The URL of the OAuth 2.0 authorization server (usually the OpenID Provider).
Spring uses this to:
Automatically discover the public key (/.well-known/openid-configuration)
Validate the signature of incoming JWTs with the public key
Validate the iss (issuer) claim in incoming JWTs
Validate aud (audience) claim in incoming JWTs
β
Expected: The target resource server does only accept exchanged tokens with the audience claim set to http://localhost:9092/api/messages
and no tokens provided directly from the client application.
Start the Target Resource Server by running the TargetResourceServerApplication
class in your IDE or using the Maven Spring Boot plugin.
Finally, the token exchange resource server is a Spring Boot application that acts as an OAuth2 resource server performing the token exchange and then calls the final target resource server (the target resource server we just have started).
Here you will add the finalizing code to perform the token exchange with the authorization server and call the target resource server with the exchanged token.
The token exchange resource server is configured to validate the exchanged token with the public key from the issuer at http://localhost:9000
and check for the expected audience claim value http://localhost:9092/api/messages
. This is done in the application.yml
file:
The corresponding configuration code to enable JWT validation is implemented in the WebSecurityConfiguration
class:
Start the Token Exchange Resource Server by running the TokenExchangeResourceServerApplication
class in your IDE or using the Maven Spring Boot plugin.
As we started all the applications, we can now try the complete flow.
You will be redirected to the login page of the authorization server.
Log in with the user credentials user/password
.
You will be redirected back to the client application and see a greeting message, but it will not show the message from the target resource server. That is because it just uses the token from the client application to call the target resource server.
The token exchange resource server must be configured to perform the token exchange with the authorization server and call the target resource server with the exchanged token.
First, we need to add the Spring Security components required to perform the token exchange. This is done in the WebSecurityConfiguration
class:
π§ Purpose of the OAuth2AuthorizedClientManager
Bean
OAuth2AuthorizedClientManager
is used to manage OAuth 2.0 clients, typically to:
Automatically acquire or refresh tokens,
Perform token exchanges,
Handle the persistence of authorized clients.
π What This Specific Code Does
Creates a provider that knows how to perform OAuth 2.0 Token Exchange:
Builds an OAuth2AuthorizedClientProvider
with only token exchange capabilities.
You could also include other providers here (e.g., refresh_token, client_credentials, etc.) if needed:
The next lines create a DefaultOAuth2AuthorizedClientManager
that uses:
ClientRegistrationRepository
to look up client configurations (client-id, client-secret, scopes, etc.)
OAuth2AuthorizedClientRepository
to store/retrieve authorized clients (e.g., from the session or request context).
The final line configures the manager to use the custom provider, which supports token exchange.
π§ Purpose of the OAuth2AccessTokenResponseClient
Bean
This tells Spring Security how to handle the token exchange request and response. The RestClientTokenExchangeTokenResponseClient
is a built-in implementation that uses a REST client to perform the token exchange.
The final step is to extend the existing API endpoint /api/messages
to perform the token exchange with the authorization server and successfully call the target resource server with the exchanged token.
To achieve this, we need to change the ClientApi
class to use the OAuth2AuthorizedClientManager
to perform the token exchange and call the target resource server with the exchanged token.
π Breakdown of What this Method Does
π Accept secured requests
Accepts incoming requests with a JWT token
π Perform token exchange
Uses the incoming token to obtain a new access token
π Call downstream service
Calls another resource server with the new token
π¦ Aggregate response
Returns a message containing data from the target resource
It receives an authenticated user (JwtAuthenticationToken) representing a valid JWT-based access token that was used to call this resource server.
It creates an OAuth2AuthorizeRequest
object that specifies the client registration ID (from application.yml) and the principal (the authenticated user). Finally, it triggers token exchange using the OAuth2AuthorizedClientManager and the jwtAuthentication token as the subject token.
Ensures the token exchange worked and an access token was received.
Now it uses the exchanged token and passes it as a bearer token to make a request to the target resource server.
Now that we have implemented the token exchange, we need to restart the token exchange resource server to apply the changes.
β Congratulations: You have implemented a complete OAuth2 Token Exchange flow using Spring Security and Spring Authorization Server. You have learned how to configure the authorization server, the client application, the token exchange resource server, and the target resource server. You have also learned how to perform the token exchange and call the target resource server with the exchanged token.
β That also completes the workshop! You have learned about three important features of Spring Security
Passkeys
Enhanced Authorization (i.e., authorize domain objects)
OAuth2 Token Exchange
Details on implementing the OAuth2 Token Exchange feature using Spring Security can be found in the .
messaging-client: This client is used to authenticate the user and retrieve an access token for the first resource server. This client is configured as a public client and uses the authorization code grant type including .
The Spring Authorization Server will be available at . You can check if the authorization server is running by opening the following URL in your browser: .
The client uses the userβs token to call a downstream resource server on their behalf with the .
The OAuth2 Client Application will be available at . If you navigate to this address in your browser, you will be redirected to the login page of the authorization server. Please do not continue at this point. We will continue from this point in the next steps.
The Target Resource Server will be available at . You can check if the target resource server is running by opening the following URL in your browser: . Expect a 401 Unauthorized error, as the target resource server is configured to require a valid JWT token for access.
The Token Exchange Resource Server will be available at . You can check if the token exchange resource server is running by opening the following URL in your browser: . Expect a 401 Unauthorized error, as the token exchange resource server is configured to require a valid JWT token for access.
Open your browser and navigate to .
After it successfully has restarted, retry the complete flow by navigating to in your browser. This time the complete flow should work successfully, and you should see a message from the target resource server.
If you have any questions or feedback, please feel free to reach out to me (just connect via ). I hope you enjoyed the workshop and learned something new!