|
@@ -17,7 +17,6 @@
|
|
|
package org.springframework.security.oauth2.server.resource.authentication;
|
|
|
|
|
|
import java.time.Instant;
|
|
|
-import java.util.Collection;
|
|
|
|
|
|
import org.apache.commons.logging.Log;
|
|
|
import org.apache.commons.logging.LogFactory;
|
|
@@ -25,6 +24,7 @@ import org.apache.commons.logging.LogFactory;
|
|
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
|
|
import org.springframework.security.authentication.AuthenticationProvider;
|
|
|
import org.springframework.security.authentication.AuthenticationServiceException;
|
|
|
+import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
|
|
import org.springframework.security.core.Authentication;
|
|
|
import org.springframework.security.core.AuthenticationException;
|
|
|
import org.springframework.security.core.GrantedAuthority;
|
|
@@ -35,6 +35,7 @@ import org.springframework.security.oauth2.server.resource.BearerTokenAuthentica
|
|
|
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
|
|
|
import org.springframework.security.oauth2.server.resource.introspection.BadOpaqueTokenException;
|
|
|
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
|
|
|
+import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter;
|
|
|
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
|
|
|
import org.springframework.util.Assert;
|
|
|
|
|
@@ -49,16 +50,21 @@ import org.springframework.util.Assert;
|
|
|
* opaque access token, returning its attributes set as part of the {@link Authentication}
|
|
|
* statement.
|
|
|
* <p>
|
|
|
- * Scopes are translated into {@link GrantedAuthority}s according to the following
|
|
|
- * algorithm:
|
|
|
- * <ol>
|
|
|
- * <li>If there is a "scope" attribute, then convert to a {@link Collection} of
|
|
|
- * {@link String}s.
|
|
|
- * <li>Take the resulting {@link Collection} and prepend the "SCOPE_" keyword to each
|
|
|
- * element, adding as {@link GrantedAuthority}s.
|
|
|
- * </ol>
|
|
|
+ * This {@link ReactiveAuthenticationManager} is responsible for introspecting and
|
|
|
+ * verifying an opaque access token, returning its attributes set as part of the
|
|
|
+ * {@link Authentication} statement.
|
|
|
+ * <p>
|
|
|
+ * <p>
|
|
|
+ * {@link org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector}
|
|
|
+ * is responsible for retrieving token attributes from authorization-server.
|
|
|
+ * </p>
|
|
|
+ * <p>
|
|
|
+ * authenticationConverter is responsible for turning successful introspection into
|
|
|
+ * {@link Authentication} (which includes {@link GrantedAuthority}s mapping from token
|
|
|
+ * attributes or retrieving from an other source)
|
|
|
*
|
|
|
* @author Josh Cummings
|
|
|
+ * @author Jerome Wacongne <ch4mp@c4-soft.com>
|
|
|
* @since 5.2
|
|
|
* @see AuthenticationProvider
|
|
|
*/
|
|
@@ -68,6 +74,8 @@ public final class OpaqueTokenAuthenticationProvider implements AuthenticationPr
|
|
|
|
|
|
private final OpaqueTokenIntrospector introspector;
|
|
|
|
|
|
+ private OpaqueTokenAuthenticationConverter authenticationConverter;
|
|
|
+
|
|
|
/**
|
|
|
* Creates a {@code OpaqueTokenAuthenticationProvider} with the provided parameters
|
|
|
* @param introspector The {@link OpaqueTokenIntrospector} to use
|
|
@@ -75,12 +83,20 @@ public final class OpaqueTokenAuthenticationProvider implements AuthenticationPr
|
|
|
public OpaqueTokenAuthenticationProvider(OpaqueTokenIntrospector introspector) {
|
|
|
Assert.notNull(introspector, "introspector cannot be null");
|
|
|
this.introspector = introspector;
|
|
|
+ this.setAuthenticationConverter(OpaqueTokenAuthenticationProvider::convert);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * <p>
|
|
|
* Introspect and validate the opaque
|
|
|
* <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer
|
|
|
- * Token</a>.
|
|
|
+ * Token</a> and then delegates {@link Authentication} instantiation to
|
|
|
+ * {@link OpaqueTokenAuthenticationConverter}.
|
|
|
+ * </p>
|
|
|
+ * <p>
|
|
|
+ * If created Authentication is instance of {@link AbstractAuthenticationToken} and
|
|
|
+ * details are null, then introspection result details are used.
|
|
|
+ * </p>
|
|
|
* @param authentication the authentication request object.
|
|
|
* @return A successful authentication
|
|
|
* @throws AuthenticationException if authentication failed for some reason
|
|
@@ -92,8 +108,16 @@ public final class OpaqueTokenAuthenticationProvider implements AuthenticationPr
|
|
|
}
|
|
|
BearerTokenAuthenticationToken bearer = (BearerTokenAuthenticationToken) authentication;
|
|
|
OAuth2AuthenticatedPrincipal principal = getOAuth2AuthenticatedPrincipal(bearer);
|
|
|
- AbstractAuthenticationToken result = convert(principal, bearer.getToken());
|
|
|
- result.setDetails(bearer.getDetails());
|
|
|
+ Authentication result = this.authenticationConverter.convert(bearer.getToken(), principal);
|
|
|
+ if (result == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (AbstractAuthenticationToken.class.isAssignableFrom(result.getClass())) {
|
|
|
+ final AbstractAuthenticationToken auth = (AbstractAuthenticationToken) result;
|
|
|
+ if (auth.getDetails() == null) {
|
|
|
+ auth.setDetails(bearer.getDetails());
|
|
|
+ }
|
|
|
+ }
|
|
|
this.logger.debug("Authenticated token");
|
|
|
return result;
|
|
|
}
|
|
@@ -116,11 +140,32 @@ public final class OpaqueTokenAuthenticationProvider implements AuthenticationPr
|
|
|
return BearerTokenAuthenticationToken.class.isAssignableFrom(authentication);
|
|
|
}
|
|
|
|
|
|
- private AbstractAuthenticationToken convert(OAuth2AuthenticatedPrincipal principal, String token) {
|
|
|
- Instant iat = principal.getAttribute(OAuth2TokenIntrospectionClaimNames.IAT);
|
|
|
- Instant exp = principal.getAttribute(OAuth2TokenIntrospectionClaimNames.EXP);
|
|
|
- OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, token, iat, exp);
|
|
|
- return new BearerTokenAuthentication(principal, accessToken, principal.getAuthorities());
|
|
|
+ /**
|
|
|
+ * Default {@link OpaqueTokenAuthenticationConverter}.
|
|
|
+ * @param introspectedToken the bearer sring that was successfuly introspected
|
|
|
+ * @param authenticatedPrincipal the successful introspection output
|
|
|
+ * @returna {@link BearerTokenAuthentication}
|
|
|
+ */
|
|
|
+ static BearerTokenAuthentication convert(String introspectedToken,
|
|
|
+ OAuth2AuthenticatedPrincipal authenticatedPrincipal) {
|
|
|
+ Instant iat = authenticatedPrincipal.getAttribute(OAuth2TokenIntrospectionClaimNames.IAT);
|
|
|
+ Instant exp = authenticatedPrincipal.getAttribute(OAuth2TokenIntrospectionClaimNames.EXP);
|
|
|
+ OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, introspectedToken,
|
|
|
+ iat, exp);
|
|
|
+ return new BearerTokenAuthentication(authenticatedPrincipal, accessToken,
|
|
|
+ authenticatedPrincipal.getAuthorities());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Provide with a custom bean to turn successful introspection result into an
|
|
|
+ * {@link Authentication} instance of your choice. By default,
|
|
|
+ * {@link BearerTokenAuthentication} will be built.
|
|
|
+ * @param authenticationConverter the converter to use
|
|
|
+ * @since 5.8
|
|
|
+ */
|
|
|
+ public void setAuthenticationConverter(OpaqueTokenAuthenticationConverter authenticationConverter) {
|
|
|
+ Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
|
|
|
+ this.authenticationConverter = authenticationConverter;
|
|
|
}
|
|
|
|
|
|
}
|