Search in sources :

Example 1 with UserSessionCrossDCManager

use of org.keycloak.services.managers.UserSessionCrossDCManager in project keycloak by keycloak.

the class DeviceGrantType method oauth2DeviceFlow.

public Response oauth2DeviceFlow() {
    if (!realm.getOAuth2DeviceConfig().isOAuth2DeviceAuthorizationGrantEnabled(client)) {
        event.error(Errors.NOT_ALLOWED);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Client not allowed OAuth 2.0 Device Authorization Grant", Response.Status.BAD_REQUEST);
    }
    String deviceCode = formParams.getFirst(OAuth2Constants.DEVICE_CODE);
    if (deviceCode == null) {
        event.error(Errors.INVALID_OAUTH2_DEVICE_CODE);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST, "Missing parameter: " + OAuth2Constants.DEVICE_CODE, Response.Status.BAD_REQUEST);
    }
    OAuth2DeviceTokenStoreProvider store = session.getProvider(OAuth2DeviceTokenStoreProvider.class);
    OAuth2DeviceCodeModel deviceCodeModel = store.getByDeviceCode(realm, deviceCode);
    if (deviceCodeModel == null) {
        event.error(Errors.INVALID_OAUTH2_DEVICE_CODE);
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, "Device code not valid", Response.Status.BAD_REQUEST);
    }
    if (deviceCodeModel.isExpired()) {
        event.error(Errors.EXPIRED_OAUTH2_DEVICE_CODE);
        throw new CorsErrorResponseException(cors, OAuthErrorException.EXPIRED_TOKEN, "Device code is expired", Response.Status.BAD_REQUEST);
    }
    if (!store.isPollingAllowed(deviceCodeModel)) {
        event.error(Errors.SLOW_DOWN);
        throw new CorsErrorResponseException(cors, OAuthErrorException.SLOW_DOWN, "Slow down", Response.Status.BAD_REQUEST);
    }
    if (deviceCodeModel.isDenied()) {
        event.error(Errors.ACCESS_DENIED);
        throw new CorsErrorResponseException(cors, OAuthErrorException.ACCESS_DENIED, "The end user denied the authorization request", Response.Status.BAD_REQUEST);
    }
    if (deviceCodeModel.isPending()) {
        throw new CorsErrorResponseException(cors, OAuthErrorException.AUTHORIZATION_PENDING, "The authorization request is still pending", Response.Status.BAD_REQUEST);
    }
    // https://tools.ietf.org/html/rfc7636#section-4.6
    String codeVerifier = formParams.getFirst(OAuth2Constants.CODE_VERIFIER);
    String codeChallenge = deviceCodeModel.getCodeChallenge();
    String codeChallengeMethod = deviceCodeModel.getCodeChallengeMethod();
    if (codeChallengeMethod != null && !codeChallengeMethod.isEmpty()) {
        PkceUtils.checkParamsForPkceEnforcedClient(codeVerifier, codeChallenge, codeChallengeMethod, null, null, event, cors);
    } else {
        // PKCE Activation is OFF, execute the codes implemented in KEYCLOAK-2604
        PkceUtils.checkParamsForPkceNotEnforcedClient(codeVerifier, codeChallenge, codeChallengeMethod, null, null, event, cors);
    }
    // Approved
    String userSessionId = deviceCodeModel.getUserSessionId();
    event.detail(Details.CODE_ID, userSessionId);
    event.session(userSessionId);
    // Retrieve UserSession
    UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, userSessionId, client.getId());
    if (userSession == null) {
        userSession = session.sessions().getUserSession(realm, userSessionId);
        if (userSession == null) {
            throw new CorsErrorResponseException(cors, OAuthErrorException.AUTHORIZATION_PENDING, "The authorization request is verified but can not lookup the user session yet", Response.Status.BAD_REQUEST);
        }
    }
    // Now, remove the device code
    store.removeDeviceCode(realm, deviceCode);
    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);
    }
    AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
    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 (!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);
    }
    try {
        session.clientPolicy().triggerOnEvent(new DeviceTokenRequestContext(deviceCodeModel, formParams));
    } catch (ClientPolicyException cpe) {
        event.error(cpe.getError());
        throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_GRANT, cpe.getErrorDetail(), Response.Status.BAD_REQUEST);
    }
    // Compute client scopes again from scope parameter. Check if user still has them granted
    // (but in device_code-to-token request, it could just theoretically happen that they are not available)
    String scopeParam = deviceCodeModel.getScope();
    if (!TokenManager.verifyConsentStillAvailable(session, user, client, TokenManager.getRequestedClientScopes(scopeParam, client))) {
        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, deviceCodeModel.getNonce());
    return tokenEndpoint.createTokenResponse(user, userSession, clientSessionCtx, scopeParam, false);
}
Also used : OAuth2DeviceTokenStoreProvider(org.keycloak.models.OAuth2DeviceTokenStoreProvider) UserModel(org.keycloak.models.UserModel) UserSessionModel(org.keycloak.models.UserSessionModel) DeviceTokenRequestContext(org.keycloak.protocol.oidc.grants.device.clientpolicy.context.DeviceTokenRequestContext) DefaultClientSessionContext(org.keycloak.services.util.DefaultClientSessionContext) ClientSessionContext(org.keycloak.models.ClientSessionContext) OAuth2DeviceCodeModel(org.keycloak.models.OAuth2DeviceCodeModel) AuthenticatedClientSessionModel(org.keycloak.models.AuthenticatedClientSessionModel) CorsErrorResponseException(org.keycloak.services.CorsErrorResponseException) UserSessionCrossDCManager(org.keycloak.services.managers.UserSessionCrossDCManager) ClientPolicyException(org.keycloak.services.clientpolicy.ClientPolicyException)

