From ddcbee2bff6ee7ec1994f2b6020f7577c0539d0d Mon Sep 17 00:00:00 2001 From: Hynek Mlnarik Date: Fri, 14 Jul 2017 13:31:47 +0200 Subject: [PATCH] KEYCLOAK-4187 Minor updates in API --- .../actiontoken/AbstractActionTokenHander.java | 7 ++++--- .../actiontoken/ActionTokenHandler.java | 2 +- .../actiontoken/DefaultActionToken.java | 4 ++-- .../actiontoken/DefaultActionTokenKey.java | 3 +++ .../services/resources/LoginActionsService.java | 16 +++++++++------- .../resources/LoginActionsServiceChecks.java | 9 +++++---- 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/services/src/main/java/org/keycloak/authentication/actiontoken/AbstractActionTokenHander.java b/services/src/main/java/org/keycloak/authentication/actiontoken/AbstractActionTokenHander.java index 52d94d95f7..ccdc2f8cd9 100644 --- a/services/src/main/java/org/keycloak/authentication/actiontoken/AbstractActionTokenHander.java +++ b/services/src/main/java/org/keycloak/authentication/actiontoken/AbstractActionTokenHander.java @@ -20,6 +20,7 @@ import org.keycloak.Config.Scope; import org.keycloak.events.EventType; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.representations.JsonWebToken; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.sessions.AuthenticationSessionModel; @@ -27,7 +28,7 @@ import org.keycloak.sessions.AuthenticationSessionModel; * * @author hmlnarik */ -public abstract class AbstractActionTokenHander implements ActionTokenHandler, ActionTokenHandlerFactory { +public abstract class AbstractActionTokenHander implements ActionTokenHandler, ActionTokenHandlerFactory { private final String id; private final Class tokenClass; @@ -86,8 +87,8 @@ public abstract class AbstractActionTokenHander im } @Override - public String getAuthenticationSessionIdFromToken(T token) { - return token == null ? null : token.getAuthenticationSessionId(); + public String getAuthenticationSessionIdFromToken(T token, ActionTokenContext tokenContext) { + return token instanceof DefaultActionToken ? ((DefaultActionToken) token).getAuthenticationSessionId() : null; } @Override diff --git a/services/src/main/java/org/keycloak/authentication/actiontoken/ActionTokenHandler.java b/services/src/main/java/org/keycloak/authentication/actiontoken/ActionTokenHandler.java index f8d02d3468..b61c9c0c57 100644 --- a/services/src/main/java/org/keycloak/authentication/actiontoken/ActionTokenHandler.java +++ b/services/src/main/java/org/keycloak/authentication/actiontoken/ActionTokenHandler.java @@ -64,7 +64,7 @@ public interface ActionTokenHandler extends Provider { * @param token Token. Can be {@code null} * @return authentication session ID */ - String getAuthenticationSessionIdFromToken(T token); + String getAuthenticationSessionIdFromToken(T token, ActionTokenContext tokenContext); /** * Returns a event type logged with {@link EventBuilder} class. diff --git a/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionToken.java b/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionToken.java index ba4488039a..0f514d0d04 100644 --- a/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionToken.java +++ b/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionToken.java @@ -39,7 +39,7 @@ public class DefaultActionToken extends DefaultActionTokenKey implements ActionT public static final String JSON_FIELD_AUTHENTICATION_SESSION_ID = "asid"; - public static final Predicate ACTION_TOKEN_BASIC_CHECKS = t -> { + public static final Predicate ACTION_TOKEN_BASIC_CHECKS = t -> { if (t.getActionVerificationNonce() == null) { throw new VerificationException("Nonce not present."); } @@ -131,7 +131,7 @@ public class DefaultActionToken extends DefaultActionTokenKey implements ActionT *
  • {@code issuer}: URI of the given realm
  • *
  • {@code audience}: URI of the given realm (same as issuer)
  • * - * + * * @param session * @param realm * @param uri diff --git a/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionTokenKey.java b/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionTokenKey.java index b41681f303..cc4ba32e3c 100644 --- a/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionTokenKey.java +++ b/services/src/main/java/org/keycloak/authentication/actiontoken/DefaultActionTokenKey.java @@ -36,6 +36,9 @@ public class DefaultActionTokenKey extends JsonWebToken implements ActionTokenKe @JsonProperty(value = JSON_FIELD_ACTION_VERIFICATION_NONCE, required = true) private UUID actionVerificationNonce; + public DefaultActionTokenKey() { + } + public DefaultActionTokenKey(String userId, String actionId, int absoluteExpirationInSecs, UUID actionVerificationNonce) { this.subject = userId; this.type = actionId; diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java index 054cc6ef0a..5e8b0fe8bf 100755 --- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java +++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java @@ -16,7 +16,6 @@ */ package org.keycloak.services.resources; -import org.keycloak.authentication.actiontoken.DefaultActionToken; import org.keycloak.authentication.actiontoken.DefaultActionTokenKey; import org.jboss.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; @@ -27,6 +26,7 @@ import org.keycloak.authentication.RequiredActionContextResult; import org.keycloak.authentication.RequiredActionFactory; import org.keycloak.authentication.RequiredActionProvider; import org.keycloak.TokenVerifier; +import org.keycloak.authentication.ExplainedVerificationException; import org.keycloak.authentication.actiontoken.*; import org.keycloak.authentication.actiontoken.resetcred.ResetCredentialsActionTokenHandler; import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator; @@ -59,6 +59,7 @@ import org.keycloak.protocol.LoginProtocol.Error; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.utils.OIDCResponseMode; import org.keycloak.protocol.oidc.utils.OIDCResponseType; +import org.keycloak.representations.JsonWebToken; import org.keycloak.services.ErrorPage; import org.keycloak.services.ServicesLogger; import org.keycloak.services.Urls; @@ -405,7 +406,7 @@ public class LoginActionsService { return handleActionToken(key, execution, clientId); } - protected Response handleActionToken(String tokenString, String execution, String clientId) { + protected Response handleActionToken(String tokenString, String execution, String clientId) { T token; ActionTokenHandler handler; ActionTokenContext tokenContext; @@ -430,8 +431,8 @@ public class LoginActionsService { throw new ExplainedTokenVerificationException(null, Errors.NOT_ALLOWED, Messages.INVALID_REQUEST); } - TokenVerifier tokenVerifier = TokenVerifier.create(tokenString, DefaultActionToken.class); - DefaultActionToken aToken = tokenVerifier.getToken(); + TokenVerifier tokenVerifier = TokenVerifier.create(tokenString, DefaultActionTokenKey.class); + DefaultActionTokenKey aToken = tokenVerifier.getToken(); event .detail(Details.TOKEN_ID, aToken.getId()) @@ -477,6 +478,8 @@ public class LoginActionsService { return handleActionTokenVerificationException(null, ex, Errors.EXPIRED_CODE, Messages.EXPIRED_ACTION_TOKEN_NO_SESSION); } catch (ExplainedTokenVerificationException ex) { return handleActionTokenVerificationException(null, ex, ex.getErrorEvent(), ex.getMessage()); + } catch (ExplainedVerificationException ex) { + return handleActionTokenVerificationException(null, ex, ex.getErrorEvent(), ex.getMessage()); } catch (VerificationException ex) { return handleActionTokenVerificationException(null, ex, eventError, defaultErrorMessage); } @@ -485,7 +488,7 @@ public class LoginActionsService { tokenContext = new ActionTokenContext(session, realm, uriInfo, clientConnection, request, event, handler, execution, this::processFlow, this::brokerLoginFlow); try { - String tokenAuthSessionId = handler.getAuthenticationSessionIdFromToken(token); + String tokenAuthSessionId = handler.getAuthenticationSessionIdFromToken(token, tokenContext); if (tokenAuthSessionId != null) { // This can happen if the token contains ID but user opens the link in a new browser @@ -541,7 +544,6 @@ public class LoginActionsService { } } - private Response processFlowFromPath(String flowPath, AuthenticationSessionModel authSession, String errorMessage) { if (AUTHENTICATE_PATH.equals(flowPath)) { return processAuthentication(false, null, authSession, errorMessage); @@ -555,7 +557,7 @@ public class LoginActionsService { } - private ActionTokenHandler resolveActionTokenHandler(String actionId) throws VerificationException { + private ActionTokenHandler resolveActionTokenHandler(String actionId) throws VerificationException { if (actionId == null) { throw new VerificationException("Action token operation not set"); } diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsServiceChecks.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsServiceChecks.java index 9edc513b44..b9031cb25b 100644 --- a/services/src/main/java/org/keycloak/services/resources/LoginActionsServiceChecks.java +++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsServiceChecks.java @@ -18,7 +18,7 @@ package org.keycloak.services.resources; import org.keycloak.TokenVerifier.Predicate; import org.keycloak.authentication.AuthenticationProcessor; -import org.keycloak.authentication.actiontoken.DefaultActionToken; +import org.keycloak.authentication.actiontoken.DefaultActionTokenKey; import org.keycloak.authentication.ExplainedVerificationException; import org.keycloak.authentication.actiontoken.ActionTokenContext; import org.keycloak.authentication.actiontoken.ExplainedTokenVerificationException; @@ -152,7 +152,7 @@ public class LoginActionsServiceChecks { * Verifies whether the user given by ID both exists in the current realm. If yes, * it optionally also injects the user using the given function (e.g. into session context). */ - public static void checkIsUserValid(T token, ActionTokenContext context) throws VerificationException { + public static void checkIsUserValid(T token, ActionTokenContext context) throws VerificationException { try { checkIsUserValid(context.getSession(), context.getRealm(), token.getUserId(), context.getAuthenticationSession()::setAuthenticatedUser); } catch (ExplainedVerificationException ex) { @@ -178,7 +178,7 @@ public class LoginActionsServiceChecks { * Verifies whether the client denoted by client ID in token's {@code iss} ({@code issuedFor}) * field both exists and is enabled. */ - public static void checkIsClientValid(T token, ActionTokenContext context) throws VerificationException { + public static void checkIsClientValid(T token, ActionTokenContext context) throws VerificationException { String clientId = token.getIssuedFor(); AuthenticationSessionModel authSession = context.getAuthenticationSession(); ClientModel client = authSession == null ? null : authSession.getClient(); @@ -297,8 +297,9 @@ public class LoginActionsServiceChecks { return true; } - public static void checkTokenWasNotUsedYet(T token, ActionTokenContext context) throws VerificationException { + public static void checkTokenWasNotUsedYet(T token, ActionTokenContext context) throws VerificationException { ActionTokenStoreProvider actionTokenStore = context.getSession().getProvider(ActionTokenStoreProvider.class); + if (actionTokenStore.get(token) != null) { throw new ExplainedTokenVerificationException(token, Errors.EXPIRED_CODE, Messages.EXPIRED_ACTION); }