revocation next phase: undertow complete
This commit is contained in:
parent
7b30cc59b8
commit
c8023c6651
14 changed files with 138 additions and 139 deletions
|
@ -10,17 +10,17 @@ import java.io.Serializable;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class KeycloakAuthenticatedSession implements Serializable {
|
||||
public class KeycloakSecurityContext implements Serializable {
|
||||
protected String tokenString;
|
||||
protected AccessToken token;
|
||||
protected IDToken idToken;
|
||||
protected String idTokenString;
|
||||
protected transient ResourceMetadata metadata;
|
||||
|
||||
public KeycloakAuthenticatedSession() {
|
||||
public KeycloakSecurityContext() {
|
||||
}
|
||||
|
||||
public KeycloakAuthenticatedSession(String tokenString, AccessToken token, String idTokenString, IDToken idToken, ResourceMetadata metadata) {
|
||||
public KeycloakSecurityContext(String tokenString, AccessToken token, String idTokenString, IDToken idToken, ResourceMetadata metadata) {
|
||||
this.tokenString = tokenString;
|
||||
this.token = token;
|
||||
this.idToken = idToken;
|
|
@ -4,7 +4,7 @@ import org.apache.http.HttpEntity;
|
|||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.keycloak.KeycloakAuthenticatedSession;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.HttpClientBuilder;
|
||||
import org.keycloak.representations.IDToken;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
@ -37,13 +37,13 @@ public class CustomerDatabaseClient {
|
|||
}
|
||||
|
||||
public static IDToken getIDToken(HttpServletRequest req) {
|
||||
KeycloakAuthenticatedSession session = (KeycloakAuthenticatedSession) req.getAttribute(KeycloakAuthenticatedSession.class.getName());
|
||||
KeycloakSecurityContext session = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
|
||||
return session.getIdToken();
|
||||
|
||||
}
|
||||
|
||||
public static List<String> getCustomers(HttpServletRequest req) throws Failure {
|
||||
KeycloakAuthenticatedSession session = (KeycloakAuthenticatedSession) req.getAttribute(KeycloakAuthenticatedSession.class.getName());
|
||||
KeycloakSecurityContext session = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
|
||||
|
||||
HttpClient client = new HttpClientBuilder()
|
||||
.trustStore(session.getMetadata().getTruststore())
|
||||
|
|
|
@ -4,7 +4,7 @@ import org.apache.http.HttpEntity;
|
|||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.keycloak.KeycloakAuthenticatedSession;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.HttpClientBuilder;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
|
@ -35,7 +35,7 @@ public class ProductDatabaseClient
|
|||
}
|
||||
|
||||
public static List<String> getProducts(HttpServletRequest req) throws Failure {
|
||||
KeycloakAuthenticatedSession session = (KeycloakAuthenticatedSession)req.getAttribute(KeycloakAuthenticatedSession.class.getName());
|
||||
KeycloakSecurityContext session = (KeycloakSecurityContext)req.getAttribute(KeycloakSecurityContext.class.getName());
|
||||
HttpClient client = new HttpClientBuilder()
|
||||
.trustStore(session.getMetadata().getTruststore())
|
||||
.hostnameVerification(HttpClientBuilder.HostnameVerificationPolicy.ANY).build();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package org.keycloak.adapters;
|
||||
|
||||
import org.keycloak.KeycloakAuthenticatedSession;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.RSATokenVerifier;
|
||||
import org.keycloak.VerificationException;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
|
@ -15,7 +15,7 @@ import java.io.IOException;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RefreshableKeycloakSession extends KeycloakAuthenticatedSession {
|
||||
public class RefreshableKeycloakSession extends KeycloakSecurityContext {
|
||||
|
||||
protected static Logger log = Logger.getLogger(RefreshableKeycloakSession.class);
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import org.apache.catalina.connector.Request;
|
|||
import org.apache.catalina.connector.Response;
|
||||
import org.apache.catalina.valves.ValveBase;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.KeycloakAuthenticatedSession;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.AdapterConstants;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
|
@ -45,7 +45,7 @@ public class AuthenticatedActionsValve extends ValveBase {
|
|||
@Override
|
||||
public void invoke(Request request, Response response) throws IOException, ServletException {
|
||||
log.debugv("AuthenticatedActionsValve.invoke {0}", request.getRequestURI());
|
||||
KeycloakAuthenticatedSession session = getSkeletonKeySession(request);
|
||||
KeycloakSecurityContext session = getSkeletonKeySession(request);
|
||||
if (corsRequest(request, response, session)) return;
|
||||
String requestUri = request.getRequestURI();
|
||||
if (requestUri.endsWith(AdapterConstants.K_QUERY_BEARER_TOKEN)) {
|
||||
|
@ -55,17 +55,17 @@ public class AuthenticatedActionsValve extends ValveBase {
|
|||
getNext().invoke(request, response);
|
||||
}
|
||||
|
||||
public KeycloakAuthenticatedSession getSkeletonKeySession(Request request) {
|
||||
KeycloakAuthenticatedSession skSession = (KeycloakAuthenticatedSession) request.getAttribute(KeycloakAuthenticatedSession.class.getName());
|
||||
public KeycloakSecurityContext getSkeletonKeySession(Request request) {
|
||||
KeycloakSecurityContext skSession = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
|
||||
if (skSession != null) return skSession;
|
||||
Session session = request.getSessionInternal();
|
||||
if (session != null) {
|
||||
return (KeycloakAuthenticatedSession) session.getNote(KeycloakAuthenticatedSession.class.getName());
|
||||
return (KeycloakSecurityContext) session.getNote(KeycloakSecurityContext.class.getName());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void queryBearerToken(Request request, Response response, KeycloakAuthenticatedSession session) throws IOException, ServletException {
|
||||
protected void queryBearerToken(Request request, Response response, KeycloakSecurityContext session) throws IOException, ServletException {
|
||||
log.debugv("queryBearerToken {0}", request.getRequestURI());
|
||||
if (abortTokenResponse(request, response, session)) return;
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
|
@ -75,7 +75,7 @@ public class AuthenticatedActionsValve extends ValveBase {
|
|||
|
||||
}
|
||||
|
||||
protected boolean abortTokenResponse(Request request, Response response, KeycloakAuthenticatedSession session) throws IOException {
|
||||
protected boolean abortTokenResponse(Request request, Response response, KeycloakSecurityContext session) throws IOException {
|
||||
if (session == null) {
|
||||
log.debugv("session was null, sending back 401: {0}", request.getRequestURI());
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
|
@ -92,7 +92,7 @@ public class AuthenticatedActionsValve extends ValveBase {
|
|||
return false;
|
||||
}
|
||||
|
||||
protected boolean corsRequest(Request request, Response response, KeycloakAuthenticatedSession session) throws IOException {
|
||||
protected boolean corsRequest(Request request, Response response, KeycloakSecurityContext session) throws IOException {
|
||||
if (!config.isCors()) return false;
|
||||
log.debugv("CORS enabled + request.getRequestURI()");
|
||||
String origin = request.getHeader("Origin");
|
||||
|
|
|
@ -2,7 +2,7 @@ package org.keycloak.adapters.as7;
|
|||
|
||||
import org.apache.catalina.connector.Request;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.KeycloakAuthenticatedSession;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.RSATokenVerifier;
|
||||
import org.keycloak.adapters.ResourceMetadata;
|
||||
|
@ -114,8 +114,8 @@ public class CatalinaBearerTokenAuthenticator {
|
|||
principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), skeletonKeyPrincipal, roles);
|
||||
request.setUserPrincipal(principal);
|
||||
request.setAuthType("KEYCLOAK");
|
||||
KeycloakAuthenticatedSession skSession = new KeycloakAuthenticatedSession(tokenString, token, null, null, resourceMetadata);
|
||||
request.setAttribute(KeycloakAuthenticatedSession.class.getName(), skSession);
|
||||
KeycloakSecurityContext skSession = new KeycloakSecurityContext(tokenString, token, null, null, resourceMetadata);
|
||||
request.setAttribute(KeycloakSecurityContext.class.getName(), skSession);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import org.apache.catalina.core.StandardContext;
|
|||
import org.apache.catalina.deploy.LoginConfig;
|
||||
import org.apache.catalina.realm.GenericPrincipal;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.KeycloakAuthenticatedSession;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.adapters.AdapterConstants;
|
||||
import org.keycloak.adapters.RefreshableKeycloakSession;
|
||||
|
@ -224,7 +224,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
*/
|
||||
protected void checkKeycloakSession(Request request) {
|
||||
if (request.getSessionInternal(false) == null || request.getSessionInternal().getPrincipal() == null) return;
|
||||
RefreshableKeycloakSession session = (RefreshableKeycloakSession)request.getSessionInternal().getNote(KeycloakAuthenticatedSession.class.getName());
|
||||
RefreshableKeycloakSession session = (RefreshableKeycloakSession)request.getSessionInternal().getNote(KeycloakSecurityContext.class.getName());
|
||||
if (session == null) return;
|
||||
// just in case session got serialized
|
||||
session.setRealmConfiguration(realmConfiguration);
|
||||
|
@ -236,7 +236,7 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
session.refreshExpiredToken();
|
||||
if (session.isActive()) return;
|
||||
|
||||
request.getSessionInternal().removeNote(KeycloakAuthenticatedSession.class.getName());
|
||||
request.getSessionInternal().removeNote(KeycloakSecurityContext.class.getName());
|
||||
request.setUserPrincipal(null);
|
||||
request.setAuthType(null);
|
||||
request.getSessionInternal().setPrincipal(null);
|
||||
|
@ -252,9 +252,9 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
request.setAuthType("KEYCLOAK");
|
||||
Session session = request.getSessionInternal();
|
||||
if (session != null) {
|
||||
KeycloakAuthenticatedSession skSession = (KeycloakAuthenticatedSession) session.getNote(KeycloakAuthenticatedSession.class.getName());
|
||||
KeycloakSecurityContext skSession = (KeycloakSecurityContext) session.getNote(KeycloakSecurityContext.class.getName());
|
||||
if (skSession != null) {
|
||||
request.setAttribute(KeycloakAuthenticatedSession.class.getName(), skSession);
|
||||
request.setAttribute(KeycloakSecurityContext.class.getName(), skSession);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -293,8 +293,8 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
|
|||
Session session = request.getSessionInternal(true);
|
||||
session.setPrincipal(principal);
|
||||
session.setAuthType("OAUTH");
|
||||
KeycloakAuthenticatedSession skSession = new RefreshableKeycloakSession(oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), resourceMetadata, realmConfiguration, oauth.getRefreshToken());
|
||||
session.setNote(KeycloakAuthenticatedSession.class.getName(), skSession);
|
||||
KeycloakSecurityContext skSession = new RefreshableKeycloakSession(oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), resourceMetadata, realmConfiguration, oauth.getRefreshToken());
|
||||
session.setNote(KeycloakSecurityContext.class.getName(), skSession);
|
||||
|
||||
String username = token.getSubject();
|
||||
log.debug("userSessionManage.login: " + username);
|
||||
|
|
|
@ -2,7 +2,7 @@ package org.keycloak.jaxrs;
|
|||
|
||||
import org.jboss.resteasy.logging.Logger;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.KeycloakAuthenticatedSession;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.RSATokenVerifier;
|
||||
import org.keycloak.adapters.ResourceMetadata;
|
||||
|
@ -67,8 +67,8 @@ public class JaxrsBearerTokenFilter implements ContainerRequestFilter {
|
|||
|
||||
try {
|
||||
AccessToken token = RSATokenVerifier.verifyToken(tokenString, resourceMetadata.getRealmKey(), resourceMetadata.getRealm());
|
||||
KeycloakAuthenticatedSession skSession = new KeycloakAuthenticatedSession(tokenString, token, null, null, resourceMetadata);
|
||||
ResteasyProviderFactory.pushContext(KeycloakAuthenticatedSession.class, skSession);
|
||||
KeycloakSecurityContext skSession = new KeycloakSecurityContext(tokenString, token, null, null, resourceMetadata);
|
||||
ResteasyProviderFactory.pushContext(KeycloakSecurityContext.class, skSession);
|
||||
String callerPrincipal = securityContext.getUserPrincipal() != null ? securityContext.getUserPrincipal().getName() : null;
|
||||
|
||||
final KeycloakPrincipal principal = new KeycloakPrincipal(token.getSubject(), callerPrincipal);
|
||||
|
|
|
@ -2,23 +2,16 @@ package org.keycloak.adapters.undertow;
|
|||
|
||||
import io.undertow.security.api.AuthenticationMechanism;
|
||||
import io.undertow.security.api.SecurityContext;
|
||||
import io.undertow.security.idm.Account;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.util.AttachmentKey;
|
||||
import io.undertow.util.Headers;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.KeycloakAuthenticatedSession;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.adapters.RefreshableKeycloakSession;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.adapters.ResourceMetadata;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
|
@ -27,7 +20,6 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism
|
|||
protected Logger log = Logger.getLogger(KeycloakAuthenticationMechanism.class);
|
||||
|
||||
public static final AttachmentKey<KeycloakChallenge> KEYCLOAK_CHALLENGE_ATTACHMENT_KEY = AttachmentKey.create(KeycloakChallenge.class);
|
||||
public static final AttachmentKey<KeycloakAuthenticatedSession> SKELETON_KEY_SESSION_ATTACHMENT_KEY = AttachmentKey.create(KeycloakAuthenticatedSession.class);
|
||||
|
||||
protected ResourceMetadata resourceMetadata;
|
||||
protected AdapterConfig adapterConfig;
|
||||
|
@ -41,11 +33,6 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism
|
|||
this.sslRedirectPort = sslRedirectPort;
|
||||
}
|
||||
|
||||
public KeycloakAuthenticationMechanism(AdapterConfig adapterConfig, ResourceMetadata resourceMetadata) {
|
||||
this.resourceMetadata = resourceMetadata;
|
||||
this.adapterConfig = adapterConfig;
|
||||
}
|
||||
|
||||
public KeycloakAuthenticationMechanism(AdapterConfig adapterConfig, RealmConfiguration realmConfig) {
|
||||
this.resourceMetadata = realmConfig.getMetadata();
|
||||
this.adapterConfig = adapterConfig;
|
||||
|
@ -60,28 +47,38 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism
|
|||
if (outcome == AuthenticationMechanismOutcome.NOT_AUTHENTICATED) {
|
||||
exchange.putAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY, bearer.getChallenge());
|
||||
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
|
||||
}
|
||||
else if (outcome == AuthenticationMechanismOutcome.AUTHENTICATED) {
|
||||
completeAuthentication(securityContext, bearer);
|
||||
} else if (outcome == AuthenticationMechanismOutcome.AUTHENTICATED) {
|
||||
completeAuthentication(exchange, securityContext, bearer);
|
||||
return AuthenticationMechanismOutcome.AUTHENTICATED;
|
||||
}
|
||||
else if (adapterConfig.isBearerOnly()) {
|
||||
} else if (adapterConfig.isBearerOnly()) {
|
||||
exchange.putAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY, bearer.getChallenge());
|
||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
||||
}
|
||||
// We cache account ourselves instead of using the Cache session handler of Undertow because
|
||||
// Undertow will return a 403 from an invalid account when calling IdentityManager.verify(Account) and
|
||||
// we want to just return NOT_ATTEMPTED so we can be redirected to relogin
|
||||
KeycloakUndertowAccount account = checkCachedAccount(exchange);
|
||||
if (account != null) {
|
||||
log.info("Cached account found");
|
||||
securityContext.authenticationComplete(account, "KEYCLOAK", false);
|
||||
propagateKeycloakContext(exchange, account);
|
||||
return AuthenticationMechanismOutcome.AUTHENTICATED;
|
||||
}
|
||||
|
||||
|
||||
OAuthAuthenticator oauth = createOAuthAuthenticator(exchange);
|
||||
outcome = oauth.authenticate();
|
||||
if (outcome == AuthenticationMechanismOutcome.NOT_AUTHENTICATED) {
|
||||
exchange.putAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY, oauth.getChallenge());
|
||||
return AuthenticationMechanismOutcome.NOT_AUTHENTICATED;
|
||||
}
|
||||
else if (outcome == AuthenticationMechanismOutcome.NOT_ATTEMPTED) {
|
||||
} else if (outcome == AuthenticationMechanismOutcome.NOT_ATTEMPTED) {
|
||||
exchange.putAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY, oauth.getChallenge());
|
||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
||||
|
||||
}
|
||||
completeAuthentication(exchange, securityContext, oauth);
|
||||
|
||||
// redirect to strip out access code and state query parameters
|
||||
exchange.getResponseHeaders().put(Headers.LOCATION, oauth.getStrippedOauthParametersRequestUri());
|
||||
exchange.setResponseCode(302);
|
||||
exchange.endExchange();
|
||||
|
@ -90,12 +87,6 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism
|
|||
return AuthenticationMechanismOutcome.AUTHENTICATED;
|
||||
}
|
||||
|
||||
public static void sendRedirect(final HttpServerExchange exchange, final String location) {
|
||||
// TODO - String concatenation to construct URLS is extremely error prone - switch to a URI which will better handle this.
|
||||
String loc = exchange.getRequestScheme() + "://" + exchange.getHostAndPort() + location;
|
||||
}
|
||||
|
||||
|
||||
protected OAuthAuthenticator createOAuthAuthenticator(HttpServerExchange exchange) {
|
||||
return new OAuthAuthenticator(exchange, realmConfig, sslRedirectPort);
|
||||
}
|
||||
|
@ -108,7 +99,11 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism
|
|||
final KeycloakPrincipal principal = new KeycloakPrincipal(oauth.getToken().getSubject(), null);
|
||||
RefreshableKeycloakSession session = new RefreshableKeycloakSession(oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), resourceMetadata, realmConfig, oauth.getRefreshToken());
|
||||
KeycloakUndertowAccount account = new KeycloakUndertowAccount(principal, session, adapterConfig, resourceMetadata);
|
||||
securityContext.authenticationComplete(account, "KEYCLOAK", true);
|
||||
|
||||
// We cache account ourselves instead of using the Cache session handler of Undertow because
|
||||
// Undertow will return a 403 from an invalid account when calling IdentityManager.verify(Account) and
|
||||
// we want to just return NOT_ATTEMPTED so we can be redirected to relogin
|
||||
securityContext.authenticationComplete(account, "KEYCLOAK", false);
|
||||
login(exchange, account);
|
||||
}
|
||||
|
||||
|
@ -116,12 +111,17 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism
|
|||
// complete
|
||||
}
|
||||
|
||||
protected void propagateKeycloakContext(HttpServerExchange exchange, KeycloakUndertowAccount account) {
|
||||
// complete
|
||||
}
|
||||
|
||||
protected void completeAuthentication(SecurityContext securityContext, BearerTokenAuthenticator bearer) {
|
||||
|
||||
protected void completeAuthentication(HttpServerExchange exchange, SecurityContext securityContext, BearerTokenAuthenticator bearer) {
|
||||
final KeycloakPrincipal principal = new KeycloakPrincipal(bearer.getToken().getSubject(), bearer.getSurrogate());
|
||||
RefreshableKeycloakSession session = new RefreshableKeycloakSession(bearer.getTokenString(), bearer.getToken(), null, null, resourceMetadata, realmConfig, null);
|
||||
KeycloakUndertowAccount account = new KeycloakUndertowAccount(principal, session, adapterConfig, resourceMetadata);
|
||||
securityContext.authenticationComplete(account, "KEYCLOAK", false);
|
||||
propagateKeycloakContext(exchange, account);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -132,4 +132,10 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism
|
|||
}
|
||||
return new ChallengeResult(false);
|
||||
}
|
||||
|
||||
protected KeycloakUndertowAccount checkCachedAccount(HttpServerExchange exchange) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -88,10 +88,24 @@ public class KeycloakServletExtension implements ServletExtension {
|
|||
return theAuth;
|
||||
}
|
||||
}); // authentication
|
||||
deploymentInfo.addInnerHandlerChainWrapper(ServletPropagateSessionHandler.WRAPPER); // propagates SkeletonKeySession
|
||||
deploymentInfo.addInnerHandlerChainWrapper(actions); // handles authenticated actions and cors.
|
||||
|
||||
deploymentInfo.setIdentityManager(new KeycloakIdentityManager(keycloakConfig, realmConfiguration));
|
||||
deploymentInfo.setIdentityManager(new IdentityManager() {
|
||||
@Override
|
||||
public Account verify(Account account) {
|
||||
return account;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account verify(String id, Credential credential) {
|
||||
throw new IllegalStateException("Should never be called in Keycloak flow");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account verify(Credential credential) {
|
||||
throw new IllegalStateException("Should never be called in Keycloak flow");
|
||||
}
|
||||
});
|
||||
|
||||
log.info("Setting jsession cookie path to: " + deploymentInfo.getContextPath());
|
||||
ServletSessionConfig cookieConfig = new ServletSessionConfig();
|
||||
|
|
|
@ -70,10 +70,19 @@ public class KeycloakUndertowAccount implements Account, Serializable {
|
|||
session.setRealmConfiguration(realmConfiguration);
|
||||
session.setMetadata(realmConfiguration.getMetadata());
|
||||
log.info("realmConfig notBefore: " + realmConfiguration.getNotBefore());
|
||||
if (session.isActive()) return true;
|
||||
if (session.isActive()) {
|
||||
log.info("session is active");
|
||||
return true;
|
||||
}
|
||||
|
||||
log.info("session is not active try refresh");
|
||||
session.refreshExpiredToken();
|
||||
if (!session.isActive()) return false;
|
||||
if (!session.isActive()) {
|
||||
log.info("session is not active return with failure");
|
||||
|
||||
return false;
|
||||
}
|
||||
log.info("refresh succeeded");
|
||||
|
||||
setRoles(session.getToken(), config, realmConfiguration.getMetadata());
|
||||
return true;
|
||||
|
|
|
@ -1,21 +1,14 @@
|
|||
package org.keycloak.adapters.undertow;
|
||||
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.server.session.Session;
|
||||
import io.undertow.servlet.api.ConfidentialPortManager;
|
||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
||||
import io.undertow.servlet.spec.HttpSessionImpl;
|
||||
import org.keycloak.KeycloakAuthenticatedSession;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.config.RealmConfiguration;
|
||||
import org.keycloak.adapters.ResourceMetadata;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
import java.security.AccessController;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -31,22 +24,47 @@ public class ServletKeycloakAuthenticationMechanism extends KeycloakAuthenticati
|
|||
this.userSessionManagement = userSessionManagement;
|
||||
}
|
||||
|
||||
public ServletKeycloakAuthenticationMechanism(AdapterConfig config, ResourceMetadata metadata, ConfidentialPortManager portManager) {
|
||||
super(config, metadata);
|
||||
this.portManager = portManager;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected OAuthAuthenticator createOAuthAuthenticator(HttpServerExchange exchange) {
|
||||
return new ServletOAuthAuthenticator(exchange, realmConfig, portManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected KeycloakUndertowAccount checkCachedAccount(HttpServerExchange exchange) {
|
||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
||||
HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
|
||||
HttpSession session = req.getSession(false);
|
||||
if (session == null) {
|
||||
log.info("session was null, returning null");
|
||||
return null;
|
||||
}
|
||||
KeycloakUndertowAccount account = (KeycloakUndertowAccount)session.getAttribute(KeycloakUndertowAccount.class.getName());
|
||||
if (account == null) {
|
||||
log.info("Account was not in session, returning null");
|
||||
return null;
|
||||
}
|
||||
if (account.isActive(realmConfig, adapterConfig)) return account;
|
||||
log.info("Account was not active, returning null");
|
||||
session.setAttribute(KeycloakUndertowAccount.class.getName(), null);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void propagateKeycloakContext(HttpServerExchange exchange, KeycloakUndertowAccount account) {
|
||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
||||
HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
|
||||
req.setAttribute(KeycloakSecurityContext.class.getName(), account.getSession());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void login(HttpServerExchange exchange, KeycloakUndertowAccount account) {
|
||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
||||
HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
|
||||
req.setAttribute(KeycloakSecurityContext.class.getName(), account.getSession());
|
||||
HttpSession session = req.getSession(true);
|
||||
session.setAttribute(KeycloakUndertowAccount.class.getName(), account);
|
||||
userSessionManagement.login(servletRequestContext.getDeployment().getSessionManager(), session, account.getPrincipal().getName());
|
||||
|
||||
}
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
package org.keycloak.adapters.undertow;
|
||||
|
||||
import io.undertow.server.HandlerWrapper;
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.KeycloakAuthenticatedSession;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ServletPropagateSessionHandler implements HttpHandler {
|
||||
|
||||
private static final Logger log = Logger.getLogger(ServletPropagateSessionHandler.class);
|
||||
|
||||
protected HttpHandler next;
|
||||
|
||||
protected ServletPropagateSessionHandler(HttpHandler next) {
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
public static final HandlerWrapper WRAPPER = new HandlerWrapper() {
|
||||
@Override
|
||||
public HttpHandler wrap(HttpHandler handler) {
|
||||
return new ServletPropagateSessionHandler(handler);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||
log.debug("handleRequest");
|
||||
KeycloakUndertowAccount account = (KeycloakUndertowAccount)exchange.getSecurityContext().getAuthenticatedAccount();
|
||||
if (account == null) {
|
||||
log.debug("Not logged in, nothing to propagate");
|
||||
next.handleRequest(exchange);
|
||||
return;
|
||||
}
|
||||
|
||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
||||
HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
|
||||
req.setAttribute(KeycloakAuthenticatedSession.class.getName(), account.getSession());
|
||||
|
||||
HttpSession session = req.getSession(false);
|
||||
if (session == null) {
|
||||
next.handleRequest(exchange);
|
||||
return;
|
||||
}
|
||||
log.debug("propagating to HTTP Session");
|
||||
session.setAttribute(KeycloakAuthenticatedSession.class.getName(), account.getSession());
|
||||
next.handleRequest(exchange);
|
||||
}
|
||||
}
|
|
@ -40,6 +40,7 @@ public class AuthenticationManager {
|
|||
public static final String KEYCLOAK_REMEMBER_ME = "KEYCLOAK_REMEMBER_ME";
|
||||
|
||||
public AccessToken createIdentityToken(RealmModel realm, UserModel user) {
|
||||
logger.info("createIdentityToken");
|
||||
AccessToken token = new AccessToken();
|
||||
token.id(KeycloakModelUtils.generateId());
|
||||
token.issuedNow();
|
||||
|
@ -52,6 +53,7 @@ public class AuthenticationManager {
|
|||
}
|
||||
|
||||
public NewCookie createLoginCookie(RealmModel realm, UserModel user, UriInfo uriInfo, boolean rememberMe) {
|
||||
logger.info("createLoginCookie");
|
||||
String cookieName = KEYCLOAK_IDENTITY_COOKIE;
|
||||
String cookiePath = getIdentityCookiePath(realm, uriInfo);
|
||||
return createLoginCookie(realm, user, null, cookieName, cookiePath, rememberMe);
|
||||
|
@ -140,10 +142,17 @@ public class AuthenticationManager {
|
|||
try {
|
||||
AccessToken token = RSATokenVerifier.verifyToken(tokenString, realm.getPublicKey(), realm.getName(), checkActive);
|
||||
logger.info("identity token verified");
|
||||
if (checkActive && !token.isActive()) {
|
||||
logger.info("identity cookie expired");
|
||||
expireIdentityCookie(realm, uriInfo);
|
||||
return null;
|
||||
if (checkActive) {
|
||||
logger.info("Checking if identity token is active");
|
||||
if (!token.isActive() || token.getIssuedAt() < realm.getNotBefore()) {
|
||||
logger.info("identity cookie expired");
|
||||
expireIdentityCookie(realm, uriInfo);
|
||||
return null;
|
||||
} else {
|
||||
logger.info("token.isActive() : " + token.isActive());
|
||||
logger.info("token.issuedAt: " + token.getIssuedAt());
|
||||
logger.info("real.notbefore: " + realm.getNotBefore());
|
||||
}
|
||||
}
|
||||
|
||||
UserModel user = realm.getUserById(token.getSubject());
|
||||
|
|
Loading…
Reference in a new issue