Announcement Announcement Module
Collapse
No announcement yet.
Reuse of tokens by OAuth2 client Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Reuse of tokens by OAuth2 client

    I have a web application which is acting as OAuh2 client to access google apis. Is there a way I can store the tokens and reuse them ? I see that there is org.springframework.security.oauth2.client.token.C lientTokenServices interface available. Can a class implementing this service be used to save a and retrieve the tokens? If so how to configure that ?

    At present once user logs out from web app and comes back to web application, permission page from google is shown again. I would like to avoid this and reuse the token till it expires.

    My resource configuration looks like below, I have modified client-id, client-secret and pre-established-redirect-uri
    Code:
    <oauth:resource id="cloudfiles" type="authorization_code"
    		client-id="my client id" client-secret="my secret"
    		access-token-uri="https://accounts.google.com/o/oauth2/token"
    		user-authorization-uri="https://accounts.google.com/o/oauth2/auth"
    		scope="https://www.googleapis.com/auth/userinfo.email"
    		client-authentication-scheme="form" pre-established-redirect-uri="http://my webapp" />
    I am using 1.0.3.RELEASE version of spring-security-oauth2

  • #2
    AccessTokenProvider chain has a setter for ClientTokenServices, and an AccessTokenProviderChain is used by OAuth2RestTemplate, so the idea is that you inject it there I think (e.g. <rest-template access-token-provider="mytokenprovider"/>).

    Comment


    • #3
      That is working ! Thank you !

      Comment


      • #4
        I, too, have an interest in providing an external token store for use by an OAuth2RestTemplate instance.

        I have this code

        Code:
        ResourceOwnerPasswordResourceDetails details = applicationContext.getBean(ResourceOwnerPasswordResourceDetails.class);
        details.setUsername("user");
        details.setPassword("password");
        details.setAccessTokenUri("http://localhost:" + oAuth2ServerPort + "/auth/v2/oauth2/tokens");
        details.setScope(Collections.singletonList("a/b"));
        
        AccessTokenProviderChain provider = new AccessTokenProviderChain(Arrays.asList(new ResourceOwnerPasswordAccessTokenProvider()));
        provider.setClientTokenServices(new ClientTokenServicesImpl());
        
        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(details);
        restTemplate.setAccessTokenProvider(provider);
        
        String body = restTemplate.getForEntity("http://localhost:" + resourceServerPort, String.class).getBody();
        assertThat(body, equalTo("hello"));
        with this simple ClientTokenServices implemenation

        Code:
        public class ClientTokenServicesImpl implements ClientTokenServices {
        
           @VisibleForTesting
           final Map<String, OAuth2AccessToken> tokenStore = new HashMap<String, OAuth2AccessToken>();
        
           private final ClientKeyGenerator clientKeyGenerator = new DefaultClientKeyGenerator();
        
           @Override
           public OAuth2AccessToken getAccessToken(OAuth2ProtectedResourceDetails resource, Authentication authentication) {
              return tokenStore.get(clientKeyGenerator.extractKey(resource, authentication));
           }
        
           @Override
           public void saveAccessToken(OAuth2ProtectedResourceDetails resource, Authentication authentication, OAuth2AccessToken                      accessToken) {
              tokenStore.put(clientKeyGenerator.extractKey(resource, authentication), accessToken);
           }
         
           @Override
           public void removeAccessToken(OAuth2ProtectedResourceDetails resource, Authentication authentication) {
              tokenStore.remove(clientKeyGenerator.extractKey(resource, authentication));
           }
        }
        I think my expectation of what the token service does is mistaken. I thought the REST template would consult the ClientTokenService instance for the access token via

        com.xoom.inf.httpclient.ClientTokenServicesImpl#ge tAccessToken

        before the template sought to the OAuth2 endpoint for an access token. That said, I do see the token saved to the ClientTokenService via

        com.xoom.inf.httpclient.ClientTokenServicesImpl#sa veAccessToken

        which is all well and good.

        So my question is when does the template consult the ClientTokenService for the token?

        Thank you.
        Last edited by ae6rt; Apr 30th, 2014, 09:27 PM. Reason: improve code formatting

        Comment


        • #5
          I should have read the underlying Spring code before I asked this follow-up question, but I'll post my observations as compensation.

          In org.springframework.security.oauth2.client.token.A ccessTokenProviderChain#obtainAccessToken we see

          Code:
                  if (resource.isClientOnly() || (auth != null && auth.isAuthenticated())) {
                      existingToken = request.getExistingToken();
                      if (existingToken == null && clientTokenServices != null) {
                          existingToken = clientTokenServices.getAccessToken(resource, auth);
                      }
          
                      if (existingToken != null) {
                          if (existingToken.isExpired()) {
                              if (clientTokenServices != null) {
                                  clientTokenServices.removeAccessToken(resource, auth);
                              }
                              OAuth2RefreshToken refreshToken = existingToken.getRefreshToken();
                              if (refreshToken != null) {
                                  accessToken = refreshAccessToken(resource, refreshToken, request);
                              }
                          }
                          else {
                              accessToken = existingToken;
                          }
                      }
                  }
          Because my use case requires the Resource Owner flow, resource.isClientOnly() is naturally false. But if prior to calling the high level HTTP methods on the template, I place a capable Authentication instance in the SecurityContext, I can drive the code through the clientTokenServices.getAccessToken() code, which is what I need.

          Hopefully this helps someone down the line.

          Comment

          Working...
          X