org.wildfly.security.http.oidc.RequestAuthenticator Maven / Gradle / Ivy
The newest version!
/*
* JBoss, Home of Professional Open Source.
* Copyright 2020 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wildfly.security.http.oidc;
import static org.wildfly.security.http.oidc.ElytronMessages.log;
import static org.wildfly.security.http.oidc.Oidc.ACCEPT;
import static org.wildfly.security.http.oidc.Oidc.AuthOutcome;
import static org.wildfly.security.http.oidc.Oidc.FACES_REQUEST;
import static org.wildfly.security.http.oidc.Oidc.HTML_CONTENT_TYPE;
import static org.wildfly.security.http.oidc.Oidc.PARTIAL;
import static org.wildfly.security.http.oidc.Oidc.SOAP_ACTION;
import static org.wildfly.security.http.oidc.Oidc.TEXT_CONTENT_TYPE;
import static org.wildfly.security.http.oidc.Oidc.WILDCARD_CONTENT_TYPE;
import static org.wildfly.security.http.oidc.Oidc.XML_HTTP_REQUEST;
import static org.wildfly.security.http.oidc.Oidc.X_REQUESTED_WITH;
import java.util.Collections;
import java.util.List;
import org.wildfly.security.http.HttpScope;
import org.wildfly.security.http.Scope;
/**
* @author Pedro Igor
* @author Farah Juma
*/
public class RequestAuthenticator {
protected OidcHttpFacade facade;
protected AuthChallenge challenge;
protected OidcClientConfiguration deployment;
protected int sslRedirectPort;
public RequestAuthenticator(OidcHttpFacade facade, OidcClientConfiguration deployment, int sslRedirectPort) {
this.facade = facade;
this.deployment = deployment;
this.sslRedirectPort = sslRedirectPort;
}
public AuthOutcome authenticate() {
AuthOutcome authenticate = doAuthenticate();
if (AuthOutcome.AUTHENTICATED.equals(authenticate) && !facade.isAuthorized()) {
return AuthOutcome.FAILED;
}
return authenticate;
}
protected OidcRequestAuthenticator createOidcAuthenticator() {
return new OidcRequestAuthenticator(this, facade, deployment, sslRedirectPort, facade.getTokenStore());
}
protected void completeOidcAuthentication(final OidcPrincipal principal) {
facade.authenticationComplete(new OidcAccount(principal), true);
}
protected void completeBearerAuthentication(final OidcPrincipal principal) {
facade.authenticationComplete(new OidcAccount(principal), false);
}
protected String changeHttpSessionId(boolean create) {
HttpScope session = facade.getScope(Scope.SESSION);
if (create) {
if (! session.exists()) {
session.create();
}
}
return session != null ? session.getID() : null;
}
public AuthChallenge getChallenge() {
return challenge;
}
private AuthOutcome doAuthenticate() {
if (log.isTraceEnabled()) {
log.trace("--> authenticate()");
}
if (log.isTraceEnabled()) {
log.trace("try bearer");
}
BearerTokenRequestAuthenticator bearer = new BearerTokenRequestAuthenticator(facade, deployment);
AuthOutcome outcome = bearer.authenticate();
if (outcome == AuthOutcome.FAILED) {
challenge = bearer.getChallenge();
log.debug("Bearer FAILED");
return AuthOutcome.FAILED;
} else if (outcome == AuthOutcome.AUTHENTICATED) {
if (verifySSL()) return AuthOutcome.FAILED;
completeAuthentication(bearer);
log.debug("Bearer AUTHENTICATED");
return AuthOutcome.AUTHENTICATED;
}
QueryParameterTokenRequestAuthenticator queryParamAuth = new QueryParameterTokenRequestAuthenticator(facade, deployment);
if (log.isTraceEnabled()) {
log.trace("try query parameter auth");
}
outcome = queryParamAuth.authenticate();
if (outcome == AuthOutcome.FAILED) {
challenge = queryParamAuth.getChallenge();
log.debug("QueryParamAuth auth FAILED");
return AuthOutcome.FAILED;
} else if (outcome == AuthOutcome.AUTHENTICATED) {
if (verifySSL()) return AuthOutcome.FAILED;
log.debug("QueryParamAuth AUTHENTICATED");
completeAuthentication(queryParamAuth);
return AuthOutcome.AUTHENTICATED;
}
if (deployment.isEnableBasicAuth()) {
BasicAuthRequestAuthenticator basicAuth = new BasicAuthRequestAuthenticator(facade, deployment);
if (log.isTraceEnabled()) {
log.trace("try basic auth");
}
outcome = basicAuth.authenticate();
if (outcome == AuthOutcome.FAILED) {
challenge = basicAuth.getChallenge();
log.debug("BasicAuth FAILED");
return AuthOutcome.FAILED;
} else if (outcome == AuthOutcome.AUTHENTICATED) {
if (verifySSL()) return AuthOutcome.FAILED;
log.debug("BasicAuth AUTHENTICATED");
completeAuthentication(basicAuth);
return AuthOutcome.AUTHENTICATED;
}
}
if (deployment.isBearerOnly()) {
challenge = bearer.getChallenge();
log.debug("NOT_ATTEMPTED: bearer only");
return AuthOutcome.NOT_ATTEMPTED;
}
if (log.isTraceEnabled()) {
log.trace("try oidc");
}
if (facade.getTokenStore().isCached(this)) {
if (verifySSL()) return AuthOutcome.FAILED;
log.debug("AUTHENTICATED: was cached");
return AuthOutcome.AUTHENTICATED;
}
if (isAutodetectedBearerOnly()) {
challenge = bearer.getChallenge();
log.debug("NOT_ATTEMPTED: Treating as bearer only");
return AuthOutcome.NOT_ATTEMPTED;
}
OidcRequestAuthenticator oidc = createOidcAuthenticator();
outcome = oidc.authenticate();
if (outcome == AuthOutcome.FAILED) {
challenge = oidc.getChallenge();
return AuthOutcome.FAILED;
} else if (outcome == AuthOutcome.NOT_ATTEMPTED) {
challenge = oidc.getChallenge();
return AuthOutcome.NOT_ATTEMPTED;
}
if (verifySSL()) return AuthOutcome.FAILED;
completeAuthentication(oidc);
// redirect to strip out access code and state query parameters
facade.getResponse().setHeader("Location", oidc.getStrippedOauthParametersRequestUri());
facade.getResponse().setStatus(302);
facade.getResponse().end();
log.debug("AUTHENTICATED");
return AuthOutcome.AUTHENTICATED;
}
protected boolean verifySSL() {
if (!facade.getRequest().isSecure() && deployment.getSSLRequired().isRequired(facade.getRequest().getRemoteAddr())) {
log.warnf("SSL is required to authenticate. Remote address %s is secure: %s, SSL required for: %s .",
facade.getRequest().getRemoteAddr(), facade.getRequest().isSecure(), deployment.getSSLRequired().name());
return true;
}
return false;
}
protected void completeAuthentication(OidcRequestAuthenticator oidc) {
RefreshableOidcSecurityContext session = new RefreshableOidcSecurityContext(deployment, facade.getTokenStore(), oidc.getTokenString(), oidc.getToken(), oidc.getIDTokenString(), oidc.getIDToken(), oidc.getRefreshToken());
final OidcPrincipal principal = new OidcPrincipal<>(oidc.getIDToken().getPrincipalName(deployment), session);
completeOidcAuthentication(principal);
log.debugv("User ''{0}'' invoking ''{1}'' on client ''{2}''", principal.getName(), facade.getRequest().getURI(), deployment.getResourceName());
}
protected void completeAuthentication(BearerTokenRequestAuthenticator bearer) {
RefreshableOidcSecurityContext session = new RefreshableOidcSecurityContext(deployment, null, bearer.getTokenString(), bearer.getToken(), null, null, null);
final OidcPrincipal principal = new OidcPrincipal<>(bearer.getToken().getPrincipalName(deployment), session);
completeBearerAuthentication(principal);
log.debugv("User ''{0}'' invoking ''{1}'' on client ''{2}''", principal.getName(), facade.getRequest().getURI(), deployment.getResourceName());
}
protected boolean isAutodetectedBearerOnly() {
if (! deployment.isAutodetectBearerOnly()) return false;
String headerValue = facade.getRequest().getHeader(X_REQUESTED_WITH);
if (headerValue != null && headerValue.equalsIgnoreCase(XML_HTTP_REQUEST)) {
return true;
}
headerValue = facade.getRequest().getHeader(FACES_REQUEST);
if (headerValue != null && headerValue.startsWith(PARTIAL)) {
return true;
}
headerValue = facade.getRequest().getHeader(SOAP_ACTION);
if (headerValue != null) {
return true;
}
List accepts = facade.getRequest().getHeaders(ACCEPT);
if (accepts == null) accepts = Collections.emptyList();
for (String accept : accepts) {
if (accept.contains(HTML_CONTENT_TYPE) || accept.contains(TEXT_CONTENT_TYPE) || accept.contains(WILDCARD_CONTENT_TYPE)) {
return false;
}
}
return true;
}
}