From 680984c24202656100bed83960ae5b103257e532 Mon Sep 17 00:00:00 2001 From: Joe Grandja Date: Fri, 22 Sep 2017 09:51:00 -0400 Subject: [PATCH] SecurityTokenRepository associates SecurityToken to ClientRegistration Fixes gh-4563 --- ...thorizationCodeAuthenticationProvider.java | 2 +- .../token/InMemoryAccessTokenRepository.java | 65 ++++++++++++------- .../client/token/SecurityTokenRepository.java | 13 ++-- 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizationCodeAuthenticationProvider.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizationCodeAuthenticationProvider.java index b4b53811fd..3643f80f05 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizationCodeAuthenticationProvider.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizationCodeAuthenticationProvider.java @@ -155,7 +155,7 @@ public class AuthorizationCodeAuthenticationProvider implements AuthenticationPr } oauth2UserAuthentication.setDetails(oauth2ClientAuthentication.getDetails()); - this.accessTokenRepository.saveSecurityToken(accessToken, oauth2UserAuthentication); + this.accessTokenRepository.saveSecurityToken(accessToken, clientRegistration); return oauth2UserAuthentication; } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/token/InMemoryAccessTokenRepository.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/token/InMemoryAccessTokenRepository.java index 98a8f08fb1..e548c559d0 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/token/InMemoryAccessTokenRepository.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/token/InMemoryAccessTokenRepository.java @@ -15,58 +15,73 @@ */ package org.springframework.security.oauth2.client.token; -import org.springframework.security.oauth2.client.authentication.OAuth2UserAuthenticationToken; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.ClientRegistrationIdentifierStrategy; import org.springframework.security.oauth2.core.AccessToken; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.security.oauth2.oidc.core.user.OidcUser; import org.springframework.util.Assert; +import java.util.Base64; import java.util.HashMap; import java.util.Map; /** - * A basic implementation of a {@link SecurityTokenRepository} - * that stores {@link AccessToken}(s) in-memory. + * A {@link SecurityTokenRepository} that associates an {@link AccessToken} + * to a {@link ClientRegistration Client} and stores it in-memory. * * @author Joe Grandja * @since 5.0 * @see SecurityTokenRepository * @see AccessToken + * @see ClientRegistration */ public final class InMemoryAccessTokenRepository implements SecurityTokenRepository { + private final ClientRegistrationIdentifierStrategy identifierStrategy = new AuthorizedClientIdentifierStrategy(); private final Map accessTokens = new HashMap<>(); @Override - public AccessToken loadSecurityToken(OAuth2UserAuthenticationToken authentication) { - Assert.notNull(authentication, "authentication cannot be null"); - return this.accessTokens.get(this.resolveAuthenticationKey(authentication)); + public AccessToken loadSecurityToken(ClientRegistration registration) { + Assert.notNull(registration, "registration cannot be null"); + return this.accessTokens.get(this.identifierStrategy.getIdentifier(registration)); } @Override - public void saveSecurityToken(AccessToken accessToken, OAuth2UserAuthenticationToken authentication) { + public void saveSecurityToken(AccessToken accessToken, ClientRegistration registration) { Assert.notNull(accessToken, "accessToken cannot be null"); - Assert.notNull(authentication, "authentication cannot be null"); - this.accessTokens.put(this.resolveAuthenticationKey(authentication), accessToken); + Assert.notNull(registration, "registration cannot be null"); + this.accessTokens.put(this.identifierStrategy.getIdentifier(registration), accessToken); } @Override - public void removeSecurityToken(OAuth2UserAuthenticationToken authentication) { - Assert.notNull(authentication, "authentication cannot be null"); - this.accessTokens.remove(this.resolveAuthenticationKey(authentication)); + public void removeSecurityToken(ClientRegistration registration) { + Assert.notNull(registration, "registration cannot be null"); + this.accessTokens.remove(this.identifierStrategy.getIdentifier(registration)); } - private String resolveAuthenticationKey(OAuth2UserAuthenticationToken authentication) { - String authenticationKey; + /** + * A client is considered "authorized", if it receives a successful response from the Token Endpoint. + * + * @see Section 4.1.3 Access Token Request + * @see Section 5.1 Access Token Response + */ + private static class AuthorizedClientIdentifierStrategy implements ClientRegistrationIdentifierStrategy { - OAuth2User oauth2User = (OAuth2User) authentication.getPrincipal(); - if (OidcUser.class.isAssignableFrom(oauth2User.getClass())) { - OidcUser oidcUser = (OidcUser)oauth2User; - authenticationKey = oidcUser.getIssuer().toString() + "-" + oidcUser.getSubject(); - } else { - authenticationKey = authentication.getClientAuthentication().getClientRegistration() - .getProviderDetails().getUserInfoUri() + "-" + oauth2User.getName(); - } + @Override + public String getIdentifier(ClientRegistration clientRegistration) { + StringBuilder builder = new StringBuilder(); + + // Access Token Request attributes + builder.append("[").append(clientRegistration.getAuthorizationGrantType().getValue()).append("]"); + builder.append("[").append(clientRegistration.getRedirectUri()).append("]"); + builder.append("[").append(clientRegistration.getClientId()).append("]"); + + // Access Token Response attributes + builder.append("[").append(clientRegistration.getScope().toString()).append("]"); - return authenticationKey; + // Client alias is unique as well + builder.append("[").append(clientRegistration.getClientAlias()).append("]"); + + return Base64.getEncoder().encodeToString(builder.toString().getBytes()); + } } } + diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/token/SecurityTokenRepository.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/token/SecurityTokenRepository.java index 47758104c1..ded51d04b2 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/token/SecurityTokenRepository.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/token/SecurityTokenRepository.java @@ -15,22 +15,25 @@ */ package org.springframework.security.oauth2.client.token; -import org.springframework.security.oauth2.client.authentication.OAuth2UserAuthenticationToken; +import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.core.SecurityToken; /** * Implementations of this interface are responsible for the persistence - * of {@link SecurityToken}(s) that are associated to an {@link OAuth2UserAuthenticationToken}. + * and association of an OAuth 2.0 / OpenID Connect 1.0 + * {@link SecurityToken} to a {@link ClientRegistration Client}. * * @author Joe Grandja * @since 5.0 + * @see SecurityToken + * @see ClientRegistration */ public interface SecurityTokenRepository { - T loadSecurityToken(OAuth2UserAuthenticationToken authentication); + T loadSecurityToken(ClientRegistration registration); - void saveSecurityToken(T securityToken, OAuth2UserAuthenticationToken authentication); + void saveSecurityToken(T securityToken, ClientRegistration registration); - void removeSecurityToken(OAuth2UserAuthenticationToken authentication); + void removeSecurityToken(ClientRegistration registration); }