Example 2 with UserSessionCrossDCManager

use of org.keycloak.services.managers.UserSessionCrossDCManager in project keycloak by keycloak.

the class OAuth2CodeParser method parseCode.

/**
 * Will parse the code and retrieve the corresponding OAuth2Code and AuthenticatedClientSessionModel. Will also check if code wasn't already
 * used and if it wasn't expired. If it was already used (or other error happened during parsing), then returned parser will have "isIllegalHash"
 * set to true. If it was expired, the parser will have "isExpired" set to true
 *
 * @param session
 * @param code
 * @param realm
 * @param event
 * @return
 */
public static ParseResult parseCode(KeycloakSession session, String code, RealmModel realm, EventBuilder event) {
    ParseResult result = new ParseResult(code);
    String[] parsed = DOT.split(code, 3);
    if (parsed.length < 3) {
        logger.warn("Invalid format of the code");
        return result.illegalCode();
    }
    String userSessionId = parsed[1];
    String clientUUID = parsed[2];
    event.detail(Details.CODE_ID, userSessionId);
    event.session(userSessionId);
    // Parse UUID
    UUID codeUUID;
    try {
        codeUUID = UUID.fromString(parsed[0]);
    } catch (IllegalArgumentException re) {
        logger.warn("Invalid format of the UUID in the code");
        return result.illegalCode();
    }
    // Retrieve UserSession
    UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, userSessionId, clientUUID);
    if (userSession == null) {
        // Needed to track if code is invalid or was already used.
        userSession = session.sessions().getUserSession(realm, userSessionId);
        if (userSession == null) {
            return result.illegalCode();
        }
    }
    result.clientSession = userSession.getAuthenticatedClientSessionByClient(clientUUID);
    CodeToTokenStoreProvider codeStore = session.getProvider(CodeToTokenStoreProvider.class);
    Map<String, String> codeData = codeStore.remove(codeUUID);
    // Either code not available or was already used
    if (codeData == null) {
        logger.warnf("Code '%s' already used for userSession '%s' and client '%s'.", codeUUID, userSessionId, clientUUID);
        return result.illegalCode();
    }
    logger.tracef("Successfully verified code '%s'. User session: '%s', client: '%s'", codeUUID, userSessionId, clientUUID);
    result.codeData = OAuth2Code.deserializeCode(codeData);
    // Finally doublecheck if code is not expired
    int currentTime = Time.currentTime();
    if (currentTime > result.codeData.getExpiration()) {
        return result.expiredCode();
    }
    return result;
}
Also used : UserSessionModel(org.keycloak.models.UserSessionModel) CodeToTokenStoreProvider(org.keycloak.models.CodeToTokenStoreProvider) UUID(java.util.UUID) UserSessionCrossDCManager(org.keycloak.services.managers.UserSessionCrossDCManager)

Example 3 with UserSessionCrossDCManager

use of org.keycloak.services.managers.UserSessionCrossDCManager in project keycloak by keycloak.

the class SamlSessionUtils method getClientSession.

public static AuthenticatedClientSessionModel getClientSession(KeycloakSession session, RealmModel realm, String sessionIndex) {
    if (sessionIndex == null) {
        return null;
    }
    String[] parts = PATTERN.split(sessionIndex);
    if (parts.length != 2) {
        return null;
    }
    String userSessionId = parts[0];
    String clientUUID = parts[1];
    UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, userSessionId, false, clientUUID);
    if (userSession == null) {
        return null;
    }
    return userSession.getAuthenticatedClientSessionByClient(clientUUID);
}
Also used : UserSessionModel(org.keycloak.models.UserSessionModel) UserSessionCrossDCManager(org.keycloak.services.managers.UserSessionCrossDCManager)

