Java Code Examples for org.keycloak.services.managers.AuthenticationManager#backchannelLogout()

The following examples show how to use org.keycloak.services.managers.AuthenticationManager#backchannelLogout() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: SessionResource.java    From keycloak with Apache License 2.0 6 votes vote down vote up
/**
 * Remove sessions
 *
 * @param removeCurrent remove current session (default is false)
 * @return
 */
@DELETE
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response logout(@QueryParam("current") boolean removeCurrent) {
    auth.require(AccountRoles.MANAGE_ACCOUNT);
    List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);

    for (UserSessionModel s : userSessions) {
        if (removeCurrent || !isCurrentSession(s)) {
            AuthenticationManager.backchannelLogout(session, s, true);
        }
    }

    return Cors.add(request, Response.noContent()).auth().allowedOrigins(auth.getToken()).build();
}
 
Example 2
Source File: AuthorizationEndpointBase.java    From keycloak with Apache License 2.0 5 votes vote down vote up
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(userSessionId, realm);
                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;

}
 
Example 3
Source File: RealmAdminResource.java    From keycloak with Apache License 2.0 5 votes vote down vote up
/**
 * Remove a specific user session. Any client that has an admin url will also be told to invalidate this
 * particular session.
 *
 * @param sessionId
 */
@Path("sessions/{session}")
@DELETE
public void deleteSession(@PathParam("session") String sessionId) {
    auth.users().requireManage();

    UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
    if (userSession == null) throw new NotFoundException("Sesssion not found");
    AuthenticationManager.backchannelLogout(session, realm, userSession, session.getContext().getUri(), connection, headers, true);
    adminEvent.operation(OperationType.DELETE).resource(ResourceType.USER_SESSION).resourcePath(session.getContext().getUri()).success();

}
 
Example 4
Source File: UserResource.java    From keycloak with Apache License 2.0 5 votes vote down vote up
/**
 * Remove all user sessions associated with the user
 *
 * Also send notification to all clients that have an admin URL to invalidate the sessions for the particular user.
 *
 */
@Path("logout")
@POST
public void logout() {
    auth.users().requireManage(user);

    session.users().setNotBeforeForUser(realm, user, Time.currentTime());

    List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
    for (UserSessionModel userSession : userSessions) {
        AuthenticationManager.backchannelLogout(session, realm, userSession, session.getContext().getUri(), clientConnection, headers, true);
    }
    adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri()).success();
}
 
Example 5
Source File: SessionResource.java    From keycloak with Apache License 2.0 5 votes vote down vote up
/**
 * Remove a specific session
 *
 * @param id a specific session to remove
 * @return
 */
@Path("/{id}")
@DELETE
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response logout(@PathParam("id") String id) {
    auth.require(AccountRoles.MANAGE_ACCOUNT);
    UserSessionModel userSession = session.sessions().getUserSession(realm, id);
    if (userSession != null && userSession.getUser().equals(user)) {
        AuthenticationManager.backchannelLogout(session, userSession, true);
    }
    return Cors.add(request, Response.noContent()).auth().allowedOrigins(auth.getToken()).build();
}
 
Example 6
Source File: AccountFormService.java    From keycloak with Apache License 2.0 5 votes vote down vote up
@Path("sessions")
@POST
public Response processSessionsLogout(final MultivaluedMap<String, String> formData) {
    if (auth == null) {
        return login("sessions");
    }

    auth.require(AccountRoles.MANAGE_ACCOUNT);
    csrfCheck(formData);

    UserModel user = auth.getUser();

    // Rather decrease time a bit. To avoid situation when user is immediatelly redirected to login screen, then automatically authenticated (eg. with Kerberos) and then seeing issues due the stale token
    // as time on the token will be same like notBefore
    session.users().setNotBeforeForUser(realm, user, Time.currentTime() - 1);

    List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
    for (UserSessionModel userSession : userSessions) {
        AuthenticationManager.backchannelLogout(session, realm, userSession, session.getContext().getUri(), clientConnection, headers, true);
    }

    UriBuilder builder = Urls.accountBase(session.getContext().getUri().getBaseUri()).path(AccountFormService.class, "sessionsPage");
    String referrer = session.getContext().getUri().getQueryParameters().getFirst("referrer");
    if (referrer != null) {
        builder.queryParam("referrer", referrer);

    }
    URI location = builder.build(realm.getName());
    return Response.seeOther(location).build();
}
 
