use of org.keycloak.protocol.oidc.BackchannelLogoutResponse in project keycloak by keycloak.
the class AuthenticationManager method backchannelLogout.
/**
* @param session
* @param realm
* @param userSession
* @param uriInfo
* @param connection
* @param headers
* @param logoutBroker
* @param offlineSession
*
* @return BackchannelLogoutResponse with logout information
*/
public static BackchannelLogoutResponse backchannelLogout(KeycloakSession session, RealmModel realm, UserSessionModel userSession, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers, boolean logoutBroker, boolean offlineSession) {
BackchannelLogoutResponse backchannelLogoutResponse = new BackchannelLogoutResponse();
if (userSession == null) {
backchannelLogoutResponse.setLocalLogoutSucceeded(true);
return backchannelLogoutResponse;
}
UserModel user = userSession.getUser();
if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {
userSession.setState(UserSessionModel.State.LOGGING_OUT);
}
logger.debugv("Logging out: {0} ({1}) offline: {2}", user.getUsername(), userSession.getId(), userSession.isOffline());
boolean expireUserSessionCookieSucceeded = expireUserSessionCookie(session, userSession, realm, uriInfo, headers, connection);
final AuthenticationSessionManager asm = new AuthenticationSessionManager(session);
AuthenticationSessionModel logoutAuthSession = createOrJoinLogoutSession(session, realm, asm, userSession, false);
boolean userSessionOnlyHasLoggedOutClients = false;
try {
backchannelLogoutResponse = backchannelLogoutAll(session, realm, userSession, logoutAuthSession, uriInfo, headers, logoutBroker);
userSessionOnlyHasLoggedOutClients = checkUserSessionOnlyHasLoggedOutClients(realm, userSession, logoutAuthSession);
} finally {
RootAuthenticationSessionModel rootAuthSession = logoutAuthSession.getParentSession();
rootAuthSession.removeAuthenticationSessionByTabId(logoutAuthSession.getTabId());
}
userSession.setState(UserSessionModel.State.LOGGED_OUT);
if (offlineSession) {
new UserSessionManager(session).revokeOfflineUserSession(userSession);
// Check if "online" session still exists and remove it too
String onlineUserSessionId = userSession.getNote(CORRESPONDING_SESSION_ID);
UserSessionModel onlineUserSession = (onlineUserSessionId != null) ? session.sessions().getUserSession(realm, onlineUserSessionId) : session.sessions().getUserSession(realm, userSession.getId());
if (onlineUserSession != null) {
session.sessions().removeUserSession(realm, onlineUserSession);
}
} else {
session.sessions().removeUserSession(realm, userSession);
}
backchannelLogoutResponse.setLocalLogoutSucceeded(expireUserSessionCookieSucceeded && userSessionOnlyHasLoggedOutClients);
return backchannelLogoutResponse;
}
use of org.keycloak.protocol.oidc.BackchannelLogoutResponse in project keycloak by keycloak.
the class LogoutEndpoint method backchannelLogout.
/**
* Backchannel logout endpoint implementation for Keycloak, which tries to logout the user from all sessions via
* POST with a valid LogoutToken.
*
* Logout a session via a non-browser invocation. Will be implemented as a backchannel logout based on the
* specification
* https://openid.net/specs/openid-connect-backchannel-1_0.html
*
* @return
*/
@Path("/backchannel-logout")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response backchannelLogout() {
MultivaluedMap<String, String> form = request.getDecodedFormParameters();
event.event(EventType.LOGOUT);
String encodedLogoutToken = form.getFirst(OAuth2Constants.LOGOUT_TOKEN);
if (encodedLogoutToken == null) {
event.error(Errors.INVALID_TOKEN);
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "No logout token", Response.Status.BAD_REQUEST);
}
LogoutTokenValidationCode validationCode = tokenManager.verifyLogoutToken(session, realm, encodedLogoutToken);
if (!validationCode.equals(LogoutTokenValidationCode.VALIDATION_SUCCESS)) {
event.error(Errors.INVALID_TOKEN);
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, validationCode.getErrorMessage(), Response.Status.BAD_REQUEST);
}
LogoutToken logoutToken = tokenManager.toLogoutToken(encodedLogoutToken).get();
Stream<String> identityProviderAliases = tokenManager.getValidOIDCIdentityProvidersForBackchannelLogout(realm, session, encodedLogoutToken, logoutToken).map(idp -> idp.getConfig().getAlias());
boolean logoutOfflineSessions = Boolean.parseBoolean(logoutToken.getEvents().getOrDefault(TokenUtil.TOKEN_BACKCHANNEL_LOGOUT_EVENT_REVOKE_OFFLINE_TOKENS, false).toString());
BackchannelLogoutResponse backchannelLogoutResponse;
if (logoutToken.getSid() != null) {
backchannelLogoutResponse = backchannelLogoutWithSessionId(logoutToken.getSid(), identityProviderAliases, logoutOfflineSessions, logoutToken.getSubject());
} else {
backchannelLogoutResponse = backchannelLogoutFederatedUserId(logoutToken.getSubject(), identityProviderAliases, logoutOfflineSessions);
}
if (!backchannelLogoutResponse.getLocalLogoutSucceeded()) {
event.error(Errors.LOGOUT_FAILED);
throw new ErrorResponseException(OAuthErrorException.SERVER_ERROR, "There was an error in the local logout", Response.Status.NOT_IMPLEMENTED);
}
session.getProvider(SecurityHeadersProvider.class).options().allowEmptyContentType();
if (oneOrMoreDownstreamLogoutsFailed(backchannelLogoutResponse)) {
return Cors.add(request).auth().builder(Response.status(Response.Status.GATEWAY_TIMEOUT).type(MediaType.APPLICATION_JSON_TYPE)).build();
}
return Cors.add(request).auth().builder(Response.ok().type(MediaType.APPLICATION_JSON_TYPE)).build();
}
use of org.keycloak.protocol.oidc.BackchannelLogoutResponse in project keycloak by keycloak.
the class AuthenticationManager method backchannelLogoutAll.
private static BackchannelLogoutResponse backchannelLogoutAll(KeycloakSession session, RealmModel realm, UserSessionModel userSession, AuthenticationSessionModel logoutAuthSession, UriInfo uriInfo, HttpHeaders headers, boolean logoutBroker) {
BackchannelLogoutResponse backchannelLogoutResponse = new BackchannelLogoutResponse();
for (AuthenticatedClientSessionModel clientSession : userSession.getAuthenticatedClientSessions().values()) {
Response clientSessionLogoutResponse = backchannelLogoutClientSession(session, realm, clientSession, logoutAuthSession, uriInfo, headers);
String backchannelLogoutUrl = OIDCAdvancedConfigWrapper.fromClientModel(clientSession.getClient()).getBackchannelLogoutUrl();
BackchannelLogoutResponse.DownStreamBackchannelLogoutResponse downStreamBackchannelLogoutResponse = new BackchannelLogoutResponse.DownStreamBackchannelLogoutResponse();
downStreamBackchannelLogoutResponse.setWithBackchannelLogoutUrl(backchannelLogoutUrl != null);
if (clientSessionLogoutResponse != null) {
downStreamBackchannelLogoutResponse.setResponseCode(clientSessionLogoutResponse.getStatus());
} else {
downStreamBackchannelLogoutResponse.setResponseCode(null);
}
backchannelLogoutResponse.addClientResponses(downStreamBackchannelLogoutResponse);
}
if (logoutBroker) {
String brokerId = userSession.getNote(Details.IDENTITY_PROVIDER);
if (brokerId != null) {
IdentityProvider identityProvider = IdentityBrokerService.getIdentityProvider(session, realm, brokerId);
try {
identityProvider.backchannelLogout(session, userSession, uriInfo, realm);
} catch (Exception e) {
logger.warn("Exception at broker backchannel logout for broker " + brokerId, e);
backchannelLogoutResponse.setLocalLogoutSucceeded(false);
}
}
}
return backchannelLogoutResponse;
}
use of org.keycloak.protocol.oidc.BackchannelLogoutResponse in project keycloak by keycloak.
the class LogoutEndpoint method backchannelLogoutWithSessionId.
private BackchannelLogoutResponse backchannelLogoutWithSessionId(String sessionId, Stream<String> identityProviderAliases, boolean logoutOfflineSessions, String federatedUserId) {
AtomicReference<BackchannelLogoutResponse> backchannelLogoutResponse = new AtomicReference<>(new BackchannelLogoutResponse());
backchannelLogoutResponse.get().setLocalLogoutSucceeded(true);
identityProviderAliases.forEach(identityProviderAlias -> {
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, identityProviderAlias + "." + sessionId);
if (logoutOfflineSessions) {
if (offlineSessionsLazyLoadingEnabled) {
logoutOfflineUserSessionByBrokerUserId(identityProviderAlias + "." + federatedUserId, identityProviderAlias + "." + sessionId);
} else {
logoutOfflineUserSession(identityProviderAlias + "." + sessionId);
}
}
if (userSession != null) {
backchannelLogoutResponse.set(logoutUserSession(userSession));
}
});
return backchannelLogoutResponse.get();
}
use of org.keycloak.protocol.oidc.BackchannelLogoutResponse in project keycloak by keycloak.
the class LogoutEndpoint method backchannelLogoutFederatedUserId.
private BackchannelLogoutResponse backchannelLogoutFederatedUserId(String federatedUserId, Stream<String> identityProviderAliases, boolean logoutOfflineSessions) {
BackchannelLogoutResponse backchannelLogoutResponse = new BackchannelLogoutResponse();
backchannelLogoutResponse.setLocalLogoutSucceeded(true);
identityProviderAliases.forEach(identityProviderAlias -> {
if (logoutOfflineSessions) {
logoutOfflineUserSessions(identityProviderAlias + "." + federatedUserId);
}
session.sessions().getUserSessionByBrokerUserIdStream(realm, identityProviderAlias + "." + federatedUserId).collect(// collect to avoid concurrent modification as backchannelLogout removes the user sessions.
Collectors.toList()).forEach(userSession -> {
BackchannelLogoutResponse userBackchannelLogoutResponse = this.logoutUserSession(userSession);
backchannelLogoutResponse.setLocalLogoutSucceeded(backchannelLogoutResponse.getLocalLogoutSucceeded() && userBackchannelLogoutResponse.getLocalLogoutSucceeded());
userBackchannelLogoutResponse.getClientResponses().forEach(backchannelLogoutResponse::addClientResponses);
});
});
return backchannelLogoutResponse;
}
Aggregations