
com.nimbusds.common.oauth2.SHA256BasedAccessTokenValidator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of common Show documentation
Show all versions of common Show documentation
Common Connect2id software classes
The newest version!
package com.nimbusds.common.oauth2;
import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.token.AccessToken;
import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
import com.thetransactioncompany.util.PropertyParseException;
import com.thetransactioncompany.util.PropertyRetriever;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.ws.rs.WebApplicationException;
import net.jcip.annotations.ThreadSafe;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
/**
* SHA-256 based access token validator. The expected access tokens are
* configured as their SHA-256 hashes, to prevent accidental leaks into logs,
* etc. Supports servlet-based and JAX-RS based web applications.
*/
@ThreadSafe
public class SHA256BasedAccessTokenValidator extends AbstractAccessTokenValidator {
/**
* The minimum acceptable access token length.
*/
public static final int MIN_TOKEN_LENGTH = 32;
/**
* The demo access token.
*/
public static final BearerAccessToken DEMO_ACCESS_TOKEN = new BearerAccessToken("ztucZS1ZyFKgh0tUEruUtiSTXhnexmd6");
/**
* The demo access token SHA-256 hash (in hex).
*/
public static final String DEMO_ACCESS_TOKEN_HASH = "cca68b8b82bcf0b96cb826199429e50cd95a042f8e8891d1ac56ab135d096633";
/**
* Creates a new access token validator.
*
* @param tokenHash The Bearer access token SHA-256 hash (in hex). If
* {@code null} access to the web API will be
* disabled.
*/
public SHA256BasedAccessTokenValidator(final String tokenHash) {
this(new String[]{tokenHash});
}
/**
* Creates a new access token validator.
*
* @param tokenHashes The Bearer access token SHA-256 hashes (in hex).
* If {@code null} access to the web API will be
* disabled.
*/
public SHA256BasedAccessTokenValidator(final String ... tokenHashes) {
for (String hash: tokenHashes) {
if (hash == null) continue;
try {
expectedTokenHashes.add(Hex.decodeHex(hash.toCharArray()));
} catch (DecoderException e) {
throw new IllegalArgumentException("Invalid hex for access token SHA-256: " + hash);
}
}
hashSalt = null;
}
/**
* Creates a new access token validator.
*
* @param tokenHash The main Bearer access token SHA-256
* hash (in hex). If {@code null} access
* to the web API will be disabled.
* @param additionalTokenHashes Additional Bearer access token SHA-256
* hashes (in hex), empty or {@code null}
* if none.
*/
public SHA256BasedAccessTokenValidator(final String tokenHash, final List additionalTokenHashes) {
if (tokenHash == null) {
return;
}
try {
expectedTokenHashes.add(Hex.decodeHex(tokenHash.toCharArray()));
} catch (DecoderException e) {
throw new IllegalArgumentException("Invalid hex for access token SHA-256: " + tokenHash);
}
if (additionalTokenHashes != null) {
for (String hash: additionalTokenHashes) {
if (hash != null) {
try {
expectedTokenHashes.add(Hex.decodeHex(hash.toCharArray()));
} catch (DecoderException e) {
throw new IllegalArgumentException("Invalid hex for access token SHA-256: " + hash);
}
}
}
}
}
/**
* Creates a new access token validator from the specified properties
* retriever.
*
* @param pr The properties retriever. Must
* not be {@code null}.
* @param propertyName The property name for the main
* Bearer access token SHA-256 hash
* (in hex). If {@code null} access
* to the web API will be disabled.
* Must not be {@code null}.
* @param propertyRequired {@code true} if the property is
* required, {@code false} if
* optional.
* @param additionalPropertyNamePrefix The property name prefix for the
* additional Bearer access token
* SHA-256 hashes (in hex),
* {@code null} if not used.
*
* @return The access token validator.
*
* @throws PropertyParseException If parsing failed.
*/
public static SHA256BasedAccessTokenValidator from(final PropertyRetriever pr,
final String propertyName,
final boolean propertyRequired,
final String additionalPropertyNamePrefix)
throws PropertyParseException {
String tokenHash;
if (propertyRequired) {
tokenHash = pr.getString(propertyName);
} else {
tokenHash = pr.getOptString(propertyName, null);
}
if (tokenHash != null) {
ensureNoDemoInProductionEnv(propertyName, Collections.singletonList(tokenHash));
}
if (additionalPropertyNamePrefix == null) {
return new SHA256BasedAccessTokenValidator(tokenHash);
}
List additionalTokenHashes = pr.getOptStringListMulti(additionalPropertyNamePrefix, Collections.emptyList());
ensureNoDemoInProductionEnv(propertyName, additionalTokenHashes);
return new SHA256BasedAccessTokenValidator(tokenHash, additionalTokenHashes);
}
private static void ensureNoDemoInProductionEnv(final String propertyName, final List tokenHashes)
throws PropertyParseException {
if (tokenHashes == null || tokenHashes.isEmpty()) {
return;
}
final boolean isProductionEnv = "prod".equals(System.getProperties().getProperty("env", "dev"));
if (! isProductionEnv) {
return;
}
for (String tokenHash: tokenHashes) {
if (tokenHash == null) {
continue;
}
if (DEMO_ACCESS_TOKEN_HASH.equals(tokenHash)) {
throw new PropertyParseException(
"The web API is configured with the demo access token, " +
"generate a new token for production use", propertyName);
}
}
}
@Override
public void validateBearerAccessToken(final String authzHeader)
throws WebApplicationException {
// Web API disabled?
if (accessIsDisabled()) {
throw WEB_API_DISABLED.toWebAppException();
}
if (StringUtils.isBlank(authzHeader)) {
throw MISSING_BEARER_TOKEN.toWebAppException();
}
BearerAccessToken receivedToken;
try {
receivedToken = BearerAccessToken.parse(authzHeader);
} catch (ParseException e) {
throw MISSING_BEARER_TOKEN.toWebAppException();
}
if (null != log) {
log.trace("[CM3000] Validating bearer access token: {}", TokenAbbreviator.abbreviate(receivedToken));
}
// Check min length
if (receivedToken.getValue().length() < MIN_TOKEN_LENGTH) {
throw INVALID_BEARER_TOKEN.toWebAppException();
}
if (isValid(receivedToken)) {
return; // pass
}
throw INVALID_BEARER_TOKEN.toWebAppException();
}
@Override
public boolean validateBearerAccessToken(final HttpServletRequest servletRequest,
final HttpServletResponse servletResponse)
throws IOException {
// Web API disabled?
if (accessIsDisabled()) {
WEB_API_DISABLED.apply(servletResponse);
return false;
}
BearerAccessToken receivedToken;
if (servletRequest.getHeader("Authorization") != null) {
String authzHeaderValue = servletRequest.getHeader("Authorization");
if (StringUtils.isBlank(authzHeaderValue)) {
MISSING_BEARER_TOKEN.apply(servletResponse);
return false;
}
try {
receivedToken = BearerAccessToken.parse(authzHeaderValue);
} catch (ParseException e) {
MISSING_BEARER_TOKEN.apply(servletResponse);
return false;
}
} else if (servletRequest.getParameter("access_token") != null) {
String accessTokenValue = servletRequest.getParameter("access_token");
if (StringUtils.isBlank(accessTokenValue)) {
MISSING_BEARER_TOKEN.apply(servletResponse);
return false;
}
receivedToken = new BearerAccessToken(accessTokenValue);
} else {
MISSING_BEARER_TOKEN.apply(servletResponse);
return false;
}
if (null != log) {
log.trace("[CM3000] Validating bearer access token: {}", TokenAbbreviator.abbreviate(receivedToken));
}
// Check min length
if (receivedToken.getValue().length() < MIN_TOKEN_LENGTH) {
INVALID_BEARER_TOKEN.apply(servletResponse);
return false;
}
// Compare hashes
if (isValid(receivedToken)) {
return true; // pass
}
INVALID_BEARER_TOKEN.apply(servletResponse);
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy