Search in sources :

Example 1 with SignatureVerifierContext

use of org.keycloak.crypto.SignatureVerifierContext in project keycloak by keycloak.

the class UserInfoEndpoint method issueUserInfo.

private Response issueUserInfo(String tokenString) {
    cors = Cors.add(request).auth().allowedMethods(request.getHttpMethod()).auth().exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS);
    try {
        session.clientPolicy().triggerOnEvent(new UserInfoRequestContext(tokenString));
    } catch (ClientPolicyException cpe) {
        throw new CorsErrorResponseException(cors.allowAllOrigins(), cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
    }
    EventBuilder event = new EventBuilder(realm, session, clientConnection).event(EventType.USER_INFO_REQUEST).detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN);
    if (tokenString == null) {
        event.error(Errors.INVALID_TOKEN);
        throw new CorsErrorResponseException(cors.allowAllOrigins(), OAuthErrorException.INVALID_REQUEST, "Token not provided", Response.Status.BAD_REQUEST);
    }
    AccessToken token;
    ClientModel clientModel = null;
    try {
        TokenVerifier<AccessToken> verifier = TokenVerifier.create(tokenString, AccessToken.class).withDefaultChecks().realmUrl(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
        SignatureVerifierContext verifierContext = session.getProvider(SignatureProvider.class, verifier.getHeader().getAlgorithm().name()).verifier(verifier.getHeader().getKeyId());
        verifier.verifierContext(verifierContext);
        token = verifier.verify().getToken();
        clientModel = realm.getClientByClientId(token.getIssuedFor());
        if (clientModel == null) {
            event.error(Errors.CLIENT_NOT_FOUND);
            throw new CorsErrorResponseException(cors.allowAllOrigins(), OAuthErrorException.INVALID_REQUEST, "Client not found", Response.Status.BAD_REQUEST);
        }
        cors.allowedOrigins(session, clientModel);
        TokenVerifier.createWithoutSignature(token).withChecks(NotBeforeCheck.forModel(clientModel), new TokenManager.TokenRevocationCheck(session)).verify();
    } catch (VerificationException e) {
        if (clientModel == null) {
            cors.allowAllOrigins();
        }
        event.error(Errors.INVALID_TOKEN);
        throw newUnauthorizedErrorResponseException(OAuthErrorException.INVALID_TOKEN, "Token verification failed");
    }
    if (!clientModel.getProtocol().equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
        event.error(Errors.INVALID_CLIENT);
        throw new CorsErrorResponseException(cors, Errors.INVALID_CLIENT, "Wrong client protocol.", Response.Status.BAD_REQUEST);
    }
    session.getContext().setClient(clientModel);
    event.client(clientModel);
    if (!clientModel.isEnabled()) {
        event.error(Errors.CLIENT_DISABLED);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Client disabled", Response.Status.BAD_REQUEST);
    }
    UserSessionModel userSession = findValidSession(token, event, clientModel);
    UserModel userModel = userSession.getUser();
    if (userModel == null) {
        event.error(Errors.USER_NOT_FOUND);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "User not found", Response.Status.BAD_REQUEST);
    }
    event.user(userModel).detail(Details.USERNAME, userModel.getUsername());
    // https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-3
    if (OIDCAdvancedConfigWrapper.fromClientModel(clientModel).isUseMtlsHokToken()) {
        if (!MtlsHoKTokenUtil.verifyTokenBindingWithClientCertificate(token, request, session)) {
            event.error(Errors.NOT_ALLOWED);
            throw newUnauthorizedErrorResponseException(OAuthErrorException.UNAUTHORIZED_CLIENT, "Client certificate missing, or its thumbprint and one in the refresh token did NOT match");
        }
    }
    // Existence of authenticatedClientSession for our client already handled before
    AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(clientModel.getId());
    // Retrieve by latest scope parameter
    ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionScopeParameter(clientSession, session);
    AccessToken userInfo = new AccessToken();
    tokenManager.transformUserInfoAccessToken(session, userInfo, userSession, clientSessionCtx);
    Map<String, Object> claims = tokenManager.generateUserInfoClaims(userInfo, userModel);
    Response.ResponseBuilder responseBuilder;
    OIDCAdvancedConfigWrapper cfg = OIDCAdvancedConfigWrapper.fromClientModel(clientModel);
    if (cfg.isUserInfoSignatureRequired()) {
        String issuerUrl = Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName());
        String audience = clientModel.getClientId();
        claims.put("iss", issuerUrl);
        claims.put("aud", audience);
        String signatureAlgorithm = session.tokens().signatureAlgorithm(TokenCategory.USERINFO);
        SignatureProvider signatureProvider = session.getProvider(SignatureProvider.class, signatureAlgorithm);
        SignatureSignerContext signer = signatureProvider.signer();
        String signedUserInfo = new JWSBuilder().type("JWT").jsonContent(claims).sign(signer);
        responseBuilder = Response.ok(signedUserInfo).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JWT);
        event.detail(Details.SIGNATURE_REQUIRED, "true");
        event.detail(Details.SIGNATURE_ALGORITHM, cfg.getUserInfoSignedResponseAlg().toString());
    } else {
        responseBuilder = Response.ok(claims).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
        event.detail(Details.SIGNATURE_REQUIRED, "false");
    }
    event.success();
    return cors.builder(responseBuilder).build();
}
Also used : UserSessionModel(org.keycloak.models.UserSessionModel) SignatureVerifierContext(org.keycloak.crypto.SignatureVerifierContext) OIDCAdvancedConfigWrapper(org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper) SignatureSignerContext(org.keycloak.crypto.SignatureSignerContext) AuthenticatedClientSessionModel(org.keycloak.models.AuthenticatedClientSessionModel) ClientPolicyException(org.keycloak.services.clientpolicy.ClientPolicyException) JWSBuilder(org.keycloak.jose.jws.JWSBuilder) UserModel(org.keycloak.models.UserModel) Response(javax.ws.rs.core.Response) HttpResponse(org.jboss.resteasy.spi.HttpResponse) ClientModel(org.keycloak.models.ClientModel) SignatureProvider(org.keycloak.crypto.SignatureProvider) UserInfoRequestContext(org.keycloak.services.clientpolicy.context.UserInfoRequestContext) EventBuilder(org.keycloak.events.EventBuilder) DefaultClientSessionContext(org.keycloak.services.util.DefaultClientSessionContext) ClientSessionContext(org.keycloak.models.ClientSessionContext) AccessToken(org.keycloak.representations.AccessToken) VerificationException(org.keycloak.common.VerificationException) CorsErrorResponseException(org.keycloak.services.CorsErrorResponseException)

