Skip to content

Commit

Permalink
Merge pull request #1735 from fjuma/ELY-2362
Browse files Browse the repository at this point in the history
[ELY-2362] Add support for the bearer-only option when using the OIDC HTTP mechanism
  • Loading branch information
fjuma committed Jul 27, 2022
2 parents b7319d1 + d950ffa commit d460e7d
Show file tree
Hide file tree
Showing 22 changed files with 1,682 additions and 333 deletions.
Expand Up @@ -17,6 +17,8 @@
*/
package org.wildfly.security.http;

import java.util.regex.Pattern;

import org.ietf.jgss.GSSManager;

/**
Expand Down Expand Up @@ -120,6 +122,7 @@ private HttpConstants() {
public static final String NEGOTIATE = "Negotiate";
public static final String NEXT_NONCE = "nextnonce";
public static final String NONCE = "nonce";
public static final String PARTIAL = "partial/";
public static final String OPAQUE = "opaque";
public static final String QOP = "qop";
public static final String REALM = "realm";
Expand All @@ -129,16 +132,30 @@ private HttpConstants() {
public static final String URI = "uri";
public static final String USERNAME = "username";
public static final String USERNAME_STAR = "username*";
public static final String XML_HTTP_REQUEST = "XMLHttpRequest";

/*
* Header Names
*/

public static final String ACCEPT = "Accept";
public static final String AUTHENTICATION_INFO = "Authentication-Info";
public static final String AUTHORIZATION = "Authorization";
public static final String FACES_REQUEST = "Faces-Request";
public static final String HOST = "Host";
public static final String LOCATION = "Location";
public static final String SOAP_ACTION = "SOAPAction";
public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
public static final String X_REQUESTED_WITH = "X-Requested-With";

/**
* Errors
*/
public static final String ERROR = "error";
public static final String ERROR_DESCRIPTION = "error_description";
public static final String INVALID_TOKEN = "invalid_token";
public static final String STALE_TOKEN = "Stale token";
public static final String NO_TOKEN = "no_token";

/*
* Mechanism Names
Expand Down Expand Up @@ -171,6 +188,7 @@ private HttpConstants() {
*/

public static final String POST = "POST";
public static final String OPTIONS = "OPTIONS";

/*
* Algorithms
Expand All @@ -187,4 +205,12 @@ private HttpConstants() {
public static final String HTTP = "http";
public static final String HTTPS = "https";

/**
* Bearer token pattern.
* The Bearer token authorization header is of the form "Bearer", followed by optional whitespace, followed by
* the token itself, followed by optional whitespace. The token itself must be one or more characters and must
* not contain any whitespace.
*/
public static final Pattern BEARER_TOKEN_PATTERN = Pattern.compile("^Bearer *([^ ]+) *$", Pattern.CASE_INSENSITIVE);

}
Expand Up @@ -19,6 +19,7 @@
package org.wildfly.security.http.bearer;