Example 4 with UserSessionCrossDCManager

use of org.keycloak.services.managers.UserSessionCrossDCManager in project keycloak by keycloak.

the class TokenManager method validateToken.

public TokenValidation validateToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, RefreshToken oldToken, HttpHeaders headers) throws OAuthErrorException {
    UserSessionModel userSession = null;
    boolean offline = TokenUtil.TOKEN_TYPE_OFFLINE.equals(oldToken.getType());
    if (offline) {
        UserSessionManager sessionManager = new UserSessionManager(session);
        userSession = sessionManager.findOfflineUserSession(realm, oldToken.getSessionState());
        if (userSession != null) {
            // Revoke timeouted offline userSession
            if (!AuthenticationManager.isOfflineSessionValid(realm, userSession)) {
                sessionManager.revokeOfflineUserSession(userSession);
                throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Offline session not active", "Offline session not active");
            }
        } else {
            throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Offline user session not found", "Offline user session not found");
        }
    } else {
        // Find userSession regularly for online tokens
        userSession = session.sessions().getUserSession(realm, oldToken.getSessionState());
        if (!AuthenticationManager.isSessionValid(realm, userSession)) {
            AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, connection, headers, true);
            throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Session not active", "Session not active");
        }
    }
    UserModel user = userSession.getUser();
    if (user == null) {
        throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", "Unknown user");
    }
    if (!user.isEnabled()) {
        throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "User disabled", "User disabled");
    }
    if (oldToken.isIssuedBeforeSessionStart(userSession.getStarted())) {
        logger.debug("Refresh toked issued before the user session started");
        throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Refresh toked issued before the user session started");
    }
    ClientModel client = session.getContext().getClient();
    AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
    // Can theoretically happen in cross-dc environment. Try to see if userSession with our client is available in remoteCache
    if (clientSession == null) {
        userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, userSession.getId(), offline, client.getId());
        if (userSession != null) {
            clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
        } else {
            throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Session doesn't have required client", "Session doesn't have required client");
        }
    }
    if (oldToken.isIssuedBeforeSessionStart(clientSession.getStarted())) {
        logger.debug("Refresh toked issued before the client session started");
        throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Refresh toked issued before the client session started");
    }
    if (!client.getClientId().equals(oldToken.getIssuedFor())) {
        throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Unmatching clients", "Unmatching clients");
    }
    try {
        TokenVerifier.createWithoutSignature(oldToken).withChecks(NotBeforeCheck.forModel(client), NotBeforeCheck.forModel(session, realm, user)).verify();
    } catch (VerificationException e) {
        throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale token");
    }
    // Setup clientScopes from refresh token to the context
    String oldTokenScope = oldToken.getScope();
    // Case when offline token is migrated from previous version
    if (oldTokenScope == null && userSession.isOffline()) {
        logger.debugf("Migrating offline token of user '%s' for client '%s' of realm '%s'", user.getUsername(), client.getClientId(), realm.getName());
        MigrationUtils.migrateOldOfflineToken(session, realm, client, user);
        oldTokenScope = OAuth2Constants.OFFLINE_ACCESS;
    }
    ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndScopeParameter(clientSession, oldTokenScope, session);
    // Check user didn't revoke granted consent
    if (!verifyConsentStillAvailable(session, user, client, clientSessionCtx.getClientScopesStream())) {
        throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Client no longer has requested consent from user");
    }
    clientSessionCtx.setAttribute(OIDCLoginProtocol.NONCE_PARAM, oldToken.getNonce());
    // recreate token.
    AccessToken newToken = createClientAccessToken(session, realm, client, user, userSession, clientSessionCtx);
    return new TokenValidation(user, userSession, clientSessionCtx, newToken);
}
Also used : UserSessionModel(org.keycloak.models.UserSessionModel) OAuthErrorException(org.keycloak.OAuthErrorException) AuthenticatedClientSessionModel(org.keycloak.models.AuthenticatedClientSessionModel) UserSessionCrossDCManager(org.keycloak.services.managers.UserSessionCrossDCManager) UserSessionManager(org.keycloak.services.managers.UserSessionManager) UserModel(org.keycloak.models.UserModel) ClientModel(org.keycloak.models.ClientModel) DefaultClientSessionContext(org.keycloak.services.util.DefaultClientSessionContext) ClientSessionContext(org.keycloak.models.ClientSessionContext) AccessToken(org.keycloak.representations.AccessToken) VerificationException(org.keycloak.common.VerificationException)