Example 7
Source File: TokenManager.java    From keycloak with Apache License 2.0 4 votes vote down vote up
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.getIssuedAt() + 1 < 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 (!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.getClientScopes())) {
        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);
}
 
Example 8
Source File: LogoutEndpoint.java    From keycloak with Apache License 2.0 4 votes vote down vote up
/**
 * Logout user session.  User must be logged in via a session cookie.
 *
 * When the logout is initiated by a remote idp, the parameter "initiating_idp" can be supplied. This param will
 * prevent upstream logout (since the logout procedure has already been started in the remote idp).
 *
 * @param redirectUri
 * @param initiatingIdp The alias of the idp initiating the logout.
 * @return
 */
@GET
@NoCache
public Response logout(@QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri, // deprecated
                       @QueryParam("id_token_hint") String encodedIdToken,
                       @QueryParam("post_logout_redirect_uri") String postLogoutRedirectUri,
                       @QueryParam("state") String state,
                       @QueryParam("initiating_idp") String initiatingIdp) {
    String redirect = postLogoutRedirectUri != null ? postLogoutRedirectUri : redirectUri;

    if (redirect != null) {
        String validatedUri = RedirectUtils.verifyRealmRedirectUri(session, redirect);
        if (validatedUri == null) {
            event.event(EventType.LOGOUT);
            event.detail(Details.REDIRECT_URI, redirect);
            event.error(Errors.INVALID_REDIRECT_URI);
            return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REDIRECT_URI);
        }
        redirect = validatedUri;
    }

    UserSessionModel userSession = null;
    IDToken idToken = null;
    if (encodedIdToken != null) {
        try {
            idToken = tokenManager.verifyIDTokenSignature(session, encodedIdToken);
            TokenVerifier.createWithoutSignature(idToken).tokenType(TokenUtil.TOKEN_TYPE_ID).verify();
            userSession = session.sessions().getUserSession(realm, idToken.getSessionState());

            if (userSession != null) {
                checkTokenIssuedAt(idToken, userSession);
            }
        } catch (OAuthErrorException | VerificationException e) {
            event.event(EventType.LOGOUT);
            event.error(Errors.INVALID_TOKEN);
            return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.SESSION_NOT_ACTIVE);
        }
    }

    // authenticate identity cookie, but ignore an access token timeout as we're logging out anyways.
    AuthenticationManager.AuthResult authResult = AuthenticationManager.authenticateIdentityCookie(session, realm, false);
    if (authResult != null) {
        userSession = userSession != null ? userSession : authResult.getSession();
        return initiateBrowserLogout(userSession, redirect, state, initiatingIdp);
    }
    else if (userSession != null) {
        // identity cookie is missing but there's valid id_token_hint which matches session cookie => continue with browser logout
        if (idToken != null && idToken.getSessionState().equals(AuthenticationManager.getSessionIdFromSessionCookie(session))) {
            return initiateBrowserLogout(userSession, redirect, state, initiatingIdp);
        }
        // non browser logout
        event.event(EventType.LOGOUT);
        AuthenticationManager.backchannelLogout(session, realm, userSession, session.getContext().getUri(), clientConnection, headers, true);
        event.user(userSession.getUser()).session(userSession).success();
    }

    if (redirect != null) {
        UriBuilder uriBuilder = UriBuilder.fromUri(redirect);
        if (state != null) uriBuilder.queryParam(OIDCLoginProtocol.STATE_PARAM, state);
        return Response.status(302).location(uriBuilder.build()).build();
    } else {
        // TODO Empty content with ok makes no sense. Should it display a page? Or use noContent?
        session.getProvider(SecurityHeadersProvider.class).options().allowEmptyContentType();
        return Response.ok().build();
    }
}
 
Example 9
Source File: LogoutEndpoint.java    From keycloak with Apache License 2.0 4 votes vote down vote up
private void logout(UserSessionModel userSession, boolean offline) {
    AuthenticationManager.backchannelLogout(session, realm, userSession, session.getContext().getUri(), clientConnection, headers, true, offline);
    event.user(userSession.getUser()).session(userSession).success();
}
 
