Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Spring Security OAuth2, what's the right way to refresh the stored authentication in the TokenStore?

We're using resource-owner credentials grant type (with oauth2:password in security-config.xml. Let's play out this scenario to explain my predicament:

  1. Bob was created with authorities ROLE_USER
  2. Bob tries to access an oauth2 protected resource
  3. Bob uses the official mobile app to access it, so the client credentials are correct
  4. Bob's access token is created and stored in the TokenStore, keyed on his username, client_id, and scope. (see DefaultAuthenticationKeyGenerator.java)
  5. Bob's phone tries to call the protected services with this access token, but those services require users to have an authority of ROLE_MOBILE_USER.
  6. Bob contacts the db owner and has ROLE_MOBLE_USER added to his user in the database.
  7. Bob tries to get another access token, but the DefaultTokenServices returns him the same, non-working access token.
  8. The only way to take advantage of his new authority is to wait until his old access token expires so he can get a new access token with the correct authority.

There are a number of ways to address this.

For one, the administration app that adds ROLE_MOBILE_USER to Bob's authorities could then clear all access tokens and authorizations in the database. This way the DefaultTokenServices will just create a new one with the correct authorities serialized as his new OAuth2Authentication. However we may not want the Administration webapp to be concerned with OAuth at this point (at least not yet). If possible we'd like to keep the administration app concerns as concise as possible, and right now there are no dependencies on oauth.

We could expose the DELETE method to the /oauth/access_token endpoint and tell the mobile app to try deleting that access token and re-requesting one, just in case the stored authorities are stale. This feels more like a work-around though.

Finally I can serialize the authorities in my own defined AuthenticationKeyGenerator. It would basically use the username, client_id, scope, and authorities of the authorization and perform the same digest algorithm on them. This way when Bob tries to log in he'll get the same access token, but the underlying token store will recognize that he has a different authentication (from the authentication manager in the token granter bean) and refresh its database. The problem I have with this solution is that it simply relies on the implementation behavior of the underlying token store (though both InMemoryTokenStore and JdbcTokenStore behave this way).

Can you think of any better/cleaner solutions? Am I over-thinking this?

Thanks in advance.

like image 325
Joe Avatar asked Aug 15 '13 20:08

Joe


People also ask

How do I refresh my authentication token?

To refresh your access token as well as an ID token, you send a token request with a grant_type of refresh_token . Be sure to include the openid scope when you want to refresh the ID token. If the refresh token is valid, then you get back a new access and the refresh token.

How does OAuth2 refresh token work?

The Refresh Token grant type is used by clients to exchange a refresh token for an access token when the access token has expired. This allows clients to continue to have a valid access token without further interaction with the user.

Why do we need refresh token in OAuth2?

A refresh token can help you balance security with usability. Since refresh tokens are typically longer-lived, you can use them to request new access tokens after the shorter-lived access tokens expire.

What is OAuth 2.0 and how it works in Spring boot?

OAuth2 is an authorization framework that enables the application Web Security to access the resources from the client. To build an OAuth2 application, we need to focus on the Grant Type (Authorization code), Client ID and Client secret.


2 Answers

I resolved this issue in my app by deleting all tokens for a given user when the authentication information is sent.

Use a custom AuthenticationProvider bean.

@Component("authenticationProvider")
public class AuthenticationProviderImpl implements AuthenticationProvider

Autowire in the token store bean.

@Autowired
@Qualifier("tokenStore")
private TokenStore tokenStore;

Then in the authenticate method, remove all tokens for a given user if the credentials are passed a second time.

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;

    try {
         //Do authentication

        //Delete previous tokens
        Collection<OAuth2AccessToken> tokenCollection = tokenStore.findTokensByUserName(token.getName());
        for (OAuth2AccessToken oToken : tokenCollection){
            tokenStore.removeAccessToken(oToken);
        }

        //return Authentication;
    }
}

Most of the requests will be using a token and will bypass this entirely, but when the credentials are passed, a new token will be generated. This token will be associated with the new authentication object which will include all new roles, and changes made to the user.

like image 130
Scott Leonard Avatar answered Sep 28 '22 00:09

Scott Leonard


I had the same problem and I solved it with this function:

protected void reloadUserFromSecurityContext(SecurityContext securityContext, Person user){
    OAuth2Authentication requestingUser = (OAuth2Authentication) securityContext.getUserPrincipal();
    Object principal = (PersonUserDetails) requestingUser.getUserAuthentication().getPrincipal();
    if(principal instanceof PersonUserDetails) {
        ((PersonUserDetails) principal).setPerson(user);
    }
    OAuth2AuthenticationDetails authDetails = (OAuth2AuthenticationDetails) requestingUser.getDetails();
    OAuth2AccessToken tokenStored = jdbcTokenStore.readAccessToken(authDetails.getTokenValue());
    jdbcTokenStore.storeAccessToken(tokenStored, requestingUser);
}

That is a example for update an attribute for PersonUserDetails object that is in OAuth2Authentication

like image 25
David Fernandez Vazquez Avatar answered Sep 28 '22 01:09

David Fernandez Vazquez