mirror of https://github.com/apache/jclouds.git
JCLOUDS-1148: Fix token caches in OAuth flows
This commit is contained in:
parent
9662edf903
commit
913fdeb168
|
@ -38,12 +38,9 @@ public abstract class ClientSecret {
|
|||
/** The scope(s) to authorize against. **/
|
||||
@Nullable public abstract String scope();
|
||||
|
||||
/** When does the token expire. **/
|
||||
public abstract long expire();
|
||||
|
||||
@SerializedNames({ "client_id", "client_secret", "resource", "scope", "expire" })
|
||||
public static ClientSecret create(String clientId, String clientSecret, String resource, String scope, long expire) {
|
||||
return new AutoValue_ClientSecret(clientId, clientSecret, resource, scope, expire);
|
||||
@SerializedNames({ "client_id", "client_secret", "resource", "scope" })
|
||||
public static ClientSecret create(String clientId, String clientSecret, String resource, String scope) {
|
||||
return new AutoValue_ClientSecret(clientId, clientSecret, resource, scope);
|
||||
}
|
||||
|
||||
ClientSecret() {
|
||||
|
|
|
@ -58,7 +58,6 @@ public class ClientCredentialsJWTBearerTokenFlow implements OAuthFilter {
|
|||
private final String audience;
|
||||
private final Supplier<Credentials> credentialsSupplier;
|
||||
private final OAuthScopes scopes;
|
||||
private final long tokenDuration;
|
||||
private final LoadingCache<ClientCredentialsAuthArgs, Token> tokenCache;
|
||||
|
||||
@Inject
|
||||
|
@ -71,7 +70,6 @@ public class ClientCredentialsJWTBearerTokenFlow implements OAuthFilter {
|
|||
this.scopes = scopes;
|
||||
this.audience = audience;
|
||||
this.resource = resource;
|
||||
this.tokenDuration = tokenDuration;
|
||||
// since the session interval is also the token expiration time requested to the server make the token expire a
|
||||
// bit before the deadline to make sure there aren't session expiration exceptions
|
||||
long cacheExpirationSeconds = tokenDuration > 30 ? tokenDuration - 30 : tokenDuration;
|
||||
|
@ -80,26 +78,40 @@ public class ClientCredentialsJWTBearerTokenFlow implements OAuthFilter {
|
|||
|
||||
static final class AuthorizeToken extends CacheLoader<ClientCredentialsAuthArgs, Token> {
|
||||
private final AuthorizationApi api;
|
||||
private final long tokenDuration;
|
||||
|
||||
@Inject AuthorizeToken(AuthorizationApi api) {
|
||||
@Inject AuthorizeToken(AuthorizationApi api, @Named(PROPERTY_SESSION_INTERVAL) long tokenDuration) {
|
||||
this.api = api;
|
||||
this.tokenDuration = tokenDuration;
|
||||
}
|
||||
|
||||
long currentTimeSeconds() {
|
||||
return System.currentTimeMillis() / 1000;
|
||||
}
|
||||
|
||||
@Override public Token load(ClientCredentialsAuthArgs key) throws Exception {
|
||||
return api.authorize(key.clientId(), key.claims(), key.resource(), key.scope());
|
||||
final long now = currentTimeSeconds();
|
||||
final ClientCredentialsClaims claims = ClientCredentialsClaims.create(
|
||||
key.claims().iss(),
|
||||
key.claims().sub(),
|
||||
key.claims().aud(),
|
||||
now + tokenDuration,
|
||||
now,
|
||||
UUID.randomUUID().toString()
|
||||
);
|
||||
return api.authorize(key.clientId(), claims, key.resource(), key.scope());
|
||||
}
|
||||
}
|
||||
|
||||
@Override public HttpRequest filter(HttpRequest request) throws HttpException {
|
||||
long now = currentTimeSeconds();
|
||||
List<String> configuredScopes = scopes.forRequest(request);
|
||||
ClientCredentialsClaims claims = ClientCredentialsClaims.create( //
|
||||
credentialsSupplier.get().identity, // iss
|
||||
credentialsSupplier.get().identity, // sub
|
||||
audience, // aud
|
||||
now + tokenDuration, // exp
|
||||
now, // nbf
|
||||
UUID.randomUUID().toString() // jti
|
||||
-1, // placeholder exp for the cache
|
||||
-1, // placeholder nbf for the cache
|
||||
null // placeholder jti for the cache
|
||||
);
|
||||
ClientCredentialsAuthArgs authArgs = ClientCredentialsAuthArgs.create(
|
||||
credentialsSupplier.get().identity,
|
||||
|
@ -112,9 +124,5 @@ public class ClientCredentialsJWTBearerTokenFlow implements OAuthFilter {
|
|||
String authorization = String.format("%s %s", token.tokenType(), token.accessToken());
|
||||
return request.toBuilder().addHeader("Authorization", authorization).build();
|
||||
}
|
||||
|
||||
long currentTimeSeconds() {
|
||||
return System.currentTimeMillis() / 1000;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,6 @@ public class ClientCredentialsSecretFlow implements OAuthFilter {
|
|||
private static final Joiner ON_SPACE = Joiner.on(" ");
|
||||
|
||||
private final Supplier<Credentials> credentialsSupplier;
|
||||
private final long tokenDuration;
|
||||
private final LoadingCache<ClientSecret, Token> tokenCache;
|
||||
private final String resource;
|
||||
private final OAuthScopes scopes;
|
||||
|
@ -67,7 +66,6 @@ public class ClientCredentialsSecretFlow implements OAuthFilter {
|
|||
this.credentialsSupplier = credentialsSupplier;
|
||||
this.scopes = scopes;
|
||||
this.resource = resource;
|
||||
this.tokenDuration = tokenDuration;
|
||||
// since the session interval is also the token expiration time requested to the server make the token expire a
|
||||
// bit before the deadline to make sure there aren't session expiration exceptions
|
||||
long cacheExpirationSeconds = tokenDuration > 30 ? tokenDuration - 30 : tokenDuration;
|
||||
|
@ -87,21 +85,15 @@ public class ClientCredentialsSecretFlow implements OAuthFilter {
|
|||
}
|
||||
|
||||
@Override public HttpRequest filter(HttpRequest request) throws HttpException {
|
||||
long now = currentTimeSeconds();
|
||||
List<String> configuredScopes = scopes.forRequest(request);
|
||||
ClientSecret client = ClientSecret.create(
|
||||
credentialsSupplier.get().identity,
|
||||
credentialsSupplier.get().credential,
|
||||
resource == null ? "" : resource,
|
||||
configuredScopes.isEmpty() ? null : ON_SPACE.join(configuredScopes),
|
||||
now + tokenDuration
|
||||
configuredScopes.isEmpty() ? null : ON_SPACE.join(configuredScopes)
|
||||
);
|
||||
Token token = tokenCache.getUnchecked(client);
|
||||
String authorization = String.format("%s %s", token.tokenType(), token.accessToken());
|
||||
return request.toBuilder().addHeader("Authorization", authorization).build();
|
||||
}
|
||||
|
||||
long currentTimeSeconds() {
|
||||
return System.currentTimeMillis() / 1000;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.jclouds.oauth.v2.config.OAuthScopes;
|
|||
import org.jclouds.oauth.v2.domain.Claims;
|
||||
import org.jclouds.oauth.v2.domain.Token;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
|
@ -53,30 +54,36 @@ public class JWTBearerTokenFlow implements OAuthFilter {
|
|||
private final String audience;
|
||||
private final Supplier<Credentials> credentialsSupplier;
|
||||
private final OAuthScopes scopes;
|
||||
private final long tokenDuration;
|
||||
private final LoadingCache<Claims, Token> tokenCache;
|
||||
private final LoadingCache<TokenCacheKey, Token> tokenCache;
|
||||
|
||||
@Inject JWTBearerTokenFlow(AuthorizeToken loader, @Named(PROPERTY_SESSION_INTERVAL) long tokenDuration,
|
||||
@Provider Supplier<Credentials> credentialsSupplier, OAuthScopes scopes, @Named(AUDIENCE) String audience) {
|
||||
this.credentialsSupplier = credentialsSupplier;
|
||||
this.scopes = scopes;
|
||||
this.audience = audience;
|
||||
this.tokenDuration = tokenDuration;
|
||||
// since the session interval is also the token expiration time requested to the server make the token expire a
|
||||
// bit before the deadline to make sure there aren't session expiration exceptions
|
||||
long cacheExpirationSeconds = tokenDuration > 30 ? tokenDuration - 30 : tokenDuration;
|
||||
this.tokenCache = CacheBuilder.newBuilder().expireAfterWrite(cacheExpirationSeconds, SECONDS).build(loader);
|
||||
}
|
||||
|
||||
static final class AuthorizeToken extends CacheLoader<Claims, Token> {
|
||||
static final class AuthorizeToken extends CacheLoader<TokenCacheKey, Token> {
|
||||
private final AuthorizationApi api;
|
||||
private final long tokenDuration;
|
||||
|
||||
@Inject AuthorizeToken(AuthorizationApi api) {
|
||||
@Inject AuthorizeToken(AuthorizationApi api, @Named(PROPERTY_SESSION_INTERVAL) long tokenDuration) {
|
||||
this.api = api;
|
||||
this.tokenDuration = tokenDuration;
|
||||
}
|
||||
|
||||
@Override public Token load(Claims key) throws Exception {
|
||||
return api.authorize(key);
|
||||
@Override public Token load(TokenCacheKey tokenCacheKey) throws Exception {
|
||||
final Claims claims = Claims.create(
|
||||
tokenCacheKey.claims().iss(),
|
||||
tokenCacheKey.claims().scope(),
|
||||
tokenCacheKey.claims().aud(),
|
||||
tokenCacheKey.startTime + tokenDuration,
|
||||
tokenCacheKey.startTime);
|
||||
return api.authorize(claims);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,10 +93,11 @@ public class JWTBearerTokenFlow implements OAuthFilter {
|
|||
credentialsSupplier.get().identity, // iss
|
||||
ON_COMMA.join(scopes.forRequest(request)), // scope
|
||||
audience, // aud
|
||||
now + tokenDuration, // exp
|
||||
now // iat
|
||||
-1, // placeholder exp for the cache
|
||||
-1 // placeholder iat for the cache
|
||||
);
|
||||
Token token = tokenCache.getUnchecked(claims);
|
||||
final TokenCacheKey tokenCacheKey = TokenCacheKey.create(claims, now);
|
||||
Token token = tokenCache.getUnchecked(tokenCacheKey);
|
||||
String authorization = String.format("%s %s", token.tokenType(), token.accessToken());
|
||||
return request.toBuilder().addHeader("Authorization", authorization).build();
|
||||
}
|
||||
|
@ -97,4 +105,20 @@ public class JWTBearerTokenFlow implements OAuthFilter {
|
|||
long currentTimeSeconds() {
|
||||
return System.currentTimeMillis() / 1000;
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
abstract static class TokenCacheKey {
|
||||
public abstract Claims claims();
|
||||
|
||||
long startTime;
|
||||
|
||||
public static TokenCacheKey create(Claims claims, long startTime) {
|
||||
final AutoValue_JWTBearerTokenFlow_TokenCacheKey tokenCacheKey = new AutoValue_JWTBearerTokenFlow_TokenCacheKey(claims);
|
||||
tokenCacheKey.startTime = startTime;
|
||||
return tokenCacheKey;
|
||||
}
|
||||
|
||||
TokenCacheKey() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue