Skip to content

Commit

Permalink
working code for rpinitiated logout. contains tmp debug stmts
Browse files Browse the repository at this point in the history
  • Loading branch information
rsearls committed Nov 21, 2024
1 parent ad376b0 commit 3affa81
Show file tree
Hide file tree
Showing 15 changed files with 346 additions and 206 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2021 Red Hat, Inc., and individual contributors
* Copyright 2024 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -37,6 +37,7 @@
*/
public class AuthenticatedActionsHandler {

private static LogoutHandler logoutHandler = new LogoutHandler();
private OidcClientConfiguration deployment;
private OidcHttpFacade facade;

Expand All @@ -54,6 +55,10 @@ public boolean handledRequest() {
return true;
}

if (logoutHandler.tryLogout(facade)) {
return true;
}

return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ public enum Reason {
INVALID_TOKEN,
STALE_TOKEN,
NO_AUTHORIZATION_HEADER,
NO_QUERY_PARAMETER_ACCESS_TOKEN
NO_QUERY_PARAMETER_ACCESS_TOKEN,
NO_SESSION_ID,

This comment has been minimized.

Copy link
@fjuma

fjuma Nov 28, 2024

Are NO_SESSION_ID and METHOD_NOT_ALLOWED used? I don't seem to see them getting used.

This comment has been minimized.

Copy link
@rsearls

rsearls Nov 30, 2024

Author Owner

There is no enum variable NO_SESSION_ID or METHOD_NOT_ALLOWED in my
AuthenticationError. Nor do I find these in elytron's 2.x branch.
I have verified my 2.x branch is upto date with upstream/2.x.
I do not find them in your ELY-2534 branch or Pedro's branch either.

METHOD_NOT_ALLOWED
}

private Reason reason;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,5 +278,12 @@ interface ElytronMessages extends BasicLogger {

@Message(id = 23070, value = "Authentication request format must be one of the following: oauth2, request, request_uri.")
RuntimeException invalidAuthenticationRequestFormat();

@Message(id = 23071, value = "%s is not a valid value for %s")
RuntimeException invalidLogoutPath(String pathValue, String pathName);

@Message(id = 23072, value = "The end substring of %s: %s can not be identical to %s: %s")
RuntimeException invalidLogoutCallbackPath(String callbackPathTitle, String callbacPathkValue,
String logoutPathTitle, String logoutPathValue);
}

Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@
*/
final class LogoutHandler {

private static final String POST_LOGOUT_REDIRECT_URI_PARAM = "post_logout_redirect_uri";
private static final String ID_TOKEN_HINT_PARAM = "id_token_hint";
public static final String POST_LOGOUT_REDIRECT_URI_PARAM = "post_logout_redirect_uri";
public static final String ID_TOKEN_HINT_PARAM = "id_token_hint";
private static final String LOGOUT_TOKEN_PARAM = "logout_token";
private static final String LOGOUT_TOKEN_TYPE = "Logout";
private static final String SID = "sid";
private static final String ISS = "iss";
public static final String SID = "sid";
public static final String ISS = "iss";

/**
* A bounded map to store sessions marked for invalidation after receiving logout requests through the back-channel
Expand All @@ -60,12 +60,10 @@ protected boolean removeEldestEntry(Map.Entry<String, OidcClientConfiguration> e
});

boolean tryLogout(OidcHttpFacade facade) {
log.trace("## LogoutHandler.tryLogout ENTERED");
RefreshableOidcSecurityContext securityContext = getSecurityContext(facade);

if (securityContext == null) {
// no active session
log.trace("## LogoutHandler.tryLogout no active session");
return false;
}

Expand All @@ -76,48 +74,19 @@ boolean tryLogout(OidcHttpFacade facade) {
return true;
}

if (isRpInitiatedLogoutUri(facade)) {
log.trace("## LogoutHandler.tryLogout isRpInitiatedLogoutUri");
if (isRpInitiatedLogoutPath(facade)) {
redirectEndSessionEndpoint(facade);
return true;
}

if (isLogoutCallbackUri(facade)) {
log.trace("## LogoutHandler.tryLogout isLogoutCallbackUri");
if (isFrontChannel(facade)) {
log.trace("## LogoutHandler.tryLogout isFrontChannel");
handleFrontChannelLogoutRequest(facade);
return true;
} else {
log.trace("## LogoutHandler.tryLogout !isFrontChannel");
// we have an active session, should have received a GET logout request
facade.getResponse().setStatus(HttpStatus.SC_METHOD_NOT_ALLOWED);
facade.authenticationFailed();
}
if (isLogoutCallbackPath(facade)) {
handleLogoutRequest(facade);
return true;
}

return false;
}

boolean tryBackChannelLogout(OidcHttpFacade facade) {
log.trace("## LogoutHandler.tryBackChannelLogout ENTERED");
log.trace(facade.rlsGetSessionIds());
if (isLogoutCallbackUri(facade)) {
log.trace("## LogoutHandler.tryBackChannelLogout isLogoutCallbackUri");
if (isBackChannel(facade)) {
log.trace("## LogoutHandler.tryBackChannelLogout isBackChannel");
handleBackChannelLogoutRequest(facade);
return true;
} else {
log.trace("## LogoutHandler.tryBackChannelLogout ! isBackChannel");
// no active session, should have received a POST logout request
facade.getResponse().setStatus(HttpStatus.SC_METHOD_NOT_ALLOWED);
facade.authenticationFailed();
}
}
return false;
}

private boolean isSessionMarkedForInvalidation(OidcHttpFacade facade) {
RefreshableOidcSecurityContext securityContext = getSecurityContext(facade);
IDToken idToken = securityContext.getIDToken();
Expand All @@ -132,15 +101,16 @@ private boolean isSessionMarkedForInvalidation(OidcHttpFacade facade) {
private void redirectEndSessionEndpoint(OidcHttpFacade facade) {
RefreshableOidcSecurityContext securityContext = getSecurityContext(facade);
OidcClientConfiguration clientConfiguration = securityContext.getOidcClientConfiguration();

String logoutUri;

try {
URIBuilder redirectUriBuilder = new URIBuilder(clientConfiguration.getEndSessionEndpointUrl())
.addParameter(ID_TOKEN_HINT_PARAM, securityContext.getIDTokenString());
String postLogoutUri = clientConfiguration.getPostLogoutUri();

if (postLogoutUri != null) {
redirectUriBuilder.addParameter(POST_LOGOUT_REDIRECT_URI_PARAM, getRedirectUri(facade) + postLogoutUri);
String postLogoutPath = clientConfiguration.getPostLogoutPath();
if (postLogoutPath != null) {
redirectUriBuilder.addParameter(POST_LOGOUT_REDIRECT_URI_PARAM,
getRedirectUri(facade) + postLogoutPath);
}

logoutUri = redirectUriBuilder.build().toString();
Expand All @@ -153,7 +123,20 @@ private void redirectEndSessionEndpoint(OidcHttpFacade facade) {
facade.getResponse().setHeader(HttpConstants.LOCATION, logoutUri);
}

private void handleLogoutRequest(OidcHttpFacade facade) {
if (isFrontChannel(facade)) {
handleFrontChannelLogoutRequest(facade);
} else if (isBackChannel(facade)) {
handleBackChannelLogoutRequest(facade);
} else {
// logout requests should arrive either as a HTTP GET or POST
facade.getResponse().setStatus(HttpStatus.SC_METHOD_NOT_ALLOWED);
facade.authenticationFailed();
}
}

private void handleBackChannelLogoutRequest(OidcHttpFacade facade) {
RefreshableOidcSecurityContext securityContext = getSecurityContext(facade);
String logoutToken = facade.getRequest().getFirstParam(LOGOUT_TOKEN_PARAM);
TokenValidator tokenValidator = TokenValidator.builder(facade.getOidcClientConfiguration())
.setSkipExpirationValidator()
Expand Down Expand Up @@ -186,7 +169,7 @@ private void handleBackChannelLogoutRequest(OidcHttpFacade facade) {
}

log.debug("Marking session for invalidation during back-channel logout");
sessionsMarkedForInvalidation.put(sessionId, facade.getOidcClientConfiguration());
sessionsMarkedForInvalidation.put(sessionId, securityContext.getOidcClientConfiguration());
}

private void handleFrontChannelLogoutRequest(OidcHttpFacade facade) {
Expand Down Expand Up @@ -221,8 +204,7 @@ private String getRedirectUri(OidcHttpFacade facade) {
if (uri.indexOf('?') != -1) {
uri = uri.substring(0, uri.indexOf('?'));
}

int logoutPathIndex = uri.indexOf(getLogoutUri(facade));
int logoutPathIndex = uri.indexOf(getLogoutPath(facade));

if (logoutPathIndex != -1) {
uri = uri.substring(0, logoutPathIndex);
Expand All @@ -231,22 +213,29 @@ private String getRedirectUri(OidcHttpFacade facade) {
return uri;
}

private boolean isLogoutCallbackUri(OidcHttpFacade facade) {
private boolean isLogoutCallbackPath(OidcHttpFacade facade) {
String path = facade.getRequest().getRelativePath();
log.trace("## LogoutHandler.isLogoutCallbackUri path: "
+ path + ", logoutCallbackUri: " + getLogoutCallbackUri(facade));
return path.endsWith(getLogoutCallbackUri(facade));
String xx = getLogoutCallbackPath(facade); // rls debug only
return path.endsWith(getLogoutCallbackPath(facade));
}

private boolean isRpInitiatedLogoutUri(OidcHttpFacade facade) {
private boolean isRpInitiatedLogoutPath(OidcHttpFacade facade) {
String path = facade.getRequest().getRelativePath();
log.trace("## LogoutHandler.isRpInitiatedLogoutUri path: "
+ path + ", logoutUri: " + getLogoutUri(facade));
return path.endsWith(getLogoutUri(facade));
return path.endsWith(getLogoutPath(facade));
}

private boolean isSessionRequiredOnLogout(OidcHttpFacade facade) {
return facade.getOidcClientConfiguration().isSessionRequiredOnLogout();
return getOidcClientConfiguration(facade).isSessionRequiredOnLogout();
}

private OidcClientConfiguration getOidcClientConfiguration(OidcHttpFacade facade) {
RefreshableOidcSecurityContext securityContext = getSecurityContext(facade);

if (securityContext == null) {
return null;
}

return securityContext.getOidcClientConfiguration();
}

private RefreshableOidcSecurityContext getSecurityContext(OidcHttpFacade facade) {
Expand All @@ -261,12 +250,11 @@ private RefreshableOidcSecurityContext getSecurityContext(OidcHttpFacade facade)
return securityContext;
}

private String getLogoutUri(OidcHttpFacade facade) {
return facade.getOidcClientConfiguration().getLogoutUrl();
private String getLogoutPath(OidcHttpFacade facade) {
return getOidcClientConfiguration(facade).getLogoutPath();
}

private String getLogoutCallbackUri(OidcHttpFacade facade) {
return facade.getOidcClientConfiguration().getLogoutCallbackUrl();
private String getLogoutCallbackPath(OidcHttpFacade facade) {
return getOidcClientConfiguration(facade).getLogoutCallbackPath();
}

private boolean isBackChannel(OidcHttpFacade facade) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ public class Oidc {
public static final String CONFIDENTIAL_PORT = "confidential-port";
public static final String ENABLE_BASIC_AUTH = "enable-basic-auth";
public static final String PROVIDER_URL = "provider-url";
public static final String LOGOUT_PATH = "logout-path";
public static final String LOGOUT_CALLBACK_PATH = "logout-callback-path";
public static final String POST_LOGOUT_PATH = "post-logout-path";
public static final String LOGOUT_SESSION_REQUIRED = "logout-session-required";

/**
* Bearer token pattern.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ public String getMechanismName() {

@Override
public void evaluateRequest(HttpServerRequest request) throws HttpAuthenticationException {
log.trace("## OidcAuthenticationMechanism.evaluateRequest requestURI: " +
request.getRequestURI().toString());
String tmpURI = request.getRequestURI().toString();
OidcClientContext oidcClientContext = getOidcClientContext(request);
if (oidcClientContext == null) {
Expand All @@ -73,7 +71,6 @@ public void evaluateRequest(HttpServerRequest request) throws HttpAuthentication
OidcHttpFacade httpFacade = new OidcHttpFacade(request, oidcClientContext, callbackHandler);
OidcClientConfiguration oidcClientConfiguration = httpFacade.getOidcClientConfiguration();
if (! oidcClientConfiguration.isConfigured()) {
log.trace("## OidcAuthenticationMechanism.evaluateRequest !isConfigured()");
request.noAuthenticationInProgress();
return;
}
Expand All @@ -89,35 +86,21 @@ public void evaluateRequest(HttpServerRequest request) throws HttpAuthentication

AuthOutcome outcome = authenticator.authenticate();
if (AuthOutcome.AUTHENTICATED.equals(outcome)) {
log.trace("## OidcAuthenticationMechanism.evaluateRequest AuthOutcome.AUTHENTICATED outcome");
if (new AuthenticatedActionsHandler(oidcClientConfiguration, httpFacade).handledRequest()
|| logoutHandler.tryLogout(httpFacade)) {
log.trace("## OidcAuthenticationMechanism.evaluateRequest call authenticationInProgress()");
) {
httpFacade.authenticationInProgress();
} else {
log.trace("## OidcAuthenticationMechanism.evaluateRequest call authenticationComplete()");
httpFacade.authenticationComplete();
}
return;
}

if (AuthOutcome.NOT_ATTEMPTED.equals(outcome)) {
log.trace("## OidcAuthenticationMechanism.evaluateRequest AuthOutcome.NOT_ATTEMPTED");
if (logoutHandler.tryBackChannelLogout(httpFacade)) {
log.trace("## OidcAuthenticationMechanism.evaluateRequest tryBackChannelLogout returned true");
httpFacade.authenticationInProgress();
return;
}
}

AuthChallenge challenge = authenticator.getChallenge();
if (challenge != null) {
log.trace("## OidcAuthenticationMechanism.evaluateRequest challenge != null");
httpFacade.noAuthenticationInProgress(challenge);
return;
}
if (Oidc.AuthOutcome.FAILED.equals(outcome)) {
log.trace("## OidcAuthenticationMechanism.evaluateRequest AuthOutcome.FAILED");
httpFacade.getResponse().setStatus(HttpStatus.SC_FORBIDDEN);
httpFacade.authenticationFailed();
return;
Expand Down
Loading

0 comments on commit 3affa81

Please sign in to comment.