Search in sources :

Example 16 with ClientSessionContext

use of org.keycloak.models.ClientSessionContext in project keycloak by keycloak.

the class TokenEndpoint method resourceOwnerPasswordCredentialsGrant.

public Response resourceOwnerPasswordCredentialsGrant() {
    event.detail(Details.AUTH_METHOD, "oauth_credentials");
    if (!client.isDirectAccessGrantsEnabled()) {
        event.error(Errors.NOT_ALLOWED);
        throw new CorsErrorResponseException(cors, OAuthErrorException.UNAUTHORIZED_CLIENT, "Client not allowed for direct access grants", Response.Status.BAD_REQUEST);
    }
    if (client.isConsentRequired()) {
        event.error(Errors.CONSENT_DENIED);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_CLIENT, "Client requires user consent", Response.Status.BAD_REQUEST);
    }
    try {
        session.clientPolicy().triggerOnEvent(new ResourceOwnerPasswordCredentialsContext(formParams));
    } catch (ClientPolicyException cpe) {
        event.error(cpe.getError());
        throw new CorsErrorResponseException(cors, cpe.getError(), cpe.getErrorDetail(), cpe.getErrorStatus());
    }
    String scope = getRequestedScopes();
    RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
    AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(client);
    authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
    authSession.setAction(AuthenticatedClientSessionModel.Action.AUTHENTICATE.name());
    authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
    authSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
    AuthenticationFlowModel flow = AuthenticationFlowResolver.resolveDirectGrantFlow(authSession);
    String flowId = flow.getId();
    AuthenticationProcessor processor = new AuthenticationProcessor();
    processor.setAuthenticationSession(authSession).setFlowId(flowId).setConnection(clientConnection).setEventBuilder(event).setRealm(realm).setSession(session).setUriInfo(session.getContext().getUri()).setRequest(request);
    Response challenge = processor.authenticateOnly();
    if (challenge != null) {
        // Remove authentication session as "Resource Owner Password Credentials Grant" is single-request scoped authentication
        new AuthenticationSessionManager(session).removeAuthenticationSession(realm, authSession, false);
        cors.build(httpResponse);
        return challenge;
    }
    processor.evaluateRequiredActionTriggers();
    UserModel user = authSession.getAuthenticatedUser();
    if (user.getRequiredActionsStream().count() > 0 || authSession.getRequiredActions().size() > 0) {
        // Remove authentication session as "Resource Owner Password Credentials Grant" is single-request scoped authentication
        new AuthenticationSessionManager(session).removeAuthenticationSession(realm, authSession, false);
        event.error(Errors.RESOLVE_REQUIRED_ACTIONS);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Account is not fully set up", Response.Status.BAD_REQUEST);
    }
    AuthenticationManager.setClientScopesInSession(authSession);
    ClientSessionContext clientSessionCtx = processor.attachSession();
    UserSessionModel userSession = processor.getUserSession();
    updateUserSessionFromClientAuth(userSession);
    TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSessionCtx).generateAccessToken();
    if (OIDCAdvancedConfigWrapper.fromClientModel(client).isUseRefreshToken()) {
        responseBuilder.generateRefreshToken();
    }
    String scopeParam = clientSessionCtx.getClientSession().getNote(OAuth2Constants.SCOPE);
    if (TokenUtil.isOIDCRequest(scopeParam)) {
        responseBuilder.generateIDToken().generateAccessTokenHash();
    }
    // TODO : do the same as codeToToken()
    AccessTokenResponse res = responseBuilder.build();
    event.success();
    AuthenticationManager.logSuccess(session, authSession);
    return cors.builder(Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).build();
}
Also used : AuthenticationSessionModel(org.keycloak.sessions.AuthenticationSessionModel) RootAuthenticationSessionModel(org.keycloak.sessions.RootAuthenticationSessionModel) UserSessionModel(org.keycloak.models.UserSessionModel) ClientPolicyException(org.keycloak.services.clientpolicy.ClientPolicyException) AuthenticationSessionManager(org.keycloak.services.managers.AuthenticationSessionManager) AccessTokenResponse(org.keycloak.representations.AccessTokenResponse) Response(javax.ws.rs.core.Response) HttpResponse(org.jboss.resteasy.spi.HttpResponse) UserModel(org.keycloak.models.UserModel) DefaultClientSessionContext(org.keycloak.services.util.DefaultClientSessionContext) ClientSessionContext(org.keycloak.models.ClientSessionContext) ResourceOwnerPasswordCredentialsContext(org.keycloak.services.clientpolicy.context.ResourceOwnerPasswordCredentialsContext) RootAuthenticationSessionModel(org.keycloak.sessions.RootAuthenticationSessionModel) AuthenticationFlowModel(org.keycloak.models.AuthenticationFlowModel) CorsErrorResponseException(org.keycloak.services.CorsErrorResponseException) AuthenticationProcessor(org.keycloak.authentication.AuthenticationProcessor) TokenManager(org.keycloak.protocol.oidc.TokenManager) AccessTokenResponse(org.keycloak.representations.AccessTokenResponse)

