-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Simplify customizing the access token response #925
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
Comments
There is org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer example here |
I think token endpoint response additional parameters and token claims are different things, and by default token claims are not in the response. For example, the idToken attribute of OIDC is officially stored in additionalParameters: // ----- ID token -----
OidcIdToken idToken;
if (authorizationRequest.getScopes().contains(OidcScopes.OPENID)) {
// @formatter:off
tokenContext = tokenContextBuilder
.tokenType(ID_TOKEN_TOKEN_TYPE)
.authorization(authorizationBuilder.build()) // ID token customizer may need access to the access token and/or refresh token
.build();
// @formatter:on
OAuth2Token generatedIdToken = this.tokenGenerator.generate(tokenContext);
if (!(generatedIdToken instanceof Jwt)) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
"The token generator failed to generate the ID token.", ERROR_URI);
throw new OAuth2AuthenticationException(error);
}
idToken = new OidcIdToken(generatedIdToken.getTokenValue(), generatedIdToken.getIssuedAt(),
generatedIdToken.getExpiresAt(), ((Jwt) generatedIdToken).getClaims());
authorizationBuilder.token(idToken, (metadata) ->
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()));
} else {
idToken = null;
}
authorization = authorizationBuilder.build();
// Invalidate the authorization code as it can only be used once
authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, authorizationCode.getToken());
this.authorizationService.save(authorization);
Map<String, Object> additionalParameters = Collections.emptyMap();
if (idToken != null) {
additionalParameters = new HashMap<>();
additionalParameters.put(OidcParameterNames.ID_TOKEN, idToken.getTokenValue());
} |
@Hccake As mentioned by @javamachr, the OAuth2TokenCustomizer is the equivalent to authorizationServerConfigurer.tokenEndpoint.accessTokenResponseHandler() provides the ability to customize the OAuth 2.0 Access Token Response parameters, however, the parameters are not necessarily the same as the claims contained in the access token. Can you be more specific on what type of parameters you want to customize (add?) to the OAuth 2.0 Access Token Response? |
For some clients, when returning access_token, want to directly return some user information, such as When using Using |
As per the reference documentation for
You can register a The client can obtain the user information claims from the OAuth2 Token Introspection Endpoint on a subsequent call. If you need the user information returned in the access token response, then you can supply a custom public static class CustomAccessTokenResponseHandler implements AuthenticationSuccessHandler {
private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter =
new OAuth2AccessTokenResponseHttpMessageConverter();
private OAuth2AuthorizationService authorizationService;
public CustomAccessTokenResponseHandler(OAuth2AuthorizationService authorizationService) {
this.authorizationService = authorizationService;
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
(OAuth2AccessTokenAuthenticationToken) authentication;
OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken();
OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken();
Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters();
// Lookup the authorization using the access token
OAuth2Authorization authorization = this.authorizationService.findByToken(
accessToken.getTokenValue(), OAuth2TokenType.ACCESS_TOKEN);
Map<String, Object> opaqueTokenClaims = authorization.getAccessToken().getClaims();
Authentication userPrincipal = authorization.getAttribute(Principal.class.getName());
OAuth2AccessTokenResponse.Builder builder =
OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
.tokenType(accessToken.getTokenType())
.scopes(accessToken.getScopes());
if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) {
builder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()));
}
if (refreshToken != null) {
builder.refreshToken(refreshToken.getTokenValue());
}
if (!CollectionUtils.isEmpty(additionalParameters)) {
builder.additionalParameters(additionalParameters);
}
// TODO Add custom response parameters using `opaqueTokenClaims` and/or `userPrincipal`
OAuth2AccessTokenResponse accessTokenResponse = builder.build();
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse);
}
} I'll close this as either solution provided will work. |
As I said at the beginning, a custom If we have a private void sendAccessTokenResponse(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
(OAuth2AccessTokenAuthenticationToken) authentication;
OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken();
OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken();
OAuth2AccessTokenResponse.Builder builder =
OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
.tokenType(accessToken.getTokenType())
.scopes(accessToken.getScopes());
if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) {
builder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()));
}
if (refreshToken != null) {
builder.refreshToken(refreshToken.getTokenValue());
}
Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters();
// The user only needs to register a tokenResponseEnhancer to implement
// the attribute extension of the access token response
if(tokenResponseEnhancer != null) {
additionalParameters = tokenResponseEnhancer.enhance(accessTokenAuthentication, additionalParameters);
}
if (!CollectionUtils.isEmpty(additionalParameters)) {
builder.additionalParameters(additionalParameters);
}
OAuth2AccessTokenResponse accessTokenResponse = builder.build();
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse);
} Or separate the construction and writing of private void sendAccessTokenResponse(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
OAuth2AccessTokenResponse accessTokenResponse = this.accessTokenResponseBuilder.build(authentication);
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse);
}
|
@Hccake The solution provided with However, I will re-open this ticket and we may consider adding an improvement later on at some point. |
Closing as duplicate of gh-1429 |
In OAuth2.1 draft,Token Response can be added with additional parameters.
It can currently be solved by replacing the default
AuthenticationSuccessHandler
.Hope there is a component that can be easily implemented, like
TokenEnhancer
inspring-security-oauth2
.The text was updated successfully, but these errors were encountered: