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
Together with:
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
You have to include
@Bean
public DefaultAccessTokenConverter accessTokenConverter() {
return new DefaultAccessTokenConverter();
}
and add everything to endpoints config:
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore)
.tokenEnhancer(tokenEnhancer())
.accessTokenConverter(accessTokenConverter())
.authorizationCodeServices(codeServices)
.authenticationManager(authenticationManager)
;
}
Without it, your CustomTokenEnhancer will not work.
I solve this problem when excluded UserDetailsServiceAutoConfiguration. Like this. Maybe wiil be helpful in OAuth2 resource servers.
@SpringBootApplication(exclude = [UserDetailsServiceAutoConfiguration::class])
class Application
fun main(args: Array<String>) {
runApplication<Application>(*args)
}
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!
@Component
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("role", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return super.enhance(accessToken, authentication);
}
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
enhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer(),accessTokenConverter()));
endpoints
.tokenStore(tokenStore())
.tokenEnhancer(customTokenEnhancer())
.authenticationManager(authenticationManager);
}
@Bean
protected JwtAccessTokenConverter jwtTokenEnhancer() {
JwtAccessTokenConverter converter= new JwtAccessTokenConverter();
converter.setSigningKey("my_signing_key");
return converter;
}
@Bean
public CustomTokenConverter customTokenEnhancer() {
return new CustomTokenConverter();
}
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
output response of Custom Token Enhancer..click here
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();
}
package com.security;
import java.util.HashMap;
import java.util.Map;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.stereotype.Component;
@Component
public class CustomTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken,
OAuth2Authentication authentication) {
// TODO Auto-generated method stub
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;
}
}
Following is the xml configuration:
<bean id="tokenEnhancer" class="com.security.CustomTokenEnhancer" />
<!-- Used to create token and and every thing about them except for their persistence that is reposibility of TokenStore (Given here is a default implementation) -->
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="accessTokenValiditySeconds" value="30000000"></property>
<property name="refreshTokenValiditySeconds" value="300000000"></property>
<property name="supportRefreshToken" value="true"></property>
<property name="clientDetailsService" ref="clientDetails"></property>
<property name="tokenEnhancer" ref="tokenEnhancer" />
</bean>
That's how I was able to add extra information to the Token.