Example 17 with ClientSessionContext

use of org.keycloak.models.ClientSessionContext in project keycloak by keycloak.

the class TokenEndpoint method codeToToken.

public Response codeToToken() {
    String code = formParams.getFirst(OAuth2Constants.CODE);
    if (code == null) {
        event.error(Errors.INVALID_CODE);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Missing parameter: " + OAuth2Constants.CODE, Response.Status.BAD_REQUEST);
    }
    OAuth2CodeParser.ParseResult parseResult = OAuth2CodeParser.parseCode(session, code, realm, event);
    if (parseResult.isIllegalCode()) {
        AuthenticatedClientSessionModel clientSession = parseResult.getClientSession();
        // Attempt to use same code twice should invalidate existing clientSession
        if (clientSession != null) {
            clientSession.detachFromUserSession();
        }
        event.error(Errors.INVALID_CODE);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Code not valid", Response.Status.BAD_REQUEST);
    }
    AuthenticatedClientSessionModel clientSession = parseResult.getClientSession();
    if (parseResult.isExpiredCode()) {
        event.error(Errors.EXPIRED_CODE);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Code is expired", Response.Status.BAD_REQUEST);
    }
    UserSessionModel userSession = null;
    if (clientSession != null) {
        userSession = clientSession.getUserSession();
    }
    if (userSession == null) {
        event.error(Errors.USER_SESSION_NOT_FOUND);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "User session not found", Response.Status.BAD_REQUEST);
    }
    UserModel user = userSession.getUser();
    if (user == null) {
        event.error(Errors.USER_NOT_FOUND);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "User not found", Response.Status.BAD_REQUEST);
    }
    event.user(userSession.getUser());
    if (!user.isEnabled()) {
        event.error(Errors.USER_DISABLED);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "User disabled", Response.Status.BAD_REQUEST);
    }
    OAuth2Code codeData = parseResult.getCodeData();
    String redirectUri = codeData.getRedirectUriParam();
    String redirectUriParam = formParams.getFirst(OAuth2Constants.REDIRECT_URI);
    // KEYCLOAK-4478 Backwards compatibility with the adapters earlier than KC 3.4.2
    if (redirectUriParam != null && redirectUriParam.contains("session_state=") && !redirectUri.contains("session_state=")) {
        redirectUriParam = KeycloakUriBuilder.fromUri(redirectUriParam).replaceQueryParam(OAuth2Constants.SESSION_STATE, null).build().toString();
    }
    if (redirectUri != null && !redirectUri.equals(redirectUriParam)) {
        event.error(Errors.INVALID_CODE);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Incorrect redirect_uri", Response.Status.BAD_REQUEST);
    }
    if (!client.getClientId().equals(clientSession.getClient().getClientId())) {
        event.error(Errors.INVALID_CODE);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Auth error", Response.Status.BAD_REQUEST);
    }
    if (!client.isStandardFlowEnabled()) {
        event.error(Errors.NOT_ALLOWED);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Client not allowed to exchange code", Response.Status.BAD_REQUEST);
    }
    if (!AuthenticationManager.isSessionValid(realm, userSession)) {
        event.error(Errors.USER_SESSION_NOT_FOUND);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Session not active", Response.Status.BAD_REQUEST);
    }
    // https://tools.ietf.org/html/rfc7636#section-4.6
    String codeVerifier = formParams.getFirst(OAuth2Constants.CODE_VERIFIER);
    String codeChallenge = codeData.getCodeChallenge();
    String codeChallengeMethod = codeData.getCodeChallengeMethod();
    String authUserId = user.getId();
    String authUsername = user.getUsername();
    if (authUserId == null) {
        authUserId = "unknown";
    }
    if (authUsername == null) {
        authUsername = "unknown";
    }
    if (codeChallengeMethod != null && !codeChallengeMethod.isEmpty()) {
        PkceUtils.checkParamsForPkceEnforcedClient(codeVerifier, codeChallenge, codeChallengeMethod, authUserId, authUsername, event, cors);
    } else {
        // PKCE Activation is OFF, execute the codes implemented in KEYCLOAK-2604
        PkceUtils.checkParamsForPkceNotEnforcedClient(codeVerifier, codeChallenge, codeChallengeMethod, authUserId, authUsername, event, cors);
    }
    try {
        session.clientPolicy().triggerOnEvent(new TokenRequestContext(formParams, parseResult));
    } catch (ClientPolicyException cpe) {
        event.error(cpe.getError());
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, cpe.getErrorDetail(), Response.Status.BAD_REQUEST);
    }
    updateClientSession(clientSession);
    updateUserSessionFromClientAuth(userSession);
    // Compute client scopes again from scope parameter. Check if user still has them granted
    // (but in code-to-token request, it could just theoretically happen that they are not available)
    String scopeParam = codeData.getScope();
    Supplier<Stream<ClientScopeModel>> clientScopesSupplier = () -> TokenManager.getRequestedClientScopes(scopeParam, client);
    if (!TokenManager.verifyConsentStillAvailable(session, user, client, clientScopesSupplier.get())) {
        event.error(Errors.NOT_ALLOWED);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_SCOPE, "Client no longer has requested consent from user", Response.Status.BAD_REQUEST);
    }
    ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndScopeParameter(clientSession, scopeParam, session);
    // Set nonce as an attribute in the ClientSessionContext. Will be used for the token generation
    clientSessionCtx.setAttribute(OIDCLoginProtocol.NONCE_PARAM, codeData.getNonce());
    return createTokenResponse(user, userSession, clientSessionCtx, scopeParam, true);
}
Also used : TokenRequestContext(org.keycloak.services.clientpolicy.context.TokenRequestContext) ServiceAccountTokenRequestContext(org.keycloak.services.clientpolicy.context.ServiceAccountTokenRequestContext) UserSessionModel(org.keycloak.models.UserSessionModel) AuthenticatedClientSessionModel(org.keycloak.models.AuthenticatedClientSessionModel) OAuth2CodeParser(org.keycloak.protocol.oidc.utils.OAuth2CodeParser) ClientPolicyException(org.keycloak.services.clientpolicy.ClientPolicyException) UserModel(org.keycloak.models.UserModel) DefaultClientSessionContext(org.keycloak.services.util.DefaultClientSessionContext) ClientSessionContext(org.keycloak.models.ClientSessionContext) OAuth2Code(org.keycloak.protocol.oidc.utils.OAuth2Code) Stream(java.util.stream.Stream) CorsErrorResponseException(org.keycloak.services.CorsErrorResponseException)

