Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I map OAuth 2 token to UserDetails object in a resource server?

I have 2 separate Spring Boot applications, one serving as an an OAuth 2 authorization server, and the other as resource server. I'm using Spring's RemoteTokenServices in my resource server to check tokens from the authorization server. Now, I'm trying to define protected controller code in my resource server application, but I'm not sure how to map the UserDetails class to the authentication principal provided through the OAuth 2 mechanism.

I have set up my authorization server with a custom TokenEnhancer that adds more details to the token such that /oauth/check_token?token=<token> returns with custom fields, which I want to map to my resource server controllers.

In a more monolithic setup where the authorization server is also the resource server, I can define controller methods that make use of the authenticated principal this way:

//User implements UserDetails
public Map<String, Object> getResource(@AuthenticationPrincipal User user) {
    //code that uses the user object
}

However, this doesn't seem to work as straight forward in a more distributed approach. The mapping fails, and the user parameter ends up being a null object. I tried using the following approach:

public Map<String, Object> getResource(Authentication authentication) {
    //code that uses the authentication object
}

While the code above successfully maps the authentication details, it doesn't provide a way for me to directly access the custom fields I've set through the TokenEnhancer I mentioned earlier. I can't seem to find anything from the Spring docs regarding this.

like image 457
Psycho Punch Avatar asked Dec 11 '16 12:12

Psycho Punch


People also ask

How does resource server validate access token?

The access token A resource server validates such a token by making a call to the authorisation server's introspection endpoint. The token encodes the entire authorisation in itself and is cryptographically protected against tampering. JSON Web Token (JWT) has become the defacto standard for self-contained tokens.

What is an OAuth2 resource server?

A resource server is an OAuth 2.0 API server . To secure access-protected resources, it verifies access tokens from your app and authorizes access to your API. It verifies the issuer based on the token signature, validity based on token expiration, and access level based on the scopes in token claims.

What is resource in access token?

Access token. A string that represents authorization granted to the OAuth client by the resource owner. This string represents specific scopes and durations of access. It is granted by the resource owner and enforced by the OAuth server. Protected resource.

Who does separate the role of client and resource owner in OAuth?

OAuth 1.0's consumer, service provider and user become client, authorization server, resource server and resource owner in OAuth 2.0. OAuth 1.0 does not explicitly separate the roles of resource server and authorization server.


1 Answers

To resolve the issue, let me go through a bit of architectural background first. The UserDetails object automatically mapped through the @AuthenticationPrincipal comes from the principal field of the active Authentication object. A resource server controller has access to an OAuth2Authencation object, which is a specialized instance of Authentication for Spring OAuth2 security framework, just by simply declaring it as part of the method parameters.

public void controllerMethod(OAuth2Authentication authentication) {
    //controller definition
}

Knowing this, the problem now shifts to how to make sure that the getPrincipal() method in the Authentication object is an instance of my custom UserDetails class. The RemoteTokenServices I use in the resource server application uses an instance of AccessTokenConverter to interpret token details sent by the authorization server. By default, it uses DefaultAccessTokenConverter, which just sets the authentication principal as the username, which is a String. This converter makes use of UserAuthenticationConverter to convert the data coming from the authorization server into an instance of Authentication. This is what I needed to customize:

DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
tokenConverter.setUserTokenConverter(new DefaultUserAuthenticationConverter() {

    @Override
    public Authentication extractAuthentication(Map<String, ?> map) {
        Authentication authentication = super.extractAuthentication(map);
        // User is my custom UserDetails class
        User user = new User();
        user.setSpecialKey(map.get("specialKey").toString());
        return new UsernamePasswordAuthenticationToken(user,
                authentication.getCredentials(), authentication.getAuthorities());
    }

});
tokenServices.setAccessTokenConverter(tokenConverter);

With all these set up, the @AuthenticationPrincipal mechanism now works as expected.

like image 103
Psycho Punch Avatar answered Nov 08 '22 08:11

Psycho Punch