From 9541ae24b5e41622072089a69e21cebb6dc921d5 Mon Sep 17 00:00:00 2001 From: Sergey Beryozkin Date: Fri, 20 Aug 2021 17:05:53 +0100 Subject: [PATCH] Get all code flow credentials visible to SecurityIdentityAugmentors --- .../runtime/CodeAuthenticationMechanism.java | 41 ++++--------------- .../io/quarkus/oidc/runtime/OidcUtils.java | 11 ++++- .../CustomSecurityIdentityAugmentor.java | 4 ++ .../it/keycloak/ProtectedResource.java | 6 ++- 4 files changed, 26 insertions(+), 36 deletions(-) diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java index 0e6b4911dba8e..cb4bb8ccd7701 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java @@ -4,7 +4,6 @@ import static io.quarkus.oidc.runtime.OidcIdentityProvider.REFRESH_TOKEN_GRANT_RESPONSE; import java.net.URI; -import java.security.Permission; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -19,12 +18,10 @@ import org.jose4j.jwt.consumer.InvalidJwtException; import io.netty.handler.codec.http.HttpResponseStatus; -import io.quarkus.oidc.AccessTokenCredential; import io.quarkus.oidc.AuthorizationCodeTokens; import io.quarkus.oidc.IdTokenCredential; import io.quarkus.oidc.OidcTenantConfig; import io.quarkus.oidc.OidcTenantConfig.Authentication; -import io.quarkus.oidc.RefreshToken; import io.quarkus.oidc.SecurityEvent; import io.quarkus.oidc.common.runtime.OidcCommonUtils; import io.quarkus.oidc.common.runtime.OidcConstants; @@ -33,7 +30,6 @@ import io.quarkus.security.AuthenticationRedirectException; import io.quarkus.security.identity.IdentityProviderManager; import io.quarkus.security.identity.SecurityIdentity; -import io.quarkus.security.runtime.QuarkusSecurityIdentity; import io.quarkus.vertx.http.runtime.security.ChallengeData; import io.smallrye.mutiny.Uni; import io.vertx.core.http.Cookie; @@ -57,27 +53,6 @@ public class CodeAuthenticationMechanism extends AbstractOidcAuthenticationMecha private static final String STATE_COOKIE_NAME = "q_auth"; private static final String POST_LOGOUT_COOKIE_NAME = "q_post_logout"; - private static QuarkusSecurityIdentity augmentIdentity(SecurityIdentity securityIdentity, - String accessToken, - String refreshToken, - RoutingContext context) { - IdTokenCredential idTokenCredential = securityIdentity.getCredential(IdTokenCredential.class); - RefreshToken refreshTokenCredential = new RefreshToken(refreshToken); - return QuarkusSecurityIdentity.builder() - .setPrincipal(securityIdentity.getPrincipal()) - .addCredential(idTokenCredential) - .addCredential(new AccessTokenCredential(accessToken, refreshTokenCredential, context)) - .addCredential(refreshTokenCredential) - .addRoles(securityIdentity.getRoles()) - .addAttributes(securityIdentity.getAttributes()) - .addPermissionChecker(new Function>() { - @Override - public Uni apply(Permission permission) { - return securityIdentity.checkPermission(permission); - } - }).build(); - } - public Uni authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) { return resolver.resolveConfig(context).chain(new Function>() { @@ -125,6 +100,7 @@ private Uni reAuthenticate(Cookie sessionCookie, sessionCookie.getValue()); context.put(OidcConstants.ACCESS_TOKEN_VALUE, session.getAccessToken()); + context.put(AuthorizationCodeTokens.class.getName(), session); return authenticate(identityProviderManager, context, new IdTokenCredential(session.getIdToken(), context)) .map(new Function() { @Override @@ -133,8 +109,7 @@ public SecurityIdentity apply(SecurityIdentity identity) { fireEvent(SecurityEvent.Type.OIDC_LOGOUT_RP_INITIATED, identity); throw redirectToLogoutEndpoint(context, configContext, session.getIdToken()); } - - return augmentIdentity(identity, session.getAccessToken(), session.getRefreshToken(), context); + return identity; } }).onFailure().recoverWithUni(new Function>() { @Override @@ -299,6 +274,7 @@ public Uni apply(final AuthorizationCodeTokens tokens, final T context.put(NEW_AUTHENTICATION, Boolean.TRUE); context.put(OidcConstants.ACCESS_TOKEN_VALUE, tokens.getAccessToken()); + context.put(AuthorizationCodeTokens.class.getName(), tokens); return authenticate(identityProviderManager, context, new IdTokenCredential(tokens.getIdToken(), context)) @@ -332,8 +308,7 @@ public SecurityIdentity apply(SecurityIdentity identity) { LOG.debugf("Final redirect URI: %s", finalRedirectUri); throw new AuthenticationRedirectException(finalRedirectUri); } else { - return augmentIdentity(identity, tokens.getAccessToken(), - tokens.getRefreshToken(), context); + return identity; } } }).onFailure().transform(new Function() { @@ -520,6 +495,7 @@ public Uni apply(final AuthorizationCodeTokens tokens, final T } } else { context.put(OidcConstants.ACCESS_TOKEN_VALUE, tokens.getAccessToken()); + context.put(AuthorizationCodeTokens.class.getName(), tokens); context.put(REFRESH_TOKEN_GRANT_RESPONSE, Boolean.TRUE); return authenticate(identityProviderManager, context, @@ -530,14 +506,11 @@ public SecurityIdentity apply(SecurityIdentity identity) { // after a successful refresh, rebuild the identity and update the cookie processSuccessfulAuthentication(context, configContext, tokens, identity); - SecurityIdentity newSecurityIdentity = augmentIdentity(identity, - tokens.getAccessToken(), tokens.getRefreshToken(), context); - fireEvent(autoRefresh ? SecurityEvent.Type.OIDC_SESSION_REFRESHED : SecurityEvent.Type.OIDC_SESSION_EXPIRED_AND_REFRESHED, - newSecurityIdentity); + identity); - return newSecurityIdentity; + return identity; } }).onFailure().transform(new Function() { @Override diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcUtils.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcUtils.java index 4a566720c051a..7b4fe1eef1e4c 100644 --- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcUtils.java +++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcUtils.java @@ -15,8 +15,11 @@ import org.jose4j.jwt.JwtClaims; import org.jose4j.jwt.consumer.InvalidJwtException; +import io.quarkus.oidc.AccessTokenCredential; +import io.quarkus.oidc.AuthorizationCodeTokens; import io.quarkus.oidc.OIDCException; import io.quarkus.oidc.OidcTenantConfig; +import io.quarkus.oidc.RefreshToken; import io.quarkus.oidc.TokenIntrospection; import io.quarkus.oidc.UserInfo; import io.quarkus.security.AuthenticationFailedException; @@ -144,7 +147,13 @@ static QuarkusSecurityIdentity validateAndCreateIdentity( OidcTenantConfig config = resolvedContext.oidcConfig; QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder(); builder.addCredential(credential); - + AuthorizationCodeTokens codeTokens = vertxContext != null ? vertxContext.get(AuthorizationCodeTokens.class.getName()) + : null; + if (codeTokens != null) { + RefreshToken refreshTokenCredential = new RefreshToken(codeTokens.getRefreshToken()); + builder.addCredential(refreshTokenCredential); + builder.addCredential(new AccessTokenCredential(codeTokens.getAccessToken(), refreshTokenCredential, vertxContext)); + } JsonWebToken jwtPrincipal; try { JwtClaims jwtClaims = JwtClaims.parse(tokenJson.encode()); diff --git a/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/CustomSecurityIdentityAugmentor.java b/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/CustomSecurityIdentityAugmentor.java index 0936eac60f2b0..5d698913165fd 100644 --- a/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/CustomSecurityIdentityAugmentor.java +++ b/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/CustomSecurityIdentityAugmentor.java @@ -2,7 +2,9 @@ import javax.enterprise.context.ApplicationScoped; +import io.quarkus.oidc.AccessTokenCredential; import io.quarkus.oidc.IdTokenCredential; +import io.quarkus.oidc.common.runtime.OidcConstants; import io.quarkus.security.identity.AuthenticationRequestContext; import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.identity.SecurityIdentityAugmentor; @@ -24,6 +26,8 @@ public Uni augment(SecurityIdentity identity, AuthenticationRe if (cred != null) { QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder(identity); builder.addAttribute(RoutingContext.class.getName(), cred.getRoutingContext()); + builder.addAttribute(OidcConstants.ACCESS_TOKEN_VALUE, + identity.getCredential(AccessTokenCredential.class).getToken()); identity = builder.build(); } return Uni.createFrom().item(identity); diff --git a/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/ProtectedResource.java b/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/ProtectedResource.java index c116c2d6468da..cf8ceb78fda7e 100644 --- a/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/ProtectedResource.java +++ b/integration-tests/oidc-code-flow/src/main/java/io/quarkus/it/keycloak/ProtectedResource.java @@ -19,6 +19,7 @@ import io.quarkus.oidc.OidcConfigurationMetadata; import io.quarkus.oidc.RefreshToken; import io.quarkus.oidc.UserInfo; +import io.quarkus.oidc.common.runtime.OidcConstants; import io.quarkus.security.Authenticated; import io.quarkus.security.identity.SecurityIdentity; import io.vertx.ext.web.RoutingContext; @@ -168,9 +169,12 @@ public String getTenantLogout() { @GET @Path("access") public String getAccessToken() { - if (accessToken.getRawToken() != null && !accessTokenCredential.getToken().equals(accessToken.getRawToken())) { + if (accessToken.getRawToken() != null && + (!accessTokenCredential.getToken().equals(accessToken.getRawToken()) + || !identity.getAttribute(OidcConstants.ACCESS_TOKEN_VALUE).equals(accessToken.getRawToken()))) { throw new OIDCException("Access token values are not equal"); } + return accessToken.getRawToken() != null && !accessToken.getRawToken().isEmpty() ? "AT injected" : "no access"; }