Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

can I include user information while issuing an access token?

I have seen in some oauth2 implementations additional information on the response returned by the authorization server when it issues access tokens. I'm wondering if there is a way to accomplish this using spring-security-oauth2. I would love to be able to include some user authorities on the access token response so that my consuming applications don't need to manage the user authorities but can still set the user on their own security contexts and apply any of their own spring-security checks.

  1. How would I get that information on the access token response?
  2. How would I intercept that information on the oauth2 client side and set it on the security context?

I suppose another option would be to use JWT tokens and share the appropriate information with the client applications so that they can parse the user / authorities out of the token and set it on the context. This makes me more uncomfortable since I'd prefer to be in control of which client applications could have access to this information (trusted apps only) and AFAIK only the authorization server and resource server should know how to parse the JWT tokens.

like image 756
RutledgePaulV Avatar asked Feb 13 '15 03:02

RutledgePaulV


People also ask

Does access token contain user information?

Access tokens cannot tell if the user has authenticated. The only user information the access token possesses is the user ID, located in the sub claim. In your applications, treat access tokens as opaque strings since they are meant for APIs.

What should be in an access token?

In computer systems, an access token contains the security credentials for a login session and identifies the user, the user's groups, the user's privileges, and, in some cases, a particular application.

What is the difference between ID token and access token?

Access tokens are what the OAuth client uses to make requests to an API. The access token is meant to be read and validated by the API. An ID token contains information about what happened when a user authenticated, and is intended to be read by the OAuth client.


2 Answers

You will need to implement a custom TokenEnhancer like so:

public class CustomTokenEnhancer implements TokenEnhancer {      @Override     public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {         User user = (User) authentication.getPrincipal();         final Map<String, Object> additionalInfo = new HashMap<>();          additionalInfo.put("customInfo", "some_stuff_here");         additionalInfo.put("authorities", user.getAuthorities());          ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);          return accessToken;     }  } 

and add it to your AuthorizationServerConfigurerAdapter as a bean with the corresponding setters

@Configuration @EnableAuthorizationServer protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {      // Some autowired stuff here      @Override     public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {         // @formatter:off         endpoints             // ...             .tokenEnhancer(tokenEnhancer());         // @formatter:on     }      @Bean     @Primary     public AuthorizationServerTokenServices tokenServices() {         DefaultTokenServices tokenServices = new DefaultTokenServices();         // ...         tokenServices.setTokenEnhancer(tokenEnhancer());         return tokenServices;     }      // Some @Bean here like tokenStore      @Bean     public TokenEnhancer tokenEnhancer() {         return new CustomTokenEnhancer();     }  } 

then in a controller (for example)

@RestController public class MyController {      @Autowired     private AuthorizationServerTokenServices tokenServices;      @RequestMapping(value = "/getSomething", method = RequestMethod.GET)     public String getSection(OAuth2Authentication authentication) {         Map<String, Object> additionalInfo = tokenServices.getAccessToken(authentication).getAdditionalInformation();          String customInfo = (String) additionalInfo.get("customInfo");         Collection<? extends GrantedAuthority> authorities = (Collection<? extends GrantedAuthority>) additionalInfo.get("authorities");          // Play with authorities          return customInfo;     }  } 

I'm personnaly using a JDBC TokenStore so my "Some autowired stuff here" are corresponding to some @Autowired Datasource, PasswordEncoder and what not.

Hope this helped!

like image 83
Philippe Avatar answered Oct 08 '22 05:10

Philippe


If you are using Spring's JwtAccessTokenConverter or DefaultAccessTokenConverter you can add your custom CustomTokenEnhancer (see first response) and apply it using a TokenEnhancerChain like this:

@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {      TokenEnhancerChain enhancerChain = new TokenEnhancerChain();     enhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer(), accessTokenConverter()));      endpoints.tokenStore(tokenStore())             .tokenEnhancer(enhancerChain)             .authenticationManager(authenticationManager); }  @Bean protected JwtAccessTokenConverter jwtTokenEnhancer() {     JwtAccessTokenConverter converter = new JwtAccessTokenConverter();     converter.setSigningKey("my_signing_key");     return converter; }  @Bean public TokenEnhancer customTokenEnhancer() {     return new CustomTokenEnhancer(); } 

Another solution is to create a custom TokenConverter that extends Spring's JwtAccessTokenConverter and override the enhance() method with your custom claims.

public class CustomTokenConverter extends JwtAccessTokenConverter {  @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {      final Map<String, Object> additionalInfo = new HashMap<>();     additionalInfo.put("customized", "true");     User user = (User) authentication.getPrincipal();     additionalInfo.put("isAdmin", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()).contains("BASF_ADMIN"));     ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);      return super.enhance(accessToken, authentication);     } }  

And then:

@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {      endpoints.tokenStore(tokenStore())             .tokenEnhancer(customTokenEnhancer())             .authenticationManager(authenticationManager); }  @Bean public CustomTokenConverter customTokenEnhancer() {     return new CustomTokenConverter(); } 
like image 20
jchrbrt Avatar answered Oct 08 '22 03:10

jchrbrt