Example 18 with ClientSessionContext

use of org.keycloak.models.ClientSessionContext in project keycloak by keycloak.

the class TokenManager method attachAuthenticationSession.

public static ClientSessionContext attachAuthenticationSession(KeycloakSession session, UserSessionModel userSession, AuthenticationSessionModel authSession) {
    ClientModel client = authSession.getClient();
    AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
    if (clientSession == null) {
        clientSession = session.sessions().createClientSession(userSession.getRealm(), client, userSession);
    }
    clientSession.setRedirectUri(authSession.getRedirectUri());
    clientSession.setProtocol(authSession.getProtocol());
    Set<String> clientScopeIds;
    if (Profile.isFeatureEnabled(Profile.Feature.DYNAMIC_SCOPES)) {
        clientScopeIds = AuthorizationContextUtil.getClientScopesStreamFromAuthorizationRequestContextWithClient(session, authSession.getClientNote(OAuth2Constants.SCOPE)).map(ClientScopeModel::getId).collect(Collectors.toSet());
    } else {
        clientScopeIds = authSession.getClientScopes();
    }
    Map<String, String> transferredNotes = authSession.getClientNotes();
    for (Map.Entry<String, String> entry : transferredNotes.entrySet()) {
        clientSession.setNote(entry.getKey(), entry.getValue());
    }
    Map<String, String> transferredUserSessionNotes = authSession.getUserSessionNotes();
    for (Map.Entry<String, String> entry : transferredUserSessionNotes.entrySet()) {
        userSession.setNote(entry.getKey(), entry.getValue());
    }
    clientSession.setNote(Constants.LEVEL_OF_AUTHENTICATION, String.valueOf(AuthenticatorUtil.getCurrentLevelOfAuthentication(authSession)));
    clientSession.setTimestamp(Time.currentTime());
    // Remove authentication session now
    new AuthenticationSessionManager(session).removeAuthenticationSession(userSession.getRealm(), authSession, true);
    ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndClientScopeIds(clientSession, clientScopeIds, session);
    return clientSessionCtx;
}
Also used : AuthenticationSessionManager(org.keycloak.services.managers.AuthenticationSessionManager) ClientModel(org.keycloak.models.ClientModel) DefaultClientSessionContext(org.keycloak.services.util.DefaultClientSessionContext) ClientSessionContext(org.keycloak.models.ClientSessionContext) AuthenticatedClientSessionModel(org.keycloak.models.AuthenticatedClientSessionModel) ClientScopeModel(org.keycloak.models.ClientScopeModel) Map(java.util.Map) HashMap(java.util.HashMap)