Example 5 with UserSessionCrossDCManager

use of org.keycloak.services.managers.UserSessionCrossDCManager in project keycloak by keycloak.

the class AuthorizationEndpointBase method createAuthenticationSession.

protected AuthenticationSessionModel createAuthenticationSession(ClientModel client, String requestState) {
    AuthenticationSessionManager manager = new AuthenticationSessionManager(session);
    RootAuthenticationSessionModel rootAuthSession = manager.getCurrentRootAuthenticationSession(realm);
    AuthenticationSessionModel authSession;
    if (rootAuthSession != null) {
        authSession = rootAuthSession.createAuthenticationSession(client);
        logger.debugf("Sent request to authz endpoint. Root authentication session with ID '%s' exists. Client is '%s' . Created new authentication session with tab ID: %s", rootAuthSession.getId(), client.getClientId(), authSession.getTabId());
    } else {
        UserSessionCrossDCManager userSessionCrossDCManager = new UserSessionCrossDCManager(session);
        UserSessionModel userSession = userSessionCrossDCManager.getUserSessionIfExistsRemotely(manager, realm);
        if (userSession != null) {
            UserModel user = userSession.getUser();
            if (user != null && !user.isEnabled()) {
                authSession = createNewAuthenticationSession(manager, client);
                AuthenticationManager.backchannelLogout(session, userSession, true);
            } else {
                String userSessionId = userSession.getId();
                rootAuthSession = session.authenticationSessions().createRootAuthenticationSession(realm, userSessionId);
                authSession = rootAuthSession.createAuthenticationSession(client);
                logger.debugf("Sent request to authz endpoint. We don't have root authentication session with ID '%s' but we have userSession." + "Re-created root authentication session with same ID. Client is: %s . New authentication session tab ID: %s", userSessionId, client.getClientId(), authSession.getTabId());
            }
        } else {
            authSession = createNewAuthenticationSession(manager, client);
        }
    }
    session.getProvider(LoginFormsProvider.class).setAuthenticationSession(authSession);
    return authSession;
}
Also used : AuthenticationSessionManager(org.keycloak.services.managers.AuthenticationSessionManager) UserModel(org.keycloak.models.UserModel) AuthenticationSessionModel(org.keycloak.sessions.AuthenticationSessionModel) RootAuthenticationSessionModel(org.keycloak.sessions.RootAuthenticationSessionModel) UserSessionModel(org.keycloak.models.UserSessionModel) LoginFormsProvider(org.keycloak.forms.login.LoginFormsProvider) RootAuthenticationSessionModel(org.keycloak.sessions.RootAuthenticationSessionModel) UserSessionCrossDCManager(org.keycloak.services.managers.UserSessionCrossDCManager)

Aggregations

UserSessionModel (org.keycloak.models.UserSessionModel)8 UserSessionCrossDCManager (org.keycloak.services.managers.UserSessionCrossDCManager)8 UserModel (org.keycloak.models.UserModel)4 AuthenticatedClientSessionModel (org.keycloak.models.AuthenticatedClientSessionModel)3 VerificationException (org.keycloak.common.VerificationException)2 ClientModel (org.keycloak.models.ClientModel)2 ClientSessionContext (org.keycloak.models.ClientSessionContext)2 CorsErrorResponseException (org.keycloak.services.CorsErrorResponseException)2 DefaultClientSessionContext (org.keycloak.services.util.DefaultClientSessionContext)2 UUID (java.util.UUID)1 OAuthErrorException (org.keycloak.OAuthErrorException)1 LoginFormsProvider (org.keycloak.forms.login.LoginFormsProvider)1 CodeToTokenStoreProvider (org.keycloak.models.CodeToTokenStoreProvider)1 OAuth2DeviceCodeModel (org.keycloak.models.OAuth2DeviceCodeModel)1 OAuth2DeviceTokenStoreProvider (org.keycloak.models.OAuth2DeviceTokenStoreProvider)1 DeviceTokenRequestContext (org.keycloak.protocol.oidc.grants.device.clientpolicy.context.DeviceTokenRequestContext)1 AccessToken (org.keycloak.representations.AccessToken)1 ClientPolicyException (org.keycloak.services.clientpolicy.ClientPolicyException)1 AuthenticationSessionManager (org.keycloak.services.managers.AuthenticationSessionManager)1 UserSessionManager (org.keycloak.services.managers.UserSessionManager)1