Skip to content
This repository has been archived by the owner on May 31, 2022. It is now read-only.

Commit

Permalink
SECOAUTH-163: add access token required for expired token
Browse files Browse the repository at this point in the history
  • Loading branch information
dsyer committed Nov 17, 2011
1 parent 7b95046 commit 0874566
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 111 deletions.
Expand Up @@ -26,6 +26,7 @@
public class OAuth2ClientHttpRequestFactory implements ClientHttpRequestFactory {

private final ClientHttpRequestFactory delegate;

private final OAuth2ProtectedResourceDetails resource;

public OAuth2ClientHttpRequestFactory(ClientHttpRequestFactory delegate, OAuth2ProtectedResourceDetails resource) {
Expand All @@ -50,12 +51,19 @@ public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IO

Map<String, OAuth2AccessToken> accessTokens = context.getAccessTokens();
OAuth2AccessToken accessToken = accessTokens == null ? null : accessTokens.get(this.resource.getId());

if (accessToken == null) {
throw new AccessTokenRequiredException(
"No OAuth 2 security context has been established. Unable to access resource '"
+ this.resource.getId() + "'.", resource);
}

if (accessToken.isExpired()) {
// If the current token has expired we can use this exception as a trigger to try and refresh it
throw new AccessTokenRequiredException("OAuth 2 token is expired. Unable to access resource '"
+ this.resource.getId() + "'.", resource);
}

String tokenType = accessToken.getTokenType();
if (!StringUtils.hasText(tokenType)) {
tokenType = OAuth2AccessToken.BEARER_TYPE; // we'll assume basic bearer token type if none is specified.
Expand All @@ -73,7 +81,8 @@ public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IO
String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, accessToken.getValue()));
}
return req;
} else {
}
else {
throw new OAuth2AccessDeniedException("Unsupported access token type: " + tokenType);
}
}
Expand All @@ -85,11 +94,11 @@ protected URI appendQueryParameter(URI uri, OAuth2AccessToken accessToken) {
// TODO: there is some duplication with UriUtils here. Probably unavoidable as long as this
// method signature uses URI not String.
String query = uri.getQuery();
String queryFragment = resource.getTokenName() + "="
+ URLEncoder.encode(accessToken.getValue(), "UTF-8");
String queryFragment = resource.getTokenName() + "=" + URLEncoder.encode(accessToken.getValue(), "UTF-8");
if (query == null) {
query = queryFragment;
} else {
}
else {
query = query + "&" + queryFragment;
}

Expand All @@ -108,9 +117,11 @@ protected URI appendQueryParameter(URI uri, OAuth2AccessToken accessToken) {

return new URI(sb.toString());

} catch (URISyntaxException e) {
}
catch (URISyntaxException e) {
throw new IllegalArgumentException("Could not parse URI", e);
} catch (UnsupportedEncodingException e) {
}
catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("Could not encode URI", e);
}