Example 10
Source File: AccountFormService.java    From keycloak with Apache License 2.0 4 votes vote down vote up
/**
 * Update account password
 * <p>
 * Form params:
 * <p>
 * password - old password
 * password-new
 * pasword-confirm
 *
 * @param formData
 * @return
 */
@Path("password")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response processPasswordUpdate(final MultivaluedMap<String, String> formData) {
    if (auth == null) {
        return login("password");
    }

    auth.require(AccountRoles.MANAGE_ACCOUNT);

    csrfCheck(formData);
    UserModel user = auth.getUser();

    boolean requireCurrent = isPasswordSet(session, realm, user);
    account.setPasswordSet(requireCurrent);

    String password = formData.getFirst("password");
    String passwordNew = formData.getFirst("password-new");
    String passwordConfirm = formData.getFirst("password-confirm");

    EventBuilder errorEvent = event.clone().event(EventType.UPDATE_PASSWORD_ERROR)
            .client(auth.getClient())
            .user(auth.getSession().getUser());

    if (requireCurrent) {
        if (Validation.isBlank(password)) {
            setReferrerOnPage();
            errorEvent.error(Errors.PASSWORD_MISSING);
            return account.setError(Status.OK, Messages.MISSING_PASSWORD).createResponse(AccountPages.PASSWORD);
        }

        UserCredentialModel cred = UserCredentialModel.password(password);
        if (!session.userCredentialManager().isValid(realm, user, cred)) {
            setReferrerOnPage();
            errorEvent.error(Errors.INVALID_USER_CREDENTIALS);
            return account.setError(Status.OK, Messages.INVALID_PASSWORD_EXISTING).createResponse(AccountPages.PASSWORD);
        }
    }

    if (Validation.isBlank(passwordNew)) {
        setReferrerOnPage();
        errorEvent.error(Errors.PASSWORD_MISSING);
        return account.setError(Status.OK, Messages.MISSING_PASSWORD).createResponse(AccountPages.PASSWORD);
    }

    if (!passwordNew.equals(passwordConfirm)) {
        setReferrerOnPage();
        errorEvent.error(Errors.PASSWORD_CONFIRM_ERROR);
        return account.setError(Status.OK, Messages.INVALID_PASSWORD_CONFIRM).createResponse(AccountPages.PASSWORD);
    }

    try {
        session.userCredentialManager().updateCredential(realm, user, UserCredentialModel.password(passwordNew, false));
    } catch (ReadOnlyException mre) {
        setReferrerOnPage();
        errorEvent.error(Errors.NOT_ALLOWED);
        return account.setError(Response.Status.BAD_REQUEST, Messages.READ_ONLY_PASSWORD).createResponse(AccountPages.PASSWORD);
    } catch (ModelException me) {
        ServicesLogger.LOGGER.failedToUpdatePassword(me);
        setReferrerOnPage();
        errorEvent.detail(Details.REASON, me.getMessage()).error(Errors.PASSWORD_REJECTED);
        return account.setError(Response.Status.NOT_ACCEPTABLE, me.getMessage(), me.getParameters()).createResponse(AccountPages.PASSWORD);
    } catch (Exception ape) {
        ServicesLogger.LOGGER.failedToUpdatePassword(ape);
        setReferrerOnPage();
        errorEvent.detail(Details.REASON, ape.getMessage()).error(Errors.PASSWORD_REJECTED);
        return account.setError(Response.Status.INTERNAL_SERVER_ERROR, ape.getMessage()).createResponse(AccountPages.PASSWORD);
    }

    List<UserSessionModel> sessions = session.sessions().getUserSessions(realm, user);
    for (UserSessionModel s : sessions) {
        if (!s.getId().equals(auth.getSession().getId())) {
            AuthenticationManager.backchannelLogout(session, realm, s, session.getContext().getUri(), clientConnection, headers, true);
        }
    }

    event.event(EventType.UPDATE_PASSWORD).client(auth.getClient()).user(auth.getUser()).success();

    setReferrerOnPage();
    return account.setPasswordSet(true).setSuccess(Messages.ACCOUNT_PASSWORD_UPDATED).createResponse(AccountPages.PASSWORD);
}