Example 2 with SignatureVerifierContext

use of org.keycloak.crypto.SignatureVerifierContext in project keycloak by keycloak.

the class LoginActionsService method handleActionToken.

protected <T extends JsonWebToken & ActionTokenKeyModel> Response handleActionToken(String tokenString, String execution, String clientId, String tabId) {
    T token;
    ActionTokenHandler<T> handler;
    ActionTokenContext<T> tokenContext;
    String eventError = null;
    String defaultErrorMessage = null;
    AuthenticationSessionModel authSession = null;
    // Setup client, so error page will contain "back to application" link
    ClientModel client = null;
    if (clientId != null) {
        client = realm.getClientByClientId(clientId);
    }
    AuthenticationSessionManager authenticationSessionManager = new AuthenticationSessionManager(session);
    if (client != null) {
        session.getContext().setClient(client);
        authSession = authenticationSessionManager.getCurrentAuthenticationSession(realm, client, tabId);
    }
    event.event(EventType.EXECUTE_ACTION_TOKEN);
    // First resolve action token handler
    try {
        if (tokenString == null) {
            throw new ExplainedTokenVerificationException(null, Errors.NOT_ALLOWED, Messages.INVALID_REQUEST);
        }
        TokenVerifier<DefaultActionTokenKey> tokenVerifier = TokenVerifier.create(tokenString, DefaultActionTokenKey.class);
        DefaultActionTokenKey aToken = tokenVerifier.getToken();
        event.detail(Details.TOKEN_ID, aToken.getId()).detail(Details.ACTION, aToken.getActionId()).user(aToken.getUserId());
        handler = resolveActionTokenHandler(aToken.getActionId());
        eventError = handler.getDefaultEventError();
        defaultErrorMessage = handler.getDefaultErrorMessage();
        if (!realm.isEnabled()) {
            throw new ExplainedTokenVerificationException(aToken, Errors.REALM_DISABLED, Messages.REALM_NOT_ENABLED);
        }
        if (!checkSsl()) {
            throw new ExplainedTokenVerificationException(aToken, Errors.SSL_REQUIRED, Messages.HTTPS_REQUIRED);
        }
        TokenVerifier<DefaultActionTokenKey> verifier = tokenVerifier.withChecks(// Token introspection checks
        TokenVerifier.IS_ACTIVE, new TokenVerifier.RealmUrlCheck(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName())), ACTION_TOKEN_BASIC_CHECKS);
        String kid = verifier.getHeader().getKeyId();
        String algorithm = verifier.getHeader().getAlgorithm().name();
        SignatureVerifierContext signatureVerifier = session.getProvider(SignatureProvider.class, algorithm).verifier(kid);
        verifier.verifierContext(signatureVerifier);
        verifier.verify();
        token = TokenVerifier.create(tokenString, handler.getTokenClass()).getToken();
    } catch (TokenNotActiveException ex) {
        if (authSession != null) {
            event.clone().error(Errors.EXPIRED_CODE);
            String flowPath = authSession.getClientNote(AuthorizationEndpointBase.APP_INITIATED_FLOW);
            if (flowPath == null) {
                flowPath = AUTHENTICATE_PATH;
            }
            AuthenticationProcessor.resetFlow(authSession, flowPath);
            // Process correct flow
            return processFlowFromPath(flowPath, authSession, Messages.EXPIRED_ACTION_TOKEN_SESSION_EXISTS);
        }
        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);
    }
    // Now proceed with the verification and handle the token
    tokenContext = new ActionTokenContext(session, realm, session.getContext().getUri(), clientConnection, request, event, handler, execution, this::processFlow, this::brokerLoginFlow);
    try {
        String tokenAuthSessionCompoundId = handler.getAuthenticationSessionIdFromToken(token, tokenContext, authSession);
        if (tokenAuthSessionCompoundId != null) {
            // This can happen if the token contains ID but user opens the link in a new browser
            String sessionId = AuthenticationSessionCompoundId.encoded(tokenAuthSessionCompoundId).getRootSessionId();
            LoginActionsServiceChecks.checkNotLoggedInYet(tokenContext, authSession, sessionId);
        }
        if (authSession == null) {
            authSession = handler.startFreshAuthenticationSession(token, tokenContext);
            tokenContext.setAuthenticationSession(authSession, true);
        } else if (tokenAuthSessionCompoundId == null || !LoginActionsServiceChecks.doesAuthenticationSessionFromCookieMatchOneFromToken(tokenContext, authSession, tokenAuthSessionCompoundId)) {
            // There exists an authentication session but no auth session ID was received in the action token
            logger.debugf("Authentication session in progress but no authentication session ID was found in action token %s, restarting.", token.getId());
            authenticationSessionManager.removeAuthenticationSession(realm, authSession, false);
            authSession = handler.startFreshAuthenticationSession(token, tokenContext);
            tokenContext.setAuthenticationSession(authSession, true);
            processLocaleParam(authSession);
        }
        initLoginEvent(authSession);
        event.event(handler.eventType());
        LoginActionsServiceChecks.checkIsUserValid(token, tokenContext);
        LoginActionsServiceChecks.checkIsClientValid(token, tokenContext);
        session.getContext().setClient(authSession.getClient());
        TokenVerifier.createWithoutSignature(token).withChecks(handler.getVerifiers(tokenContext)).verify();
        authSession = tokenContext.getAuthenticationSession();
        event = tokenContext.getEvent();
        event.event(handler.eventType());
        if (!handler.canUseTokenRepeatedly(token, tokenContext)) {
            LoginActionsServiceChecks.checkTokenWasNotUsedYet(token, tokenContext);
            authSession.setAuthNote(AuthenticationManager.INVALIDATE_ACTION_TOKEN, token.serializeKey());
        }
        authSession.setAuthNote(DefaultActionTokenKey.ACTION_TOKEN_USER_ID, token.getUserId());
        authSession.setAuthNote(Constants.KEY, tokenString);
        return handler.handleToken(token, tokenContext);
    } catch (ExplainedTokenVerificationException ex) {
        return handleActionTokenVerificationException(tokenContext, ex, ex.getErrorEvent(), ex.getMessage());
    } catch (LoginActionsServiceException ex) {
        Response response = ex.getResponse();
        return response == null ? handleActionTokenVerificationException(tokenContext, ex, eventError, defaultErrorMessage) : response;
    } catch (VerificationException ex) {
        return handleActionTokenVerificationException(tokenContext, ex, eventError, defaultErrorMessage);
    }
}
Also used : TokenNotActiveException(org.keycloak.exceptions.TokenNotActiveException) AuthenticationSessionModel(org.keycloak.sessions.AuthenticationSessionModel) RootAuthenticationSessionModel(org.keycloak.sessions.RootAuthenticationSessionModel) SignatureVerifierContext(org.keycloak.crypto.SignatureVerifierContext) ExplainedVerificationException(org.keycloak.authentication.ExplainedVerificationException) ActionTokenContext(org.keycloak.authentication.actiontoken.ActionTokenContext) DefaultActionTokenKey(org.keycloak.authentication.actiontoken.DefaultActionTokenKey) AuthenticationSessionManager(org.keycloak.services.managers.AuthenticationSessionManager) Response(javax.ws.rs.core.Response) ClientModel(org.keycloak.models.ClientModel) SignatureProvider(org.keycloak.crypto.SignatureProvider) GET(javax.ws.rs.GET) POST(javax.ws.rs.POST) ExplainedTokenVerificationException(org.keycloak.authentication.actiontoken.ExplainedTokenVerificationException) TokenVerifier(org.keycloak.TokenVerifier) VerificationException(org.keycloak.common.VerificationException) ExplainedVerificationException(org.keycloak.authentication.ExplainedVerificationException) ExplainedTokenVerificationException(org.keycloak.authentication.actiontoken.ExplainedTokenVerificationException)