Expand Down
Expand Up @@ -75,7 +75,7 @@ public OAuth2AccessToken obtainNewAccessToken(OAuth2ProtectedResourceDetails res
if (!requireAuthenticated || (auth != null && auth.isAuthenticated())) {
existingToken = tokenServices.getToken(auth, resource);
if (existingToken != null) {
if (isExpired(existingToken)) {
if (existingToken.isExpired()) {
OAuth2RefreshToken refreshToken = existingToken.getRefreshToken();
if (refreshToken != null) {
accessToken = refreshAccessToken(resource, refreshToken);
Expand Down Expand Up @@ -144,16 +144,6 @@ protected OAuth2AccessToken refreshAccessToken(OAuth2ProtectedResourceDetails re
return retrieveToken(form, resource);
}

/**
* Whether the specified access token is expired.
*
* @param token The token.
* @return Whether the specified access token is expired.
*/
protected boolean isExpired(OAuth2AccessToken token) {
return token.getExpiration() == null || token.getExpiration().getTime() < System.currentTimeMillis();
}

public void setTokenServices(OAuth2ClientTokenServices tokenServices) {
this.tokenServices = tokenServices;
}
Expand Down
Expand Up @@ -60,6 +60,15 @@ public void setExpiration(Date expiration) {
this.expiration = expiration;
}

/**
* Convenience method for checking expiration
*
* @return true if the expiration is befor ethe current time
*/
public boolean isExpired() {
return expiration!=null && expiration.before(new Date());
}

/**
* The token type, as introduced in draft 11 of the OAuth 2 spec. The spec doesn't define (yet) that the valid token
* types are, but says it's required so the default will just be "undefined".
Expand Down Expand Up @@ -129,4 +138,5 @@ public int hashCode() {
public String toString() {
return getValue();
}

}
Expand Up @@ -16,9 +16,7 @@

package org.springframework.security.oauth2.provider.token;

import java.security.SecureRandom;
import java.util.Date;
import java.util.Random;
import java.util.Set;
import java.util.UUID;

Expand All @@ -44,8 +42,6 @@
public class RandomValueTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices,
InitializingBean {

private Random random;

private int refreshTokenValiditySeconds = 60 * 60 * 24 * 30; // default 30 days.

private int accessTokenValiditySeconds = 60 * 60 * 12; // default 12 hours.
Expand All @@ -54,23 +50,18 @@ public class RandomValueTokenServices implements AuthorizationServerTokenService

private boolean reuseRefreshToken = true;

private int tokenSecretLengthBytes = 80;

private TokenStore tokenStore;

/**
* Initialize these token services. If no random generator is set, one will be created.
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(tokenStore, "tokenStore must be set");
if (random == null) {
random = new SecureRandom();
}
}

public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
ExpiringOAuth2RefreshToken refreshToken = null;
if (isSupportRefreshToken()) {
if (supportRefreshToken) {
refreshToken = createRefreshToken(authentication);
}

Expand All @@ -80,7 +71,7 @@ public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication)
public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, Set<String> scope)
throws AuthenticationException {

if (!isSupportRefreshToken()) {
if (!supportRefreshToken) {
throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
}

Expand All @@ -99,7 +90,7 @@ else if (isExpired(refreshToken)) {
OAuth2Authentication authentication = createRefreshedAuthentication(
tokenStore.readAuthentication(refreshToken), scope);

if (!isReuseRefreshToken()) {
if (!reuseRefreshToken) {
tokenStore.removeRefreshToken(refreshTokenValue);
refreshToken = createRefreshToken(authentication);
}
Expand Down Expand Up @@ -138,17 +129,12 @@ protected boolean isExpired(ExpiringOAuth2RefreshToken refreshToken) {
|| System.currentTimeMillis() > refreshToken.getExpiration().getTime();
}

private boolean isExpired(OAuth2AccessToken accessToken) {
return accessToken.getExpiration() == null
|| System.currentTimeMillis() > accessToken.getExpiration().getTime();
}

public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException {
OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);
if (accessToken == null) {
throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
}
else if (isExpired(accessToken)) {
else if (accessToken.isExpired()) {
tokenStore.removeAccessToken(accessTokenValue);
throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
}
Expand All @@ -160,66 +146,21 @@ protected ExpiringOAuth2RefreshToken createRefreshToken(OAuth2Authentication aut
ExpiringOAuth2RefreshToken refreshToken;
String refreshTokenValue = UUID.randomUUID().toString();
refreshToken = new ExpiringOAuth2RefreshToken(refreshTokenValue, new Date(System.currentTimeMillis()
+ (getRefreshTokenValiditySeconds() * 1000L)));
+ (refreshTokenValiditySeconds * 1000L)));
tokenStore.storeRefreshToken(refreshToken, authentication);
return refreshToken;
}

protected OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
String tokenValue = UUID.randomUUID().toString();
OAuth2AccessToken token = new OAuth2AccessToken(tokenValue);
token.setExpiration(new Date(System.currentTimeMillis() + (getAccessTokenValiditySeconds() * 1000L)));
token.setExpiration(new Date(System.currentTimeMillis() + (accessTokenValiditySeconds * 1000L)));
token.setRefreshToken(refreshToken);
token.setScope(authentication.getClientAuthentication().getScope());
tokenStore.storeAccessToken(token, authentication);
return token;
}

/**
* The length of the token secret in bytes, before being base64-encoded.
*
* @return The length of the token secret in bytes.
*/
public int getTokenSecretLengthBytes() {
return tokenSecretLengthBytes;
}

/**
* The length of the token secret in bytes, before being base64-encoded.
*
* @param tokenSecretLengthBytes The length of the token secret in bytes, before being base64-encoded.
*/
public void setTokenSecretLengthBytes(int tokenSecretLengthBytes) {
this.tokenSecretLengthBytes = tokenSecretLengthBytes;
}

/**
* The random value generator used to create token secrets.
*
* @return The random value generator used to create token secrets.
*/
public Random getRandom() {
return random;
}

/**
* The random value generator used to create token secrets.
*
* @param random The random value generator used to create token secrets.
*/
public void setRandom(Random random) {
this.random = random;
}

/**
* The validity (in seconds) of the unauthenticated request token.
*
* @return The validity (in seconds) of the unauthenticated request token.
*/
public int getRefreshTokenValiditySeconds() {
return refreshTokenValiditySeconds;
}

/**
* The validity (in seconds) of the unauthenticated request token.
*
Expand All @@ -229,15 +170,6 @@ public void setRefreshTokenValiditySeconds(int refreshTokenValiditySeconds) {
this.refreshTokenValiditySeconds = refreshTokenValiditySeconds;
}

/**
* The validity (in seconds) of the access token.
*
* @return The validity (in seconds) of the access token.
*/
public int getAccessTokenValiditySeconds() {
return accessTokenValiditySeconds;
}

/**
* The validity (in seconds) of the access token.
*
Expand All @@ -247,15 +179,6 @@ public void setAccessTokenValiditySeconds(int accessTokenValiditySeconds) {
this.accessTokenValiditySeconds = accessTokenValiditySeconds;
}

/**
* Whether to support the refresh token.
*
* @return Whether to support the refresh token.
*/
public boolean isSupportRefreshToken() {
return supportRefreshToken;
}

/**
* Whether to support the refresh token.
*
Expand All @@ -265,15 +188,6 @@ public void setSupportRefreshToken(boolean supportRefreshToken) {
this.supportRefreshToken = supportRefreshToken;
}

/**
* Whether to reuse refresh tokens (until expired).
*
* @return Whether to reuse refresh tokens (until expired).
*/
public boolean isReuseRefreshToken() {
return reuseRefreshToken;
}

/**
* Whether to reuse refresh tokens (until expired).
*
Expand Down
Expand Up @@ -7,7 +7,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Random;

import org.junit.Test;
import org.springframework.security.core.Authentication;
Expand Down Expand Up @@ -79,7 +78,6 @@ public void testReadingRefreshTokenForTokenThatDoesNotExist() {
public void testRefreshedTokenHasScopes() throws Exception {
RandomValueTokenServices services = new RandomValueTokenServices();
services.setTokenStore(getTokenStore());
services.setRandom(new Random(1L));
services.afterPropertiesSet();
services.setSupportRefreshToken(true);
ExpiringOAuth2RefreshToken expectedExpiringRefreshToken = new ExpiringOAuth2RefreshToken("testToken", new Date(
Expand Down

0 comments on commit 0874566

Please sign in to comment.