import static org.wildfly.security.http.HttpConstants.BEARER_TOKEN;
import static org.wildfly.security.http.HttpConstants.BEARER_TOKEN_PATTERN;
import static org.wildfly.security.http.HttpConstants.FORBIDDEN;
import static org.wildfly.security.http.HttpConstants.REALM;
import static org.wildfly.security.http.HttpConstants.UNAUTHORIZED;
Expand All @@ -28,7 +29,6 @@
import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
Expand Down Expand Up @@ -65,8 +65,6 @@
*/
final class BearerTokenAuthenticationMechanism implements HttpServerAuthenticationMechanism {

private static final Pattern BEARER_TOKEN_PATTERN = Pattern.compile("^Bearer *([^ ]+) *$", Pattern.CASE_INSENSITIVE);

private final CallbackHandler callbackHandler;

BearerTokenAuthenticationMechanism(CallbackHandler callbackHandler) {
Expand Down
Expand Up @@ -34,6 +34,7 @@ public class AccessToken extends JsonWebToken {
private static final String ALLOWED_ORIGINS = "allowed-origins";
private static final String REALM_ACCESS = "realm_access";
private static final String RESOURCE_ACCESS = "resource_access";
private static final String TRUSTED_CERTS = "trusted-certs";

/**
* Construct a new instance.
Expand Down Expand Up @@ -95,4 +96,13 @@ public RealmAccessClaim getResourceAccessClaim(String resource) {
Map<String, RealmAccessClaim> realmAccessClaimMap = getResourceAccessClaim();
return realmAccessClaimMap == null ? null : realmAccessClaimMap.get(resource);
}

/**
* Get the trusted-certs claim.
*
* @return the trusted-certs claim
*/
public List<String> getTrustedCertsClaim() {
return getStringListClaimValue(TRUSTED_CERTS);
}
}
@@ -0,0 +1,89 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2022 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 java.nio.charset.StandardCharsets.UTF_8;
import static org.wildfly.common.array.Arrays2.indexOf;
import static org.wildfly.security.http.HttpConstants.NO_TOKEN;
import static org.wildfly.security.http.oidc.ElytronMessages.log;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.List;

import org.wildfly.common.iteration.ByteIterator;
import org.wildfly.security.http.HttpConstants;

/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @author <a href="mailto:fjuma@redhat.com">Farah Juma</a>
*/
class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticator {

private static final String CHALLENGE_PREFIX = "Basic ";

public BasicAuthRequestAuthenticator(OidcHttpFacade facade, OidcClientConfiguration oidcClientConfiguration) {
super(facade, oidcClientConfiguration);
}

public Oidc.AuthOutcome authenticate() {
List<String> authorizationValues = facade.getRequest().getHeaders(HttpConstants.AUTHORIZATION);
if (authorizationValues == null || authorizationValues.isEmpty()) {
challenge = challengeResponse(AuthenticationError.Reason.NO_AUTHORIZATION_HEADER, null, null);
return Oidc.AuthOutcome.NOT_ATTEMPTED;
}

String basicValue = null;
for (String authorizationValue : authorizationValues) {
if (authorizationValue.regionMatches(true, 0, CHALLENGE_PREFIX, 0, CHALLENGE_PREFIX.length())) {
basicValue = authorizationValue.substring(CHALLENGE_PREFIX.length());
break;
}
}
if (basicValue == null) {
challenge = challengeResponse(AuthenticationError.Reason.INVALID_TOKEN, null, null);
return Oidc.AuthOutcome.NOT_ATTEMPTED;
}
byte[] decodedValue = ByteIterator.ofBytes(basicValue.getBytes(UTF_8)).asUtf8String().base64Decode().drain();
int colonPos = indexOf(decodedValue, ':');
if (colonPos <= 0) {
log.debug("Failed to obtain token");
challenge = challengeResponse(AuthenticationError.Reason.INVALID_TOKEN, NO_TOKEN, null);
return Oidc.AuthOutcome.FAILED;
}

ByteBuffer usernameBytes = ByteBuffer.wrap(decodedValue, 0, colonPos);
ByteBuffer passwordBytes = ByteBuffer.wrap(decodedValue, colonPos + 1, decodedValue.length - colonPos - 1);
CharBuffer usernameChars = UTF_8.decode(usernameBytes);
CharBuffer passwordChars = UTF_8.decode(passwordBytes);
AccessAndIDTokenResponse tokenResponse;
try {
String username = usernameChars.toString();
String password = passwordChars.toString();
tokenResponse = ServerRequest.getBearerToken(oidcClientConfiguration, username, password);
} catch (Exception e) {
log.debug("Failed to obtain token");
challenge = challengeResponse(AuthenticationError.Reason.INVALID_TOKEN, NO_TOKEN, e.getMessage());
return Oidc.AuthOutcome.FAILED;
}
tokenString = tokenResponse.getAccessToken();
return verifyToken(tokenString);
}

}

0 comments on commit d460e7d

Please sign in to comment.