Example 3 with SignatureVerifierContext

use of org.keycloak.crypto.SignatureVerifierContext in project keycloak by keycloak.

the class ClientRegistrationTokenUtils method verifyToken.

public static TokenVerification verifyToken(KeycloakSession session, RealmModel realm, String token) {
    if (token == null) {
        return TokenVerification.error(new RuntimeException("Missing token"));
    }
    String kid;
    JsonWebToken jwt;
    try {
        TokenVerifier<JsonWebToken> verifier = TokenVerifier.create(token, JsonWebToken.class).withChecks(new TokenVerifier.RealmUrlCheck(getIssuer(session, realm)), TokenVerifier.IS_ACTIVE);
        SignatureVerifierContext verifierContext = session.getProvider(SignatureProvider.class, verifier.getHeader().getAlgorithm().name()).verifier(verifier.getHeader().getKeyId());
        verifier.verifierContext(verifierContext);
        kid = verifierContext.getKid();
        verifier.verify();
        jwt = verifier.getToken();
    } catch (VerificationException e) {
        return TokenVerification.error(new RuntimeException("Failed decode token", e));
    }
    if (!(TokenUtil.TOKEN_TYPE_BEARER.equals(jwt.getType()) || TYPE_INITIAL_ACCESS_TOKEN.equals(jwt.getType()) || TYPE_REGISTRATION_ACCESS_TOKEN.equals(jwt.getType()))) {
        return TokenVerification.error(new RuntimeException("Invalid type of token"));
    }
    return TokenVerification.success(kid, jwt);
}
Also used : SignatureProvider(org.keycloak.crypto.SignatureProvider) SignatureVerifierContext(org.keycloak.crypto.SignatureVerifierContext) TokenVerifier(org.keycloak.TokenVerifier) VerificationException(org.keycloak.common.VerificationException) JsonWebToken(org.keycloak.representations.JsonWebToken)