Example 19 with ClientSessionContext

use of org.keycloak.models.ClientSessionContext in project keycloak by keycloak.

the class DefaultTokenExchangeProvider method exchangeClientToSAML2Client.

protected Response exchangeClientToSAML2Client(UserModel targetUser, UserSessionModel targetUserSession, String requestedTokenType, ClientModel targetClient, String audience, String scope) {
    // Create authSession with target SAML 2.0 client and authenticated user
    LoginProtocolFactory factory = (LoginProtocolFactory) session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, SamlProtocol.LOGIN_PROTOCOL);
    SamlService samlService = (SamlService) factory.createProtocolEndpoint(realm, event);
    ResteasyProviderFactory.getInstance().injectProperties(samlService);
    AuthenticationSessionModel authSession = samlService.getOrCreateLoginSessionForIdpInitiatedSso(session, realm, targetClient, null);
    if (authSession == null) {
        logger.error("SAML assertion consumer url not set up");
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_CLIENT, "Client requires assertion consumer url set up", Response.Status.BAD_REQUEST);
    }
    authSession.setAuthenticatedUser(targetUser);
    event.session(targetUserSession);
    AuthenticationManager.setClientScopesInSession(authSession);
    ClientSessionContext clientSessionCtx = TokenManager.attachAuthenticationSession(this.session, targetUserSession, authSession);
    updateUserSessionFromClientAuth(targetUserSession);
    // Create SAML 2.0 Assertion Response
    SamlClient samlClient = new SamlClient(targetClient);
    SamlProtocol samlProtocol = new TokenExchangeSamlProtocol(samlClient).setEventBuilder(event).setHttpHeaders(headers).setRealm(realm).setSession(session).setUriInfo(session.getContext().getUri());
    Response samlAssertion = samlProtocol.authenticated(authSession, targetUserSession, clientSessionCtx);
    if (samlAssertion.getStatus() != 200) {
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Can not get SAML 2.0 token", Response.Status.BAD_REQUEST);
    }
    String xmlString = (String) samlAssertion.getEntity();
    String encodedXML = Base64Url.encode(xmlString.getBytes(GeneralConstants.SAML_CHARSET));
    int assertionLifespan = samlClient.getAssertionLifespan();
    AccessTokenResponse res = new AccessTokenResponse();
    res.setToken(encodedXML);
    res.setTokenType("Bearer");
    res.setExpiresIn(assertionLifespan <= 0 ? realm.getAccessCodeLifespan() : assertionLifespan);
    res.setOtherClaims(OAuth2Constants.ISSUED_TOKEN_TYPE, requestedTokenType);
    event.detail(Details.AUDIENCE, targetClient.getClientId());
    event.success();
    return cors.builder(Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).build();
}
Also used : AuthenticationSessionModel(org.keycloak.sessions.AuthenticationSessionModel) RootAuthenticationSessionModel(org.keycloak.sessions.RootAuthenticationSessionModel) TokenExchangeSamlProtocol(org.keycloak.protocol.oidc.endpoints.TokenEndpoint.TokenExchangeSamlProtocol) SamlProtocol(org.keycloak.protocol.saml.SamlProtocol) TokenExchangeSamlProtocol(org.keycloak.protocol.oidc.endpoints.TokenEndpoint.TokenExchangeSamlProtocol) SamlClient(org.keycloak.protocol.saml.SamlClient) SamlService(org.keycloak.protocol.saml.SamlService) AccessTokenResponse(org.keycloak.representations.AccessTokenResponse) Response(javax.ws.rs.core.Response) LoginProtocolFactory(org.keycloak.protocol.LoginProtocolFactory) ClientSessionContext(org.keycloak.models.ClientSessionContext) CorsErrorResponseException(org.keycloak.services.CorsErrorResponseException) LoginProtocol(org.keycloak.protocol.LoginProtocol) AccessTokenResponse(org.keycloak.representations.AccessTokenResponse)

