Spring Security Oauth2: Flow to Handling Expired AccessToken

16,669

The OAuth2 Spec has a section on refreshing access tokens. It's implemented in a pretty standard way in Spring OAuth (you just post the refresh token to the /token endpoint).

BTW for SSO you don't normally need a Resource Server. But that's a different question.

Share:
16,669

Related videos on Youtube

ericagon
Author by

ericagon

Updated on September 19, 2022

Comments

  • ericagon
    ericagon over 1 year

    I am just a beginner in Spring Security Oauth2. I try to make Authorization Server and Resource Server (separated and connect to JDBC) and the purpose is to make Single Sign-On. My flow success to get accesstoken and refreshtoken from Authorization Server. My accesstoken always used as parameter to access Resouce Server, and this is give a response back.

    for example http://127.0.0.1:8080/Resource/res/staff?access_token=xxxxxxxxxxxxxxxxxxxxx
    

    My problem, if the accesstoken expired, the spring security will prevent to access the page and give the error exception. When I must use the refreshtoken to get new token? Or is my flow wrong? Is there other flow to renew accesstoken?

    Thanks

    Edited:

    FYI: I want to make SSO using Spring Security Oauth2. I have several Apps Server (use Spring Framework) and I want to make one server that responsible to manage the login. And I want to make the Apps Server become Resource Server (also the Client) So I make one Authorization Server with Spring Security Oauth2. The user who wants to access the protected Resource Server must login to Authorization Server (the Resource Server authorize to Authorization Server). It will get a code and then the Resource Server will exchange this code with accessToken and refreshToken. This flow is success.

    I can also request the new accessToken using the refreshToken that given by Authorization Server. But I cannot call this procedure because if I access the url mapping, previously the spring security was blocking the access and give the invalid token error return.

    How can I solve the missing link?

    Updated:

    This is my Authorization Server Configuration:

    @Configuration
    public class Oauth2AuthorizationServer {
    
     @Configuration
     @EnableAuthorizationServer
     protected static class AuthorizationServerConfiguration extends
            AuthorizationServerConfigurerAdapter {
    
        @Autowired
        @Qualifier("authenticationManagerBean")
        private AuthenticationManager authenticationManager;
    
        @Autowired
        DataSource dataSource;
    
    
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints)
                throws Exception {
            endpoints
                    .tokenStore(new JdbcTokenStore(dataSource))
                    .authenticationManager(authenticationManager);
        }
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
            oauthServer.tokenKeyAccess("isAnonymous() || permitAll()").checkTokenAccess("permitAll()");
        }
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients
                    .jdbc(dataSource);
        }
     }
    }
    

    And this is my Resource Server Configuration (as Client too)

    @Configuration
    public class Oauth2ResourceServer {
    
    private static final String RESOURCE_ID = "test";
    
     @Configuration @Order(10)
     protected static class NonOauthResources extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/api/halo").permitAll()
                    .antMatchers("/api/state/**").permitAll()
                    .antMatchers("/**").permitAll()
                    .and().anonymous();
        }
     }
    
     @Configuration
     @EnableResourceServer
     protected static class ResourceServerConfiguration extends
            ResourceServerConfigurerAdapter {
    
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            RemoteTokenServices tokenService = new RemoteTokenServices();
            tokenService.setClientId("jsclient");
            tokenService.setClientSecret("jspasswd");
            tokenService.setCheckTokenEndpointUrl("http://localhost:8084/Server2Auth/oauth/check_token");
    
            resources
                    .resourceId(RESOURCE_ID)
                    .tokenServices(tokenService);
        }
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .filterSecurityInterceptorOncePerRequest(true)
                    .antMatchers("/res/staff").hasRole("STAFF")
                    .antMatchers("/res/client").access("#oauth2.hasScope('trust')")
                    .antMatchers("/res/admin").hasRole("ADMIN")
                    .and()
                    .exceptionHandling().accessDeniedPage("/403");
        }
    
     }
    
    }
    

    Resource (as Client too) request to authorize:

    curl -X POST -vu clientauthcode:123456 http://localhost:10000/auth-server/oauth/token -d "client_id=clientauthcode&grant_type=refresh_token&refresh_token=436761f1-2f26-412b-ab0f-bbf2cd7459c4"
    

    Feedback from Authorize Server:

    http://localhost:10001/resource-server/api/state/new?code=8OppiR
    

    Resource (as Client) exchange the code to Authorize Server:

    curl -X POST -vu clientauthcode:123456 http://localhost:10000/auth-server/oauth/token -H "Accept: application/json" -d "grant_type=authorization_code&code=iMAtdP&redirect_uri=http://localhost:10001/resource-server/api/state/new"
    

    Feedback from Authorize Server:

    {
        "access_token":"08664d93-41e3-473c-b5d2-f2b30afe7053",
        "token_type":"bearer",
        "refresh_token":"436761f1-2f26-412b-ab0f-bbf2cd7459c4",
        "expires_in":43199,
        "scope":"write read"
    }
    

    Resource (as Client) access the url itself

    curl http://localhost:10001/resource-server/api/admin?access_token=08664d93-41e3-473c-b5d2-f2b30afe7053
    

    Request new access toke using refresh token

    curl -X POST -vu clientauthcode:123456 http://localhost:10000/auth-server/oauth/token -d "client_id=clientauthcode&grant_type=refresh_token&refresh_token=436761f1-2f26-412b-ab0f-bbf2cd7459c4"
    
    • Dave Syer
      Dave Syer over 9 years
      Unrelated comment: you should put the access token in a header (it's not secure as a URI query parameter).
    • Dave Syer
      Dave Syer over 9 years
      Another unrelated comment: you don't need the token key endpoint, so don't touch it; and the check token endpoint should not be exposed with permitAll() in a production server (try isAuthenticated()).
    • Dave Syer
      Dave Syer over 9 years
      Somewhat related comment (since it makes the question misleading) you are not doing any SSO here. It is classic auth/resource server with token-based security in the resource server.
  • Dave Syer
    Dave Syer over 9 years
    No problem. Happy to help. If my answer was helpful, please accept it so other people will know that it might help them.
  • ericagon
    ericagon over 9 years
    Sorry,I am just beginner in Oauth2 too. In the Ouath2 spec says "The authorization server MAY issue a new refresh token", how Spring Security Oauth2 can give the issue? I can make a request to get new accesstoken with refreshtoken, but I only knew that the accesstoken expired when it used to access some resource. FYI my SSO separated Authorization and Resource Server because before this I have several Resource Server, so I think I can make Authorization Server separated with my Resources.
  • Dave Syer
    Dave Syer over 9 years
    A Spring Auth Server issues refresh tokens if you enable them (it might not be the default, I forget). When an access token is issued the client knows approximately when it will expire, so it can use that information or just wait for a 401.
  • ericagon
    ericagon over 9 years
    I just enable the refresh tokens. I want to wait for a 401 and then I can call the procedure to request new access token using refresh token. But I don't know how to make 401 handler?
  • Dave Syer
    Dave Syer over 9 years
    If you use OAuth2RestTemplate it should be automatic. Otherwise I guess look at the source code for that and copy the pattern.
  • ericagon
    ericagon over 9 years
    I haven't found the solution to resolve my issue :| I updated the my configuration code in the question
  • Dave Syer
    Dave Syer over 9 years
    I don't understand this sentence: "But I cannot call this procedure because if I access the url mapping, previously the spring security was blocking the access and give the invalid token error return." Can you show the code you use to call the token endpoint (preferably try it with curl and post the curl commands)?
  • ericagon
    ericagon over 9 years
    I mean the spring security protect the url if access token invalid (expired or not suitable) and then it gives the xml output about the error. So, I cannot request new access token using refresh token automatically when the access token expired. The code just updated.
  • ericagon
    ericagon over 9 years
    Is OAuth2RestTemplate used in Client? What if my Client also as Resource Server (so it accesses itself and return the jsp page)? Can OAuth2RestTemplate used?
  • ericagon
    ericagon over 9 years
    I want to try use Oauth2RestTemplate. How to use it? resource.setAccessTokenUri("http://localhost:8084/Server2Aut‌​h/oauth/token"); resource.setClientId("admin"); resource.setClientSecret("admin"); resource.setUserAuthorizationUri("http://localhost:8084/auth‌​/oauth/authorize"); request = new DefaultAccessTokenRequest(); template = new OAuth2RestTemplate(resource, new DefaultOAuth2ClientContext(request)); template.getForObject("http://localhost:8084/Rest/admin", String.class); The error result: UserRedirectRequiredException: A redirect is required to get the users approval
  • Dave Syer
    Dave Syer over 9 years
    That should work for getting an access token. If you want to automatically refresh it when it expires you have to store it somewhere. And if you create the Oauth2RestTemplate every time you use it, you lose the context. Spring OAuth comes with convenience configuration (e.g. @EnableOAuth2Client) for setting up a template where the context is stored in session scope.
  • Ankit Pandoh
    Ankit Pandoh over 4 years
    @DaveSyer while using @EnableOAuth2Client, I saw once user is authenticated, a session is maintained and even though access token is expired, it won't trigger the refresh token flow to get the new access token. There is an issue reported here github.com/spring-projects/spring-security-oauth2-boot/issue‌​s/5 but it seems it is the expected functionality. But then, how can we trigger the refresh token flow while using @EnableOAuth2Client?
  • Dave Syer
    Dave Syer over 4 years
    I think you are reading that issue wrong. If there is a refresh token then the OAuth2RestTemplate will use it when the access token expires, in general, but only if you use it. If you only use it for authentication, it doesn't get used after the initial exchange.