Example 4 with SignatureVerifierContext

use of org.keycloak.crypto.SignatureVerifierContext in project keycloak by keycloak.

the class OpenShiftTokenReviewEndpoint method tokenReview.

@Path("/{client_id}")
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response tokenReview(@PathParam("client_id") String clientId, OpenShiftTokenReviewRequestRepresentation reviewRequest) throws Exception {
    event.event(EventType.INTROSPECT_TOKEN);
    if (clientId != null) {
        session.setAttribute("client_id", clientId);
    }
    checkSsl();
    checkRealm();
    authorizeClient();
    RealmModel realm = session.getContext().getRealm();
    AccessToken token = null;
    try {
        TokenVerifier<AccessToken> verifier = TokenVerifier.create(reviewRequest.getSpec().getToken(), AccessToken.class).realmUrl(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName())).audience(reviewRequest.getSpec().getAudiences());
        SignatureVerifierContext verifierContext = session.getProvider(SignatureProvider.class, verifier.getHeader().getAlgorithm().name()).verifier(verifier.getHeader().getKeyId());
        verifier.verifierContext(verifierContext);
        verifier.verify();
        token = verifier.getToken();
    } catch (VerificationException e) {
        error(401, Errors.INVALID_TOKEN, "Token verification failure");
    }
    if (!tokenManager.checkTokenValidForIntrospection(session, realm, token, true)) {
        error(401, Errors.INVALID_TOKEN, "Token verification failure");
    }
    OpenShiftTokenReviewResponseRepresentation response = new OpenShiftTokenReviewResponseRepresentation();
    response.getStatus().setAuthenticated(true);
    response.getStatus().setUser(new OpenShiftTokenReviewResponseRepresentation.User());
    OpenShiftTokenReviewResponseRepresentation.User userRep = response.getStatus().getUser();
    userRep.setUid(token.getSubject());
    userRep.setUsername(token.getPreferredUsername());
    if (token.getScope() != null && !token.getScope().isEmpty()) {
        OpenShiftTokenReviewResponseRepresentation.Extra extra = new OpenShiftTokenReviewResponseRepresentation.Extra();
        extra.setScopes(token.getScope().split(" "));
        userRep.setExtra(extra);
    }
    if (token.getOtherClaims() != null && token.getOtherClaims().get("groups") != null) {
        List<String> groups = (List<String>) token.getOtherClaims().get("groups");
        userRep.setGroups(groups);
    }
    event.success();
    return Response.ok(response, MediaType.APPLICATION_JSON).build();
}
Also used : SignatureVerifierContext(org.keycloak.crypto.SignatureVerifierContext) RealmModel(org.keycloak.models.RealmModel) SignatureProvider(org.keycloak.crypto.SignatureProvider) AccessToken(org.keycloak.representations.AccessToken) VerificationException(org.keycloak.common.VerificationException) List(java.util.List) Path(javax.ws.rs.Path) POST(javax.ws.rs.POST) Consumes(javax.ws.rs.Consumes) Produces(javax.ws.rs.Produces)