Example 20 with ClientSessionContext

use of org.keycloak.models.ClientSessionContext in project keycloak by keycloak.

the class DefaultTokenExchangeProvider method exchangeClientToOIDCClient.

protected Response exchangeClientToOIDCClient(UserModel targetUser, UserSessionModel targetUserSession, String requestedTokenType, ClientModel targetClient, String audience, String scope) {
    RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
    AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(targetClient);
    authSession.setAuthenticatedUser(targetUser);
    authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
    authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
    authSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
    event.session(targetUserSession);
    AuthenticationManager.setClientScopesInSession(authSession);
    ClientSessionContext clientSessionCtx = TokenManager.attachAuthenticationSession(this.session, targetUserSession, authSession);
    updateUserSessionFromClientAuth(targetUserSession);
    TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, targetClient, event, this.session, targetUserSession, clientSessionCtx).generateAccessToken();
    responseBuilder.getAccessToken().issuedFor(client.getClientId());
    if (audience != null) {
        responseBuilder.getAccessToken().addAudience(audience);
    }
    if (requestedTokenType.equals(OAuth2Constants.REFRESH_TOKEN_TYPE) && OIDCAdvancedConfigWrapper.fromClientModel(client).isUseRefreshToken()) {
        responseBuilder.generateRefreshToken();
        responseBuilder.getRefreshToken().issuedFor(client.getClientId());
    }
    String scopeParam = clientSessionCtx.getClientSession().getNote(OAuth2Constants.SCOPE);
    if (TokenUtil.isOIDCRequest(scopeParam)) {
        responseBuilder.generateIDToken().generateAccessTokenHash();
    }
    AccessTokenResponse res = responseBuilder.build();
    event.detail(Details.AUDIENCE, targetClient.getClientId());
    event.success();
    return cors.builder(Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).build();
}
Also used : AuthenticationSessionManager(org.keycloak.services.managers.AuthenticationSessionManager) AuthenticationSessionModel(org.keycloak.sessions.AuthenticationSessionModel) RootAuthenticationSessionModel(org.keycloak.sessions.RootAuthenticationSessionModel) ClientSessionContext(org.keycloak.models.ClientSessionContext) RootAuthenticationSessionModel(org.keycloak.sessions.RootAuthenticationSessionModel) AccessTokenResponse(org.keycloak.representations.AccessTokenResponse)

Aggregations

ClientSessionContext (org.keycloak.models.ClientSessionContext)21 UserSessionModel (org.keycloak.models.UserSessionModel)15 DefaultClientSessionContext (org.keycloak.services.util.DefaultClientSessionContext)12 UserModel (org.keycloak.models.UserModel)11 AuthenticationSessionModel (org.keycloak.sessions.AuthenticationSessionModel)11 RootAuthenticationSessionModel (org.keycloak.sessions.RootAuthenticationSessionModel)9 AuthenticatedClientSessionModel (org.keycloak.models.AuthenticatedClientSessionModel)8 ClientModel (org.keycloak.models.ClientModel)8 CorsErrorResponseException (org.keycloak.services.CorsErrorResponseException)8 Response (javax.ws.rs.core.Response)7 AuthenticationSessionManager (org.keycloak.services.managers.AuthenticationSessionManager)7 ClientPolicyException (org.keycloak.services.clientpolicy.ClientPolicyException)6 RealmModel (org.keycloak.models.RealmModel)5 AccessToken (org.keycloak.representations.AccessToken)4 ClientScopeModel (org.keycloak.models.ClientScopeModel)3 KeycloakSession (org.keycloak.models.KeycloakSession)3 TokenManager (org.keycloak.protocol.oidc.TokenManager)3 AccessTokenResponse (org.keycloak.representations.AccessTokenResponse)3 ErrorResponseException (org.keycloak.services.ErrorResponseException)3 HashMap (java.util.HashMap)2