Example 5 with SignatureVerifierContext

use of org.keycloak.crypto.SignatureVerifierContext in project keycloak by keycloak.

the class AccessTokenIntrospectionProvider method verifyAccessToken.

protected AccessToken verifyAccessToken(String token) {
    AccessToken accessToken;
    try {
        TokenVerifier<AccessToken> verifier = TokenVerifier.create(token, AccessToken.class).realmUrl(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
        SignatureVerifierContext verifierContext = session.getProvider(SignatureProvider.class, verifier.getHeader().getAlgorithm().name()).verifier(verifier.getHeader().getKeyId());
        verifier.verifierContext(verifierContext);
        accessToken = verifier.verify().getToken();
    } catch (VerificationException e) {
        logger.debugf("JWT check failed: %s", e.getMessage());
        return null;
    }
    RealmModel realm = this.session.getContext().getRealm();
    return tokenManager.checkTokenValidForIntrospection(session, realm, accessToken, false) ? accessToken : null;
}
Also used : RealmModel(org.keycloak.models.RealmModel) SignatureProvider(org.keycloak.crypto.SignatureProvider) SignatureVerifierContext(org.keycloak.crypto.SignatureVerifierContext) AccessToken(org.keycloak.representations.AccessToken) VerificationException(org.keycloak.common.VerificationException)

Aggregations

VerificationException (org.keycloak.common.VerificationException)7 SignatureProvider (org.keycloak.crypto.SignatureProvider)7 SignatureVerifierContext (org.keycloak.crypto.SignatureVerifierContext)7 AccessToken (org.keycloak.representations.AccessToken)5 ClientModel (org.keycloak.models.ClientModel)3 UserSessionModel (org.keycloak.models.UserSessionModel)3 POST (javax.ws.rs.POST)2 Response (javax.ws.rs.core.Response)2 TokenVerifier (org.keycloak.TokenVerifier)2 RealmModel (org.keycloak.models.RealmModel)2 UserModel (org.keycloak.models.UserModel)2 ClientPolicyException (org.keycloak.services.clientpolicy.ClientPolicyException)2 UnsupportedEncodingException (java.io.UnsupportedEncodingException)1 List (java.util.List)1 Consumes (javax.ws.rs.Consumes)1 GET (javax.ws.rs.GET)1 Path (javax.ws.rs.Path)1 Produces (javax.ws.rs.Produces)1 Cookie (javax.ws.rs.core.Cookie)1 NewCookie (javax.ws.rs.core.NewCookie)1