diff --git a/src/main/java/com/auth0/AlgorithmNameVerifier.java b/src/main/java/com/auth0/AlgorithmNameVerifier.java
deleted file mode 100644
index 2779b80..0000000
--- a/src/main/java/com/auth0/AlgorithmNameVerifier.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.auth0;
-
-@SuppressWarnings("unused")
-class AlgorithmNameVerifier extends SignatureVerifier {
-
- AlgorithmNameVerifier() {
- //Must only allow supported algorithms and never "none" algorithm
- super(null, "HS256", "RS256");
- }
-}
diff --git a/src/main/java/com/auth0/AsymmetricSignatureVerifier.java b/src/main/java/com/auth0/AsymmetricSignatureVerifier.java
deleted file mode 100644
index b8b82b4..0000000
--- a/src/main/java/com/auth0/AsymmetricSignatureVerifier.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.auth0;
-
-import com.auth0.jwk.Jwk;
-import com.auth0.jwk.JwkException;
-import com.auth0.jwk.JwkProvider;
-import com.auth0.jwt.JWT;
-import com.auth0.jwt.JWTVerifier;
-import com.auth0.jwt.algorithms.Algorithm;
-import com.auth0.jwt.interfaces.RSAKeyProvider;
-
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-
-@SuppressWarnings("unused")
-class AsymmetricSignatureVerifier extends SignatureVerifier {
-
- AsymmetricSignatureVerifier(JwkProvider jwkProvider) {
- super(createJWTVerifier(jwkProvider), "RS256");
- }
-
- private static JWTVerifier createJWTVerifier(final JwkProvider jwkProvider) {
- Algorithm alg = Algorithm.RSA256(new RSAKeyProvider() {
- @Override
- public RSAPublicKey getPublicKeyById(String keyId) {
- try {
- Jwk jwk = jwkProvider.get(keyId);
- return (RSAPublicKey) jwk.getPublicKey();
- } catch (JwkException ignored) {
- // JwkException handled by Algorithm verify implementation from java-jwt
- }
- return null;
- }
-
- @Override
- public RSAPrivateKey getPrivateKey() {
- //NO-OP
- return null;
- }
-
- @Override
- public String getPrivateKeyId() {
- //NO-OP
- return null;
- }
- });
- return JWT.require(alg)
- .ignoreIssuedAt()
- .build();
- }
-}
diff --git a/src/main/java/com/auth0/AuthenticationController.java b/src/main/java/com/auth0/AuthenticationController.java
index 8a14c9a..c768765 100644
--- a/src/main/java/com/auth0/AuthenticationController.java
+++ b/src/main/java/com/auth0/AuthenticationController.java
@@ -1,6 +1,5 @@
package com.auth0;
-import com.auth0.client.auth.AuthAPI;
import com.auth0.jwk.JwkProvider;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.Validate;
@@ -256,19 +255,20 @@ public AuthenticationController build() throws UnsupportedOperationException {
? new StaticDomainProvider(domain)
: new ResolverDomainProvider(domainResolver);
- SignatureVerifier signatureVerifier = buildSignatureVerifier();
-
- RequestProcessor processor = new RequestProcessor.Builder(domainProvider, responseType, clientId,
- clientSecret, signatureVerifier)
+ RequestProcessor.Builder builder = new RequestProcessor.Builder(
+ domainProvider, responseType, clientId, clientSecret)
.withClockSkew(clockSkew)
.withAuthenticationMaxAge(authenticationMaxAge)
.withLegacySameSiteCookie(useLegacySameSiteCookie)
.withOrganization(organization)
.withInvitation(invitation)
- .withCookiePath(cookiePath)
- .build();
+ .withCookiePath(cookiePath);
+
+ if (jwkProvider != null) {
+ builder.withJwkProvider(jwkProvider);
+ }
- return new AuthenticationController(processor);
+ return new AuthenticationController(builder.build());
}
private void validateDomainConfiguration() {
@@ -279,31 +279,6 @@ private void validateDomainConfiguration() {
throw new IllegalStateException("Cannot specify both domain and domainResolver.");
}
}
-
- private SignatureVerifier buildSignatureVerifier() {
- if (jwkProvider != null) {
- return new AsymmetricSignatureVerifier(jwkProvider);
- }
- if (responseType.contains(RESPONSE_TYPE_CODE)) {
- return new AlgorithmNameVerifier(); // legacy behavior
- }
- return new SymmetricSignatureVerifier(clientSecret);
- }
-
- @VisibleForTesting
- IdTokenVerifier.Options createIdTokenVerificationOptions(String issuer, String audience, SignatureVerifier signatureVerifier) {
- return new IdTokenVerifier.Options(issuer, audience, signatureVerifier);
- }
-
- private String getIssuer(String domain) {
- if (!domain.startsWith("http://") && !domain.startsWith("https://")) {
- domain = "https://" + domain;
- }
- if (!domain.endsWith("/")) {
- domain = domain + "/";
- }
- return domain;
- }
}
/**
diff --git a/src/main/java/com/auth0/IdTokenVerifier.java b/src/main/java/com/auth0/IdTokenVerifier.java
deleted file mode 100644
index 3ef0e32..0000000
--- a/src/main/java/com/auth0/IdTokenVerifier.java
+++ /dev/null
@@ -1,202 +0,0 @@
-package com.auth0;
-
-import com.auth0.jwt.interfaces.DecodedJWT;
-import org.apache.commons.lang3.Validate;
-
-import java.util.Calendar;
-import java.util.Date;
-import java.util.List;
-
-/**
- * Token verification utility class.
- * Supported signing algorithms: HS256 and RS256
- */
-class IdTokenVerifier {
-
- private static final Integer DEFAULT_CLOCK_SKEW = 60; //1 min = 60 sec
-
- private static final String NONCE_CLAIM = "nonce";
- private static final String AZP_CLAIM = "azp";
- private static final String AUTH_TIME_CLAIM = "auth_time";
-
- /**
- * Verifies a provided ID Token follows the OIDC specification.
- * @see Open ID Connect Specification
- *
- * @param token the ID Token to verify.
- * @param verifyOptions the verification options, like audience, issuer, algorithm.
- * @throws TokenValidationException If the ID Token is null, its signing algorithm not supported, its signature invalid or one of its claim invalid.
- */
- void verify(String token, Options verifyOptions) throws TokenValidationException {
- Validate.notNull(verifyOptions);
-
- if (isEmpty(token)) {
- throw new TokenValidationException("ID token is required but missing");
- }
-
- DecodedJWT decoded = verifyOptions.verifier.verifySignature(token);
-
- if (isEmpty(decoded.getIssuer())) {
- throw new TokenValidationException("Issuer (iss) claim must be a string present in the ID token");
- }
- if (!decoded.getIssuer().equals(verifyOptions.issuer)) {
- throw new TokenValidationException(String.format("Issuer (iss) claim mismatch in the ID token, expected \"%s\", found \"%s\"", verifyOptions.issuer, decoded.getIssuer()));
- }
-
- if (isEmpty(decoded.getSubject())) {
- throw new TokenValidationException("Subject (sub) claim must be a string present in the ID token");
- }
-
- final List audience = decoded.getAudience();
- if (audience == null) {
- throw new TokenValidationException("Audience (aud) claim must be a string or array of strings present in the ID token");
- }
- if (!audience.contains(verifyOptions.audience)) {
- throw new TokenValidationException(String.format("Audience (aud) claim mismatch in the ID token; expected \"%s\" but found \"%s\"", verifyOptions.audience, decoded.getAudience()));
- }
-
- // validate org if set
- if (verifyOptions.organization != null) {
- String org = verifyOptions.organization.trim();
- if (org.startsWith("org_")) {
- // org ID
- String orgIdClaim = decoded.getClaim("org_id").asString();
- if (isEmpty(orgIdClaim)) {
- throw new TokenValidationException("Organization Id (org_id) claim must be a string present in the ID token");
- }
- if (!org.equals(orgIdClaim)) {
- throw new TokenValidationException(String.format("Organization (org_id) claim mismatch in the ID token; expected \"%s\" but found \"%s\"", verifyOptions.organization, orgIdClaim));
- }
- }
- else {
- // org name
- String orgNameClaim = decoded.getClaim("org_name").asString();
- if (isEmpty(orgNameClaim)) {
- throw new TokenValidationException("Organization name (org_name) claim must be a string present in the ID token");
- }
- if (!org.toLowerCase().equals(orgNameClaim)) {
- throw new TokenValidationException(String.format("Organization (org_name) claim mismatch in the ID token; expected \"%s\" but found \"%s\"", verifyOptions.organization, orgNameClaim));
- }
- }
- }
-
- // TODO refactor to modern date/time APIs
- final Calendar cal = Calendar.getInstance();
- final Date now = verifyOptions.clock != null ? verifyOptions.clock : cal.getTime();
- final int clockSkew = verifyOptions.clockSkew != null ? verifyOptions.clockSkew : DEFAULT_CLOCK_SKEW;
-
- if (decoded.getExpiresAt() == null) {
- throw new TokenValidationException("Expiration Time (exp) claim must be a number present in the ID token");
- }
-
- cal.setTime(decoded.getExpiresAt());
- cal.add(Calendar.SECOND, clockSkew);
- Date expDate = cal.getTime();
-
- if (now.after(expDate)) {
- throw new TokenValidationException(String.format("Expiration Time (exp) claim error in the ID token; current time (%d) is after expiration time (%d)", now.getTime() / 1000, expDate.getTime() / 1000));
- }
-
- if (decoded.getIssuedAt() == null) {
- throw new TokenValidationException("Issued At (iat) claim must be a number present in the ID token");
- }
-
- cal.setTime(decoded.getIssuedAt());
- cal.add(Calendar.SECOND, -1 * clockSkew);
-
- if (verifyOptions.nonce != null) {
- String nonceClaim = decoded.getClaim(NONCE_CLAIM).asString();
- if (isEmpty(nonceClaim)) {
- throw new TokenValidationException("Nonce (nonce) claim must be a string present in the ID token");
- }
- if (!verifyOptions.nonce.equals(nonceClaim)) {
- throw new TokenValidationException(String.format("Nonce (nonce) claim mismatch in the ID token; expected \"%s\", found \"%s\"", verifyOptions.nonce, nonceClaim));
- }
- }
-
- if (audience.size() > 1) {
- String azpClaim = decoded.getClaim(AZP_CLAIM).asString();
- if (isEmpty(azpClaim)) {
- throw new TokenValidationException("Authorized Party (azp) claim must be a string present in the ID token when Audience (aud) claim has multiple values");
- }
- if (!verifyOptions.audience.equals(azpClaim)) {
- throw new TokenValidationException(String.format("Authorized Party (azp) claim mismatch in the ID token; expected \"%s\", found \"%s\"", verifyOptions.audience, azpClaim));
- }
- }
-
- if (verifyOptions.maxAge != null) {
- Date authTime = decoded.getClaim(AUTH_TIME_CLAIM).asDate();
- if (authTime == null) {
- throw new TokenValidationException("Authentication Time (auth_time) claim must be a number present in the ID token when Max Age (max_age) is specified");
- }
-
- cal.setTime(authTime);
- cal.add(Calendar.SECOND, verifyOptions.maxAge);
- cal.add(Calendar.SECOND, clockSkew);
- Date authTimeDate = cal.getTime();
-
- if (now.after(authTimeDate)) {
- throw new TokenValidationException(String.format("Authentication Time (auth_time) claim in the ID token indicates that too much time has passed since the last end-user authentication. Current time (%d) is after last auth at (%d)", now.getTime() / 1000, authTimeDate.getTime() / 1000));
- }
- }
- }
-
- private boolean isEmpty(String value) {
- return value == null || value.isEmpty();
- }
-
- static class Options {
- String issuer;
- final String audience;
- final SignatureVerifier verifier;
- String nonce;
- private Integer maxAge;
- Integer clockSkew;
- Date clock;
- String organization;
-
- public Options(String issuer, String audience, SignatureVerifier verifier) {
- Validate.notNull(issuer, "Issuer must not be null");
- Validate.notNull(audience, "Audience must not be null");
- Validate.notNull(verifier, "SignatureVerifier must not be null");
- this.issuer = issuer;
- this.audience = audience;
- this.verifier = verifier;
- }
-
- public Options(String audience, SignatureVerifier verifier) {
- Validate.notNull(audience, "Audience must not be null");
- Validate.notNull(verifier, "SignatureVerifier must not be null");
- this.audience = audience;
- this.verifier = verifier;
- }
-
- void setIssuer(String issuer) {
- this.issuer = issuer;
- }
-
- void setNonce(String nonce) {
- this.nonce = nonce;
- }
-
- void setMaxAge(Integer maxAge) {
- this.maxAge = maxAge;
- }
-
- void setClockSkew(Integer clockSkew) {
- this.clockSkew = clockSkew;
- }
-
- void setClock(Date now) {
- this.clock = now;
- }
-
- Integer getMaxAge() {
- return maxAge;
- }
-
- void setOrganization(String organization) {
- this.organization = organization;
- }
- }
-}
diff --git a/src/main/java/com/auth0/RequestProcessor.java b/src/main/java/com/auth0/RequestProcessor.java
index 0919da6..3b86d77 100644
--- a/src/main/java/com/auth0/RequestProcessor.java
+++ b/src/main/java/com/auth0/RequestProcessor.java
@@ -3,14 +3,26 @@
import com.auth0.client.LoggingOptions;
import com.auth0.client.auth.AuthAPI;
import com.auth0.exception.Auth0Exception;
+import com.auth0.exception.IdTokenValidationException;
+import com.auth0.exception.PublicKeyProviderException;
+import com.auth0.jwt.JWT;
import com.auth0.json.auth.TokenHolder;
+import com.auth0.jwk.Jwk;
+import com.auth0.jwk.JwkException;
+import com.auth0.jwk.JwkProvider;
+import com.auth0.jwk.UrlJwkProvider;
import com.auth0.net.client.DefaultHttpClient;
+import com.auth0.utils.tokens.IdTokenVerifier;
+import com.auth0.utils.tokens.SignatureVerifier;
import org.apache.commons.lang3.Validate;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import static com.auth0.InvalidRequestException.*;
@@ -33,34 +45,32 @@ class RequestProcessor {
private static final String KEY_FORM_POST = "form_post";
private static final String KEY_MAX_AGE = "max_age";
- // Visible for testing
-
private final DomainProvider domainProvider;
private final String responseType;
private final String clientId;
private final String clientSecret;
- private SignatureVerifier signatureVerifier;
+ private final JwkProvider jwkProvider;
- // Configuration values passed from Builder for creating per-request
- // verification options
private final Integer clockSkew;
private final Integer authenticationMaxAge;
private final String organization;
private final String invitation;
final boolean useLegacySameSiteCookie;
- private final IdTokenVerifier tokenVerifier;
private final String cookiePath;
private boolean loggingEnabled = false;
private boolean telemetryDisabled = false;
+ // Cache JwkProviders per domain for MCD support
+ private final ConcurrentMap jwkProviders = new ConcurrentHashMap<>();
+
static class Builder {
private final DomainProvider domainProvider;
private final String responseType;
private final String clientId;
private final String clientSecret;
- private final SignatureVerifier signatureVerifier;
+ private JwkProvider jwkProvider;
private boolean useLegacySameSiteCookie = true;
private Integer clockSkew;
private Integer authenticationMaxAge;
@@ -71,13 +81,16 @@ static class Builder {
public Builder(DomainProvider domainProvider,
String responseType,
String clientId,
- String clientSecret,
- SignatureVerifier signatureVerifier) {
+ String clientSecret) {
this.domainProvider = domainProvider;
this.responseType = responseType;
this.clientId = clientId;
this.clientSecret = clientSecret;
- this.signatureVerifier = signatureVerifier;
+ }
+
+ Builder withJwkProvider(JwkProvider jwkProvider) {
+ this.jwkProvider = jwkProvider;
+ return this;
}
public Builder withClockSkew(Integer clockSkew) {
@@ -111,25 +124,22 @@ Builder withInvitation(String invitation) {
}
RequestProcessor build() {
-
return new RequestProcessor(domainProvider, responseType, clientId, clientSecret,
- signatureVerifier, new IdTokenVerifier(),
- useLegacySameSiteCookie, clockSkew, authenticationMaxAge, organization, invitation, cookiePath);
+ jwkProvider, useLegacySameSiteCookie, clockSkew, authenticationMaxAge,
+ organization, invitation, cookiePath);
}
}
- private RequestProcessor(DomainProvider domainProvider, String responseType, String clientId, String clientSecret, SignatureVerifier signatureVerifier, IdTokenVerifier tokenVerifier,
+ private RequestProcessor(DomainProvider domainProvider, String responseType, String clientId,
+ String clientSecret, JwkProvider jwkProvider,
boolean useLegacySameSiteCookie, Integer clockSkew, Integer authenticationMaxAge,
String organization, String invitation, String cookiePath) {
this.domainProvider = domainProvider;
this.responseType = responseType;
this.clientId = clientId;
this.clientSecret = clientSecret;
- this.signatureVerifier = signatureVerifier;
- this.tokenVerifier = tokenVerifier;
+ this.jwkProvider = jwkProvider;
this.useLegacySameSiteCookie = useLegacySameSiteCookie;
-
- // Store individual configuration values instead of pre-built verifyOptions
this.clockSkew = clockSkew;
this.authenticationMaxAge = authenticationMaxAge;
this.organization = organization;
@@ -245,7 +255,7 @@ static boolean requiresFormPostResponseMode(List responseType) {
* Obtains code request tokens (if using Code flow) and validates the ID token.
* @param request the HTTP request
* @param frontChannelTokens the tokens obtained from the front channel
- * @param responseTypeList the reponse types
+ * @param responseTypeList the response types
* @return a Tokens object that wraps the values obtained from the front-channel and/or the code request response.
* @throws IdentityVerificationException
*/
@@ -257,14 +267,10 @@ private Tokens getVerifiedTokens(HttpServletRequest request, HttpServletResponse
String nonce = TransientCookieStore.getNonce(request, response);
- IdTokenVerifier.Options requestVerifyOptions = createRequestVerifyOptions(originIssuer, nonce);
-
try {
if (responseTypeList.contains(KEY_ID_TOKEN)) {
// Implicit/Hybrid flow: must verify front-channel ID Token first.
- // The issuer is derived from the HMAC-verified domain, so this check
- // validates the token's iss against a trusted value.
- tokenVerifier.verify(frontChannelTokens.getIdToken(), requestVerifyOptions);
+ verifyIdToken(frontChannelTokens.getIdToken(), originIssuer, originDomain, nonce);
}
if (responseTypeList.contains(KEY_CODE)) {
// Code/Hybrid flow
@@ -274,11 +280,11 @@ private Tokens getVerifiedTokens(HttpServletRequest request, HttpServletResponse
// If we already verified the front-channel token, don't verify it again.
String idTokenFromCodeExchange = codeExchangeTokens.getIdToken();
if (idTokenFromCodeExchange != null) {
- tokenVerifier.verify(idTokenFromCodeExchange, requestVerifyOptions);
+ verifyIdToken(idTokenFromCodeExchange, originIssuer, originDomain, nonce);
}
}
}
- } catch (TokenValidationException e) {
+ } catch (IdTokenValidationException e) {
throw new IdentityVerificationException(JWT_VERIFICATION_ERROR, "An error occurred while trying to verify the ID Token.", e);
} catch (Auth0Exception e) {
throw new IdentityVerificationException(API_ERROR, "An error occurred while exchanging the authorization code.", e);
@@ -288,27 +294,60 @@ private Tokens getVerifiedTokens(HttpServletRequest request, HttpServletResponse
}
/**
- * Creates per-request verification options to avoid thread safety issues.
- * This creates fresh options from the stored configuration values.
+ * Verifies an ID token using auth0-java v3's IdTokenVerifier.
+ * The signature verification strategy is determined by the token's alg header:
+ * - RS256: uses JwkProvider (customer-provided or auto-discovered per domain)
+ * - HS256: uses client secret
*/
- private IdTokenVerifier.Options createRequestVerifyOptions(String issuer, String nonce) {
- // Create fresh verification options for this specific request
- IdTokenVerifier.Options requestOptions = new IdTokenVerifier.Options(clientId, signatureVerifier);
+ private void verifyIdToken(String idToken, String issuer, String domain, String nonce) throws IdTokenValidationException {
+ SignatureVerifier sigVerifier = buildSignatureVerifier(idToken, domain);
- requestOptions.setIssuer(issuer);
- requestOptions.setNonce(nonce);
+ IdTokenVerifier.Builder verifierBuilder = IdTokenVerifier.init(issuer, clientId, sigVerifier);
if (clockSkew != null) {
- requestOptions.setClockSkew(clockSkew);
- }
- if (authenticationMaxAge != null) {
- requestOptions.setMaxAge(authenticationMaxAge);
+ verifierBuilder.withLeeway(clockSkew);
}
if (organization != null) {
- requestOptions.setOrganization(organization);
+ verifierBuilder.withOrganization(organization);
}
- return requestOptions;
+ IdTokenVerifier verifier = verifierBuilder.build();
+ verifier.verify(idToken, nonce, authenticationMaxAge);
+ }
+
+ /**
+ * Builds the appropriate SignatureVerifier based on the token's algorithm header.
+ * - If alg is HS256: use client secret
+ * - If alg is RS256: use JwkProvider (customer-provided or auto-discovered from domain)
+ */
+ private SignatureVerifier buildSignatureVerifier(String idToken, String domain) {
+ String algorithm = JWT.decode(idToken).getAlgorithm();
+
+ if ("HS256".equals(algorithm)) {
+ return SignatureVerifier.forHS256(clientSecret);
+ }
+
+ // RS256 (default): use JwkProvider
+ JwkProvider provider = getJwkProvider(domain);
+ return SignatureVerifier.forRS256(keyId -> {
+ try {
+ Jwk jwk = provider.get(keyId);
+ return (RSAPublicKey) jwk.getPublicKey();
+ } catch (JwkException e) {
+ throw new PublicKeyProviderException("Failed to get public key for key ID: " + keyId, e);
+ }
+ });
+ }
+
+ /**
+ * Gets the JwkProvider for the given domain. If the customer provided one, it is used.
+ * Otherwise, a UrlJwkProvider is auto-created and cached per domain.
+ */
+ private JwkProvider getJwkProvider(String domain) {
+ if (jwkProvider != null) {
+ return jwkProvider;
+ }
+ return jwkProviders.computeIfAbsent(domain, d -> new UrlJwkProvider(d));
}
List getResponseType() {
diff --git a/src/main/java/com/auth0/SignatureVerifier.java b/src/main/java/com/auth0/SignatureVerifier.java
deleted file mode 100644
index 3d41df0..0000000
--- a/src/main/java/com/auth0/SignatureVerifier.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.auth0;
-
-import com.auth0.jwt.JWT;
-import com.auth0.jwt.JWTVerifier;
-import com.auth0.jwt.exceptions.JWTDecodeException;
-import com.auth0.jwt.exceptions.JWTVerificationException;
-import com.auth0.jwt.exceptions.SignatureVerificationException;
-import com.auth0.jwt.interfaces.DecodedJWT;
-import org.apache.commons.lang3.Validate;
-
-import java.util.Arrays;
-import java.util.List;
-
-abstract class SignatureVerifier {
-
- private final JWTVerifier verifier;
- private final List acceptedAlgorithms;
-
- /**
- * Creates a new JWT Signature Verifier.
- * This instance will validate the token was signed using an expected algorithm
- * and then proceed to verify its signature
- *
- * @param verifier the instance that knows how to verify the signature. When null, the signature will not be checked.
- * @param algorithm the accepted algorithms. Must never be null!
- */
- SignatureVerifier(JWTVerifier verifier, String... algorithm) {
- Validate.notEmpty(algorithm);
- this.verifier = verifier;
- this.acceptedAlgorithms = Arrays.asList(algorithm);
- }
-
- private DecodedJWT decodeToken(String token) throws TokenValidationException {
- try {
- return JWT.decode(token);
- } catch (JWTDecodeException e) {
- throw new TokenValidationException("ID token could not be decoded", e);
- }
- }
-
- DecodedJWT verifySignature(String token) throws TokenValidationException {
- DecodedJWT decoded = decodeToken(token);
- if (!this.acceptedAlgorithms.contains(decoded.getAlgorithm())) {
- throw new TokenValidationException(String.format("Signature algorithm of \"%s\" is not supported. Expected the ID token to be signed with \"%s\".", decoded.getAlgorithm(), this.acceptedAlgorithms));
- }
- if (verifier != null) {
- try {
- verifier.verify(decoded);
- } catch (SignatureVerificationException e) {
- throw new TokenValidationException("Invalid token signature", e);
- } catch (JWTVerificationException ignored) {
- //NO-OP. Will be catch on a different step
- //Would only trigger for "expired tokens" (invalid exp)
- }
- }
-
- return decoded;
- }
-}
diff --git a/src/main/java/com/auth0/SymmetricSignatureVerifier.java b/src/main/java/com/auth0/SymmetricSignatureVerifier.java
deleted file mode 100644
index 81b013f..0000000
--- a/src/main/java/com/auth0/SymmetricSignatureVerifier.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.auth0;
-
-import com.auth0.jwt.JWT;
-import com.auth0.jwt.JWTVerifier;
-import com.auth0.jwt.algorithms.Algorithm;
-
-@SuppressWarnings("unused")
-class SymmetricSignatureVerifier extends SignatureVerifier {
-
- SymmetricSignatureVerifier(String secret) {
- super(createJWTVerifier(secret), "HS256");
- }
-
- private static JWTVerifier createJWTVerifier(String secret) {
- Algorithm alg = Algorithm.HMAC256(secret);
- return JWT.require(alg)
- .ignoreIssuedAt()
- .build();
- }
-}
diff --git a/src/main/java/com/auth0/TokenValidationException.java b/src/main/java/com/auth0/TokenValidationException.java
deleted file mode 100644
index e0616ac..0000000
--- a/src/main/java/com/auth0/TokenValidationException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.auth0;
-
-class TokenValidationException extends RuntimeException {
-
- TokenValidationException(String message) {
- super(message);
- }
-
- TokenValidationException(String message, Throwable cause) {
- super(message, cause);
- }
-}
diff --git a/src/test/java/com/auth0/IdTokenVerifierTest.java b/src/test/java/com/auth0/IdTokenVerifierTest.java
deleted file mode 100644
index ced54c7..0000000
--- a/src/test/java/com/auth0/IdTokenVerifierTest.java
+++ /dev/null
@@ -1,680 +0,0 @@
-package com.auth0;
-
-import com.auth0.jwt.JWT;
-import com.auth0.jwt.algorithms.Algorithm;
-import com.auth0.jwt.interfaces.DecodedJWT;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.util.Calendar;
-import java.util.Date;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class IdTokenVerifierTest {
-
- private final static String DOMAIN = "tokens-test.auth0.com";
- private final static String AUDIENCE = "tokens-test-123";
-
- // Default clock time of September 2, 2019 5:00:00 AM GMT
- private final static Date DEFAULT_CLOCK = new Date(1567400400000L);
- private final static Integer DEFAULT_CLOCK_SKEW = 60;
-
- private SignatureVerifier signatureVerifier;
-
- @BeforeEach
- public void setUp() {
- signatureVerifier = mock(SignatureVerifier.class);
- }
-
- @Test
- public void failsToCreateOptionsWhenIssuerIsNull() {
- assertThrows(NullPointerException.class,
- () -> new IdTokenVerifier.Options(null, "audience", signatureVerifier));
- }
-
- @Test
- public void failsToCreateOptionsWhenAudienceIsNull() {
- assertThrows(NullPointerException.class,
- () -> new IdTokenVerifier.Options("issuer", null, signatureVerifier));
- }
-
- @Test
- public void failsToCreateOptionsWhenVerifierIsNull() {
- assertThrows(NullPointerException.class,
- () -> new IdTokenVerifier.Options("issuer", "audience", null));
- }
-
- @Test
- public void failsWhenIDTokenMissing() {
- IdTokenVerifier.Options opts = new IdTokenVerifier.Options("issuer", "audience", signatureVerifier);
- IdTokenVerifier verifier = new IdTokenVerifier();
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> verifier.verify(null, opts));
- assertEquals("ID token is required but missing", e.getMessage());
- }
-
- @Test
- public void failsWhenIDTokenEmpty() {
- IdTokenVerifier.Options opts = new IdTokenVerifier.Options("issuer", "audience", signatureVerifier);
- IdTokenVerifier verifier = new IdTokenVerifier();
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> verifier.verify("", opts));
- assertEquals("ID token is required but missing", e.getMessage());
- }
-
- @Test
- public void failsWhenOptionsIsNull() {
- assertThrows(NullPointerException.class, () -> new IdTokenVerifier().verify("token", null));
- }
-
- @Test
- public void failsWhenTokenCannotBeDecoded() {
- String token = "boom!";
-
- SignatureVerifier signatureVerifier = new SymmetricSignatureVerifier("secret");
- IdTokenVerifier.Options opts = new IdTokenVerifier.Options(DOMAIN, AUDIENCE, signatureVerifier);
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, opts));
- assertEquals("ID token could not be decoded", e.getMessage());
- }
-
- @Test
- public void failsWhenSignatureIsInvalid() {
- String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub25jZSI6IjEyMzQiLCJpc3MiOiJodHRwczovL21lLmF1dGgwLmNvbS8iLCJhdWQiOiJkYU9nbkdzUlloa3d1NjIxdmYiLCJzdWIiOiJhdXRoMHx1c2VyMTIzIn0.a7ayNmFTxS2D-EIoUikoJ6dck7I8veWyxnje_mYD3qY";
-
- SignatureVerifier verifier = new SymmetricSignatureVerifier("asdlk59ckvkr");
- IdTokenVerifier.Options opts = new IdTokenVerifier.Options(DOMAIN, AUDIENCE, verifier);
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, opts));
- assertEquals("Invalid token signature", e.getMessage());
- }
-
- @Test
- public void failsWhenIssuerMissing() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.B4PGlucyy-fJ4v5NNK2hntvjAf5m8dJf84WttwVnzV0ZlfPbYUSJm7Vc1ys7iMqXAQzAl2I8bDf2qhtLjaLpDKAH9JUvowUpCL7Bgjd7AEc1Te_IUwwxlpCupgseOEL2nrY8enP6On7BO7BBpngmVwnD1DvuA4lNoaaFyWUopha5Dxd5jw64wMqP4lz13C6Kqs8mINZkkw-NgE8DvWszaXeyPaowy-QpfXmPBnw75YLZlGcjr-WQsWQV7rUezq4Tl_11uPivR-fNcEWdG1mAtsnQnB_zJJKaHYlE0g4fey_6H9FKmCvcNkpBGo9ylbitb7jIuExbFEvEd2r_4wKl0g";
- IdTokenVerifier.Options options = configureOptions(token);
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals("Issuer (iss) claim must be a string present in the ID token", e.getMessage());
- }
-
- @Test
- public void failsWhenIssuerInvalid() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJzb21ldGhpbmctZWxzZSIsInN1YiI6ImF1dGgwfDEyMzQ1Njc4OSIsImF1ZCI6WyJ0b2tlbnMtdGVzdC0xMjMiLCJleHRlcm5hbC10ZXN0LTEyMyJdLCJleHAiOjE1Njc0ODY4MDAsImlhdCI6MTU2NzMxNDAwMCwibm9uY2UiOiJhNTl2azU5MiIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU2NzMxNDAwMH0.lHFHyg1ei3hK2vB7X1xB9nqksAEnxtv2KKpE_Gih6RezTruF9uZu1PAZTEwxhfj2UrQxwLqCb-t6wyVnxVpCsymSCq9JIiCVgg_cYV38siMs38N9y26BrVeyifj_VOP9Om_vI_hHjOzhi8WmysK2KKAQnn0skKAkq8epY4axCX3NkRaEIMhhTaITYia3GbJ5Qki8WDD9UVucUVOhgSZBV5p1dL39FKgc9k1MOVZJG-zAd_r5GsUIRk-xUwNX0WYwCR9sC2G-FjJTvlFph_4vksponoUWJ-LPTLM0RwGgmEUPhhnIG23UjsNwpnElY4gWfIL0hsO98-5DpGjn8Ejr0w";
- IdTokenVerifier.Options options = configureOptions(token);
-
- String errorMessage = String.format("Issuer (iss) claim mismatch in the ID token, expected \"%s\", found \"%s\"",
- "https://" + DOMAIN + "/", "something-else");
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals(errorMessage, e.getMessage());
- }
-
- @Test
- public void failsWhenSubMissing() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.fDR9NSbbt75w9nzhL-eBfGjOp16HP2vfnO6m_Oav0xrmmgyYsBZSLOPd2C0O46bp6_2hKjeOUhnwYwjocsdXI4hvfQkyACERtneCkwHwSZPZK-1h6vgGF7b_7ILUywEcgo7F6e1qgFTM93Prqk63cCP53KgOBPyx02y0rqkhUOApCWRVBFrfP92tXvFN7E2phmpf9G68PPjwnEvvQtYOGjvFkaWSja7MKT98f7OxgbenBI_mAZy9LmOqUl3SKJOBe5Fibs1snI0l4nzrgQ1GNxVwyfHOdyq-srdGe8rlFx5kdhWh313EOzWxxGTg4RhGY7Tiz1QWago0VQ5yOt0w8A";
- IdTokenVerifier.Options options = configureOptions(token);
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals("Subject (sub) claim must be a string present in the ID token", e.getMessage());
- }
-
- @Test
- public void failsWhenAudienceMissing() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJleHAiOjE1Njc0ODY4MDAsImlhdCI6MTU2NzMxNDAwMCwibm9uY2UiOiJhNTl2azU5MiIsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU2NzMxNDAwMH0.XM-IM9CIZ2cJpZZaKooMSmNgvwHPTse6kcIOPATgewRZxrDdCEjtPHmzmSuyDGy84vSR__DJS_kM2jWWwbkjB_PahXes210dpUqitRW3is9xV0-k0LkVwxmhHCM-e9sClbTbcs4zLv6WWFRq4UEU5DU6HhuHLQeeH0eO2Nv_tkvu-JdpmoepHPjW3ecMs0lhzXRT6_2o-ErTPdYt4W6yqpBG57HRIMzs9F72AWcPC6vhLY0IhMqXaq68Ma3jnEPIXUmv52bll0PuQVBqKd-eDH_jD0ZHFUCkwbfWPrkhJz5Q5qLzSzUjnrWKA3KgP4_Z1KfHY2-nQA2ynMgNFSn_eA";
- IdTokenVerifier.Options options = configureOptions(token);
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals("Audience (aud) claim must be a string or array of strings present in the ID token", e.getMessage());
- }
-
- @Test
- public void failsWhenAudienceDoesNotContainClientId() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOiJleHRlcm5hbC10ZXN0LTEyMyIsImV4cCI6MTU2NzQ4NjgwMCwiaWF0IjoxNTY3MzE0MDAwLCJub25jZSI6ImE1OXZrNTkyIiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTY3MzE0MDAwfQ.SxeNIhm8reywgtSSkZ6jCpbZ8KyC09couFjpcrJFktAYKmJZnGQkv0gQLNUuGejORvysznOlhfO2nkF10yT6pKBiye9xZ8TstWQBorDKHL-74n6ZAxjPg1F0vHNokZq0zpPkwV-gKIFY6aPw3vyZTxzR6CMyoJdwc19A0RXPzPt6T7csQeqX0lzGEqqeIbU4VI5XM5RG1VvN82CgTlOQXlFZrKhyJx_xwslyWWDzx7tpPNid1wusvfznTGxoWO2wUBCyW6EhmyHp2euFi1gdJqHQVbrydutPtQ-FGQEwyWACNN8kBWqQ7UEbqimg6C0NTGrRkkKkJ79DmiW7aULHZQ";
- IdTokenVerifier.Options options = configureOptions(token);
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals(String.format("Audience (aud) claim mismatch in the ID token; expected \"%s\" but found \"%s\"", AUDIENCE, "[external-test-123]"), e.getMessage());
- }
-
- @Test
- public void failsWhenExpClaimMissing() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiaWF0IjoxNTY3MzE0MDAwLCJub25jZSI6ImE1OXZrNTkyIiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTY3MzE0MDAwfQ.b6saYAZCnCSzpVO0nrAUKVSC1n3GoqUfwrjOXG5gVxda0oFohpYJe68QwzsTmS4fOm7JtbN1FqjVRN6S4i-BnH-XGnciGOMFF4EfaOzsgo7DCrrLrjfx6rmqW8UPYalbfJTQL8mXYnLOxzMGP3DEXNlk-41GSZoFujwTAIqYjrV_Y3MUGYmzcVxdL_h2psLm_p07knMLCm7Cuo8znzKrU4PtuaLflvzorg57S4BD79oLv4uv0_dmhwPUgJDvqWeicR5Qry4aX2L5BT6V-nBWAcu3qVZDymSKcjtTebxszxY1siyA7BQe88ZmgP1bW1KXtMk_fOGsgWHFdu_AH77yow";
- IdTokenVerifier.Options options = configureOptions(token);
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals("Expiration Time (exp) claim must be a number present in the ID token", e.getMessage());
- }
-
- @Test
- public void failsWhenExpClaimInvalidOutsideDefaultLeeway() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3MzE0MDAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.uDn-4wtiigGddUw2kis_QyfDE3w75rWvu9NolMgD3b7l4_fedhQOk-z_mYID588ZXpnpLRKKiD5I2IFsXl7Qcc10rx1LIZxNqdzyc3VrgFf677x7fFZ4guR2WalH-zdJEluruMRdCIFQczIjXnGKPHGQ8gPH1LRozv43dl-bO2viX6MU4pTgNq3GIsU4ureyHrx1o9JSqF4b_RzuYvVWVVX7ABC2csMJP_ocVbEIQjUBhp1V7VcQY-Zgq0prk_HvY13g8FxK4KvSza637ZWAfonn599SKuy22PeMJqDfd64SbunWrt-mKBz9PHeAo9t4LJPLsAqSd3IQ2aJTsnqJRA";
-
- Integer actualExpTime = 1567314000;
-
- // set clock to September 1, 2019 5:00:00 AM GMT
- Date clock = new Date(1567314000000L);
- clock.setTime(clock.getTime() + ((DEFAULT_CLOCK_SKEW + 1) * 1000));
-
- IdTokenVerifier.Options options = configureOptions(token);
- options.setClock(clock);
-
- String errorMessage = String.format("Expiration Time (exp) claim error in the ID token; current time (%d) is after expiration time (%d)",
- clock.getTime() / 1000, actualExpTime + DEFAULT_CLOCK_SKEW);
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals(errorMessage, e.getMessage());
- }
-
- @Test
- public void succeedsWhenExpClaimInPastButWithinDefaultLeeway() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3MzE0MDAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.uDn-4wtiigGddUw2kis_QyfDE3w75rWvu9NolMgD3b7l4_fedhQOk-z_mYID588ZXpnpLRKKiD5I2IFsXl7Qcc10rx1LIZxNqdzyc3VrgFf677x7fFZ4guR2WalH-zdJEluruMRdCIFQczIjXnGKPHGQ8gPH1LRozv43dl-bO2viX6MU4pTgNq3GIsU4ureyHrx1o9JSqF4b_RzuYvVWVVX7ABC2csMJP_ocVbEIQjUBhp1V7VcQY-Zgq0prk_HvY13g8FxK4KvSza637ZWAfonn599SKuy22PeMJqDfd64SbunWrt-mKBz9PHeAo9t4LJPLsAqSd3IQ2aJTsnqJRA";
-
- // set clock to September 1, 2019 5:00:00 AM GMT
- Date clock = new Date(1567314000000L);
- clock.setTime(clock.getTime() + ((DEFAULT_CLOCK_SKEW - 1) * 1000));
-
- IdTokenVerifier.Options options = configureOptions(token);
- options.setClock(clock);
-
- new IdTokenVerifier().verify(token, options);
- }
-
- @Test
- public void failsWhenExpClaimInvalidOutsideCustomLeeway() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3MzE0MDAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.uDn-4wtiigGddUw2kis_QyfDE3w75rWvu9NolMgD3b7l4_fedhQOk-z_mYID588ZXpnpLRKKiD5I2IFsXl7Qcc10rx1LIZxNqdzyc3VrgFf677x7fFZ4guR2WalH-zdJEluruMRdCIFQczIjXnGKPHGQ8gPH1LRozv43dl-bO2viX6MU4pTgNq3GIsU4ureyHrx1o9JSqF4b_RzuYvVWVVX7ABC2csMJP_ocVbEIQjUBhp1V7VcQY-Zgq0prk_HvY13g8FxK4KvSza637ZWAfonn599SKuy22PeMJqDfd64SbunWrt-mKBz9PHeAo9t4LJPLsAqSd3IQ2aJTsnqJRA";
- Integer leeway = 120;
-
- Date actualExp = JWT.decode(token).getExpiresAt();
-
- // set clock to September 1, 2019 5:00:00 AM GMT
- Date clock = new Date(1567314000000L);
- clock.setTime(clock.getTime() + ((leeway + 1) * 1000));
-
- IdTokenVerifier.Options options = configureOptions(token);
- options.setClockSkew(leeway);
- options.setClock(clock);
-
- String errorMessage = String.format("Expiration Time (exp) claim error in the ID token; current time (%d) is after expiration time (%d)",
- clock.getTime() / 1000, ((actualExp.getTime() / 1000) + leeway));
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals(errorMessage, e.getMessage());
- }
-
- @Test
- public void succeedsWhenExpClaimInPastButWithinCustomLeeway() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3MzE0MDAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.uDn-4wtiigGddUw2kis_QyfDE3w75rWvu9NolMgD3b7l4_fedhQOk-z_mYID588ZXpnpLRKKiD5I2IFsXl7Qcc10rx1LIZxNqdzyc3VrgFf677x7fFZ4guR2WalH-zdJEluruMRdCIFQczIjXnGKPHGQ8gPH1LRozv43dl-bO2viX6MU4pTgNq3GIsU4ureyHrx1o9JSqF4b_RzuYvVWVVX7ABC2csMJP_ocVbEIQjUBhp1V7VcQY-Zgq0prk_HvY13g8FxK4KvSza637ZWAfonn599SKuy22PeMJqDfd64SbunWrt-mKBz9PHeAo9t4LJPLsAqSd3IQ2aJTsnqJRA";
- Integer leeway = 120;
-
- // set clock to September 1, 2019 5:00:00 AM GMTExpiration Time (exp) claim error in the ID token; current time
- Date clock = new Date(1567314000000L);
- clock.setTime(clock.getTime() + ((leeway - 1) * 1000));
-
- IdTokenVerifier.Options options = configureOptions(token);
- options.setClockSkew(leeway);
- options.setClock(clock);
- new IdTokenVerifier().verify(token, options);
- }
-
- @Test
- public void failsWhenIatClaimMissing() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJub25jZSI6ImE1OXZrNTkyIiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTY3MzE0MDAwfQ.SJDgK8W9Y8stMtE9LG2OzHzXzbIDCXg8lRhKyOim4rRXbkg3k0on7gCzN-sy2d5z5TQ-lQzbY3V4z-so3ltVDUYd_8RjmUiKgNK_95UsxfTDM2BlBEQ6USMVl3ojC5jcTBhg5MF16ZBEn94IjIGC9Uks9GPseM-JrtUPx4Uj5VvsBtmeKxLc3rSGt7rYC4JU65Oa-O5pFYRSCbNzRFNHRlmnb5b2uPHxoVLjrJAT0FhlXcsNgfz65MlbXBgAyz7xjCEhw_tTpvptaCwPTeG0mgBYlGQ7Sl3xHJzgG4jLbA7Pvvfcx0MpBPHUZxADh1FFQnf2nHB0ppddiDfOq2mHNA";
-
- IdTokenVerifier.Options options = configureOptions(token);
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals("Issued At (iat) claim must be a number present in the ID token", e.getMessage());
- }
-
- @Test
- public void failsWhenNonceConfiguredButNoNonceClaimSent() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsImF6cCI6InRva2Vucy10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU2NzMxNDAwMH0.ZRYK4s72pKXJUSadByPp_MNyuaACmPCyj9RaIfxuTTLXE45YJ0toLK6XjjDv_861E_fRmEKMthnJAmHcKXiDWGb73l3iDtD7clockBOo3KJO2cwkM1uYNpG1kbNkg6WDvgGlVsC7buxr8dbL8fI2e0g53Jl48lE9Ohi5Z_7iRmRoVAx5HE60UDfEqFeAKZyu5VsAahp9q3PwhLfaJVDobtAzWP0LcRA3x8FOA0ZdBBNpvRmeBRugU2GQTSDLSMtGzgi5xXUwXly7pr5bX-lIYICU1Q9R5n-8uYlEaFuiaYTqzxY0fmSzzGeFkwrj7b0yTQ2OwAFVT3MWCSbvjKsy-JWQ";
- IdTokenVerifier.Options options = configureOptions(token);
- options.setNonce("kssllk59akth");
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals("Nonce (nonce) claim must be a string present in the ID token", e.getMessage());
- }
-
- @Test
- public void failsWhenNonceIsInvalid() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiMDAwOTk5IiwiYXpwIjoidG9rZW5zLXRlc3QtMTIzIiwiYXV0aF90aW1lIjoxNTY3MzE0MDAwfQ.n4jIX01mNucMs92F8IZtKJeCvgUYPwrrOsaZX91fnzVkDC5tAqi4HLRGHjtUJe1PwmIijJk63FskeuApVPfxfAbITL1KBVDHiin2RVeDSAl5lhSnsSYW-k5MfzXx11MJxhS_VD5zvOgbWmuRYUHlc1zh48YyJZQE-OaEFvxGyyEM7Zhgzfz4D5_kjd2qV890WsXGs_GadyzxATfP59XENnPzMo3VLXyBC4cQ0e7rzBIqquBKo9-MT6rhy_qSwMrZJhyzSzE5gTtMd2Od9YgPUtLznBt34rBD1uJaSs_a4s1Ox3h4jTCm85xWFabGx3kz7xkD33nCiMKQ_FSy1d-toQ";
-
- String expectedNonce = "nonce";
- String actualNonce = "000999";
-
- IdTokenVerifier.Options options = configureOptions(token);
- options.setNonce("nonce");
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals(String.format("Nonce (nonce) claim mismatch in the ID token; expected \"%s\", found \"%s\"", expectedNonce, actualNonce), e.getMessage());
- }
-
- @Test
- public void failsWhenAudClaimHasMultipleItemsButAzpMissing() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOjQyLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.SliF71jOX9JsGeUPCySf3ucY_tGr3uh183cbcUN9ze3qRiOAc5bi7vdsBtODtlVJgsx0Elt0JrISTJ8SoNkpA4SxrjFpxSsfzPBwQtJrlg7pqflgBH7g6zKGVGRs2Z0jxZaCvXQvRuUZRZwFIncZ2zTFIDI3X5xLeJAGRGWaInOvLLlumGzWzfNLUG_G5uHZQW6sRgyIw9qrdqEWXO6sGjOBG9Au6jIo2IH0I53-UujAnNHWeJRPsM5xw2bHPteIde1xn4N0w26BlZ4GEQifVQDFw3ukah35SQ-ENMMS58Siu-sysF5F3oxdwVaMidyYgrD2VUN_iXIaMPwA2i0M5Q";
-
- IdTokenVerifier.Options options = configureOptions(token);
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals("Authorized Party (azp) claim must be a string present in the ID token when Audience (aud) claim has multiple values", e.getMessage());
- }
-
- @Test
- public void failsWhenAudClaimHasMultipleItemsButAzpInvalid() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJleHRlcm5hbC10ZXN0LTEyMyIsImF1dGhfdGltZSI6MTU2NzMxNDAwMH0.GLuChuSum2S6h79rfRbJrJfe_7Fw_D6RHXj9zrAhixoNLMyBosO2GBPsOgoaLTDMonJzCyqskjan-w-SJ5nw7fUmDkWfPVjXcS0x5pt72j0dgfLMu6eOFIA9jWHWN4hsN3XKJktZ9202AohI8fXO5BYQ-jMi0HWQaiUj3f6wITHEN6fTydLo_t24hriExkO1670AgzM22BVTfb-JJlrs32t6ffY77zrF5ahIg_h4ROgrcf_3LejF7ZnubHbpJ-wX-byxW9YXT5tN_JjD5EP6jC37s9iL8ArGEZtBzHVfCO0kqlaH-9PVZXgz8SjMSJ8iA2fXXN0L35ySdzida3hhzw";
-
- String actualAzp = "external-test-123";
-
- IdTokenVerifier.Options options = configureOptions(token);
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals(String.format("Authorized Party (azp) claim mismatch in the ID token; expected \"%s\", found \"%s\"", AUDIENCE, actualAzp), e.getMessage());
- }
-
- @Test
- public void failsWhenMaxAgeSentButAuthTimeClaimMissing() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMifQ.Gb36qNHgQgac1fXh9AHX7ZMroymT0j4TjNol3ZirbIOyxuHV4OxCbGcoAAxC8Zt_dIc3DH9SX3QUIwTkE3DsFxS-VJ58R2d9RbXJl5p8pO1sJNFjo59njLKbiBxVil4z8PUsw77c_4f2QtKn6LHzhGqL9CS84LUCgNPPBsBHYyNRJDwIauPrrLyOsZAS3dWlZiUDBFurSYe0Y-O6d8zF_uKOcTD8A2E3SQQlZJQ12T94IprQ9V0tbbWI8VSGQ23JghR62QwZC-rBOF9pQMcLLCNRLFTTF9sXqZuS9XRv7PZ6rRjaonHDWn8WqGjSleWSycPsvwvjjSUVR8Z3iDBZig";
-
- IdTokenVerifier.Options options = configureOptions(token);
- options.setMaxAge(200);
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals("Authentication Time (auth_time) claim must be a number present in the ID token when Max Age (max_age) is specified", e.getMessage());
- }
-
- @Test
- public void failsWhenMaxSentButAuthTimeInvalidWithinLeeway() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.AbSYZ_Tu0-ZelCRPuu9jOd9y1M19yIlk8bjSQDVVgAekRZLdRA_T_gi_JeWyFysKZVpRcHC1YJhTH4YH8CCMRTwviq3woIsLmdUecjydyZkHcUlhHXj2DbC15cyELalPNe3T9eZ4ySwk9qRJSOkjBAgXAT0a7M6rwri6QHnL0WxTLX4us4rGu8Ui3kuf1WaZH9DNoeWYs1N3xUclockTkRKaqXnuKjnwSVmsuwxFSlnIPJOiMUUZksiaBq_OUvOkB-dEG7OFiDX9XWj1m62yBHkvZHun8LBr9VW3mt1IrcBdbbtzjWwfn6ioK2c4dbtPFhuYohXsmRDaSekP63Dmlw3A";
-
- int actualAuthTime = 1567314000;
- Integer maxAge = 120;
-
- // set clock to September 1, 2019 5:00:00 AM GMT
- Date clock = new Date(1567314000000L);
- clock.setTime(clock.getTime() + ((maxAge + (DEFAULT_CLOCK_SKEW + 1)) * 1000));
-
- IdTokenVerifier.Options options = configureOptions(token);
- options.setClock(clock);
- options.setMaxAge(maxAge);
-
- String errorMessage = String.format("Authentication Time (auth_time) claim in the ID token indicates that too much time has passed since the last end-user authentication. Current time (%d) is after last auth at (%d)",
- clock.getTime() / 1000, actualAuthTime + maxAge + DEFAULT_CLOCK_SKEW);
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals(errorMessage, e.getMessage());
- }
-
- @Test
- public void succeedsWhenMaxSentAndAuthTimeWithinLeeway() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.AbSYZ_Tu0-ZelCRPuu9jOd9y1M19yIlk8bjSQDVVgAekRZLdRA_T_gi_JeWyFysKZVpRcHC1YJhTH4YH8CCMRTwviq3woIsLmdUecjydyZkHcUlhHXj2DbC15cyELalPNe3T9eZ4ySwk9qRJSOkjBAgXAT0a7M6rwri6QHnL0WxTLX4us4rGu8Ui3kuf1WaZH9DNoeWYs1N3xUclockTkRKaqXnuKjnwSVmsuwxFSlnIPJOiMUUZksiaBq_OUvOkB-dEG7OFiDX9XWj1m62yBHkvZHun8LBr9VW3mt1IrcBdbbtzjWwfn6ioK2c4dbtPFhuYohXsmRDaSekP63Dmlw3A";
-
- Integer maxAge = 120;
-
- // set clock to September 1, 2019 5:00:00 AM GMT
- Date clock = new Date(1567314000000L);
- clock.setTime(clock.getTime() + ((maxAge + (DEFAULT_CLOCK_SKEW - 1)) * 1000));
-
- IdTokenVerifier.Options options = configureOptions(token);
- options.setClock(clock);
- options.setMaxAge(maxAge);
-
- new IdTokenVerifier().verify(token, options);
- }
-
- @Test
- public void failsWhenMaxSentButAuthTimeInvalidWithCustomLeeway() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.AbSYZ_Tu0-ZelCRPuu9jOd9y1M19yIlk8bjSQDVVgAekRZLdRA_T_gi_JeWyFysKZVpRcHC1YJhTH4YH8CCMRTwviq3woIsLmdUecjydyZkHcUlhHXj2DbC15cyELalPNe3T9eZ4ySwk9qRJSOkjBAgXAT0a7M6rwri6QHnL0WxTLX4us4rGu8Ui3kuf1WaZH9DNoeWYs1N3xUclockTkRKaqXnuKjnwSVmsuwxFSlnIPJOiMUUZksiaBq_OUvOkB-dEG7OFiDX9XWj1m62yBHkvZHun8LBr9VW3mt1IrcBdbbtzjWwfn6ioK2c4dbtPFhuYohXsmRDaSekP63Dmlw3A";
-
- int actualAuthTime = 1567314000;
- Integer maxAge = 120;
- Integer customLeeway = 120;
-
- // set clock to September 1, 2019 5:00:00 AM GMT
- Date clock = new Date(1567314000000L);
- clock.setTime(clock.getTime() + ((maxAge + customLeeway + 1) * 1000));
-
- IdTokenVerifier.Options options = configureOptions(token);
- options.setClock(clock);
- options.setMaxAge(maxAge);
- options.setClockSkew(customLeeway);
-
- String errorMessage = String.format("Authentication Time (auth_time) claim in the ID token indicates that too much time has passed since the last end-user authentication. Current time (%d) is after last auth at (%d)",
- clock.getTime() / 1000, actualAuthTime + maxAge + customLeeway);
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, options));
- assertEquals(errorMessage, e.getMessage());
- }
-
- @Test
- public void succeedsWhenMaxSentAndAuthTimeWithCustomLeeway() {
- String token = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHwxMjM0NTY3ODkiLCJhdWQiOlsidG9rZW5zLXRlc3QtMTIzIiwiZXh0ZXJuYWwtdGVzdC0xMjMiXSwiZXhwIjoxNTY3NDg2ODAwLCJpYXQiOjE1NjczMTQwMDAsIm5vbmNlIjoiYTU5dms1OTIiLCJhenAiOiJ0b2tlbnMtdGVzdC0xMjMiLCJhdXRoX3RpbWUiOjE1NjczMTQwMDB9.AbSYZ_Tu0-ZelCRPuu9jOd9y1M19yIlk8bjSQDVVgAekRZLdRA_T_gi_JeWyFysKZVpRcHC1YJhTH4YH8CCMRTwviq3woIsLmdUecjydyZkHcUlhHXj2DbC15cyELalPNe3T9eZ4ySwk9qRJSOkjBAgXAT0a7M6rwri6QHnL0WxTLX4us4rGu8Ui3kuf1WaZH9DNoeWYs1N3xUclockTkRKaqXnuKjnwSVmsuwxFSlnIPJOiMUUZksiaBq_OUvOkB-dEG7OFiDX9XWj1m62yBHkvZHun8LBr9VW3mt1IrcBdbbtzjWwfn6ioK2c4dbtPFhuYohXsmRDaSekP63Dmlw3A";
-
- Integer maxAge = 120;
- Integer customLeeway = 120;
-
- // set clock to September 1, 2019 5:00:00 AM GMT
- Date clock = new Date(1567314000000L);
- clock.setTime(clock.getTime() + ((maxAge + customLeeway - 1) * 1000));
-
- IdTokenVerifier.Options options = configureOptions(token);
- options.setClock(clock);
- options.setMaxAge(maxAge);
- options.setClockSkew(customLeeway);
-
- new IdTokenVerifier().verify(token, options);
- }
-
- @Test
- public void succeedsWithValidTokenUsingDefaultClock() {
- String token = JWT.create()
- .withSubject("auth0|sdk458fks")
- .withAudience(AUDIENCE)
- .withIssuedAt(getYesterday())
- .withExpiresAt(getTomorrow())
- .withIssuer("https://" + DOMAIN + "/")
- .withClaim("nonce", "nonce")
- .sign(Algorithm.HMAC256("secret"));
-
- DecodedJWT decodedJWT = JWT.decode(token);
- SignatureVerifier verifier = mock(SignatureVerifier.class);
- when(verifier.verifySignature(token)).thenReturn(decodedJWT);
-
- IdTokenVerifier.Options opts = new IdTokenVerifier.Options("https://" + DOMAIN + "/", AUDIENCE, verifier);
- opts.setNonce("nonce");
-
- new IdTokenVerifier().verify(token, opts);
- }
-
- @Test
- public void succeedsWithValidTokenUsingDefaultClockAndHttpDomain() {
- String token = JWT.create()
- .withSubject("auth0|sdk458fks")
- .withAudience(AUDIENCE)
- .withIssuedAt(getYesterday())
- .withExpiresAt(getTomorrow())
- .withIssuer("http://" + DOMAIN + "/")
- .withClaim("nonce", "nonce")
- .sign(Algorithm.HMAC256("secret"));
-
- DecodedJWT decodedJWT = JWT.decode(token);
- SignatureVerifier verifier = mock(SignatureVerifier.class);
- when(verifier.verifySignature(token)).thenReturn(decodedJWT);
-
- IdTokenVerifier.Options opts = new IdTokenVerifier.Options("http://" + DOMAIN + "/", AUDIENCE, verifier);
- opts.setNonce("nonce");
-
- new IdTokenVerifier().verify(token, opts);
- }
-
- @Test
- public void succeedsWithValidTokenUsingDefaultClockAndHttpsDomain() {
- String token = JWT.create()
- .withSubject("auth0|sdk458fks")
- .withAudience(AUDIENCE)
- .withIssuedAt(getYesterday())
- .withExpiresAt(getTomorrow())
- .withIssuer("https://" + DOMAIN + "/")
- .withClaim("nonce", "nonce")
- .sign(Algorithm.HMAC256("secret"));
-
- DecodedJWT decodedJWT = JWT.decode(token);
- SignatureVerifier verifier = mock(SignatureVerifier.class);
- when(verifier.verifySignature(token)).thenReturn(decodedJWT);
-
- IdTokenVerifier.Options opts = new IdTokenVerifier.Options("https://" + DOMAIN + "/", AUDIENCE, verifier);
- opts.setNonce("nonce");
-
- new IdTokenVerifier().verify(token, opts);
- }
-
- @Test
- public void succeedsWhenOrganizationNameMatchesExpected() {
- String token = JWT.create()
- .withSubject("auth0|sdk458fks")
- .withAudience(AUDIENCE)
- .withIssuedAt(getYesterday())
- .withExpiresAt(getTomorrow())
- .withIssuer("https://" + DOMAIN + "/")
- .withClaim("org_name", "my org")
- .sign(Algorithm.HMAC256("secret"));
-
- String jwt = JWT.decode(token).getToken();
-
- IdTokenVerifier.Options opts = configureOptions(jwt);
- opts.setOrganization("my org");
-
- new IdTokenVerifier().verify(token, opts);
- }
-
- @Test
- public void failsWhenOrganizationNameDoesNotMatchExpected() {
- String token = JWT.create()
- .withSubject("auth0|sdk458fks")
- .withAudience(AUDIENCE)
- .withIssuedAt(getYesterday())
- .withExpiresAt(getTomorrow())
- .withIssuer("https://" + DOMAIN + "/")
- .withClaim("org_name", "my org")
- .sign(Algorithm.HMAC256("secret"));
-
- String jwt = JWT.decode(token).getToken();
-
- IdTokenVerifier.Options opts = configureOptions(jwt);
- opts.setOrganization("other org");
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, opts));
- assertEquals("Organization (org_name) claim mismatch in the ID token; expected \"other org\" but found \"my org\"", e.getMessage());
- }
-
- @Test
- public void succeedsWhenOrganizationNameDoesNotMatchExpected_caseInsensitive() {
- String token = JWT.create()
- .withSubject("auth0|sdk458fks")
- .withAudience(AUDIENCE)
- .withIssuedAt(getYesterday())
- .withExpiresAt(getTomorrow())
- .withIssuer("https://" + DOMAIN + "/")
- .withClaim("org_name", "my org")
- .sign(Algorithm.HMAC256("secret"));
-
- String jwt = JWT.decode(token).getToken();
-
- IdTokenVerifier.Options opts = configureOptions(jwt);
- opts.setOrganization("My org");
-
- new IdTokenVerifier().verify(token, opts);
- }
-
- @Test
- public void failsWhenOrganizationNameExpectedButNotPresent() {
- String token = JWT.create()
- .withSubject("auth0|sdk458fks")
- .withAudience(AUDIENCE)
- .withIssuedAt(getYesterday())
- .withExpiresAt(getTomorrow())
- .withIssuer("https://" + DOMAIN + "/")
- .sign(Algorithm.HMAC256("secret"));
-
- String jwt = JWT.decode(token).getToken();
-
- IdTokenVerifier.Options opts = configureOptions(jwt);
- opts.setOrganization("my org");
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, opts));
- assertEquals("Organization name (org_name) claim must be a string present in the ID token", e.getMessage());
- }
-
- @Test
- public void failsWhenOrganizationNameExpectedButClaimIsNotString() {
- String token = JWT.create()
- .withSubject("auth0|sdk458fks")
- .withAudience(AUDIENCE)
- .withIssuedAt(getYesterday())
- .withExpiresAt(getTomorrow())
- .withIssuer("https://" + DOMAIN + "/")
- .withClaim("org_name", 42)
- .sign(Algorithm.HMAC256("secret"));
-
- String jwt = JWT.decode(token).getToken();
-
- IdTokenVerifier.Options opts = configureOptions(jwt);
- opts.setOrganization("my org");
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, opts));
- assertEquals("Organization name (org_name) claim must be a string present in the ID token", e.getMessage());
- }
-
- @Test
- public void succeedsWhenOrganizationNameNotSpecifiedButIsPresent() {
- String token = JWT.create()
- .withSubject("auth0|sdk458fks")
- .withAudience(AUDIENCE)
- .withIssuedAt(getYesterday())
- .withExpiresAt(getTomorrow())
- .withIssuer("https://" + DOMAIN + "/")
- .withClaim("org_name", "my org")
- .sign(Algorithm.HMAC256("secret"));
-
- String jwt = JWT.decode(token).getToken();
-
- IdTokenVerifier.Options opts = configureOptions(jwt);
- new IdTokenVerifier().verify(token, opts);
- }
-
- @Test
- public void succeedsWhenOrganizationIdMatchesExpected() {
- String token = JWT.create()
- .withSubject("auth0|sdk458fks")
- .withAudience(AUDIENCE)
- .withIssuedAt(getYesterday())
- .withExpiresAt(getTomorrow())
- .withIssuer("https://" + DOMAIN + "/")
- .withClaim("org_id", "org_123")
- .sign(Algorithm.HMAC256("secret"));
-
- String jwt = JWT.decode(token).getToken();
-
- IdTokenVerifier.Options opts = configureOptions(jwt);
- opts.setOrganization("org_123");
-
- new IdTokenVerifier().verify(token, opts);
- }
-
- @Test
- public void failsWhenOrganizationIdDoesNotMatchExpected() {
- String token = JWT.create()
- .withSubject("auth0|sdk458fks")
- .withAudience(AUDIENCE)
- .withIssuedAt(getYesterday())
- .withExpiresAt(getTomorrow())
- .withIssuer("https://" + DOMAIN + "/")
- .withClaim("org_id", "org_123")
- .sign(Algorithm.HMAC256("secret"));
-
- String jwt = JWT.decode(token).getToken();
-
- IdTokenVerifier.Options opts = configureOptions(jwt);
- opts.setOrganization("org_abc");
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, opts));
- assertEquals("Organization (org_id) claim mismatch in the ID token; expected \"org_abc\" but found \"org_123\"", e.getMessage());
- }
-
- @Test
- public void failsWhenOrganizationIdDoesNotMatchExpected_caseSensitive() {
- String token = JWT.create()
- .withSubject("auth0|sdk458fks")
- .withAudience(AUDIENCE)
- .withIssuedAt(getYesterday())
- .withExpiresAt(getTomorrow())
- .withIssuer("https://" + DOMAIN + "/")
- .withClaim("org_id", "org_123")
- .sign(Algorithm.HMAC256("secret"));
-
- String jwt = JWT.decode(token).getToken();
-
- IdTokenVerifier.Options opts = configureOptions(jwt);
- opts.setOrganization("org_aBc");
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, opts));
- assertEquals("Organization (org_id) claim mismatch in the ID token; expected \"org_aBc\" but found \"org_123\"", e.getMessage());
- }
-
- @Test
- public void failsWhenOrganizationIdExpectedButNotPresent() {
- String token = JWT.create()
- .withSubject("auth0|sdk458fks")
- .withAudience(AUDIENCE)
- .withIssuedAt(getYesterday())
- .withExpiresAt(getTomorrow())
- .withIssuer("https://" + DOMAIN + "/")
- .sign(Algorithm.HMAC256("secret"));
-
- String jwt = JWT.decode(token).getToken();
-
- IdTokenVerifier.Options opts = configureOptions(jwt);
- opts.setOrganization("org_123");
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, opts));
- assertEquals("Organization Id (org_id) claim must be a string present in the ID token", e.getMessage());
- }
-
- @Test
- public void failsWhenOrganizationIdExpectedButClaimIsNotString() {
- String token = JWT.create()
- .withSubject("auth0|sdk458fks")
- .withAudience(AUDIENCE)
- .withIssuedAt(getYesterday())
- .withExpiresAt(getTomorrow())
- .withIssuer("https://" + DOMAIN + "/")
- .withClaim("org_id", 42)
- .sign(Algorithm.HMAC256("secret"));
-
- String jwt = JWT.decode(token).getToken();
-
- IdTokenVerifier.Options opts = configureOptions(jwt);
- opts.setOrganization("org_123");
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> new IdTokenVerifier().verify(token, opts));
- assertEquals("Organization Id (org_id) claim must be a string present in the ID token", e.getMessage());
- }
-
- @Test
- public void succeedsWhenOrganizationIdNotSpecifiedButIsPresent() {
- String token = JWT.create()
- .withSubject("auth0|sdk458fks")
- .withAudience(AUDIENCE)
- .withIssuedAt(getYesterday())
- .withExpiresAt(getTomorrow())
- .withIssuer("https://" + DOMAIN + "/")
- .withClaim("org_id", "org_123")
- .sign(Algorithm.HMAC256("secret"));
-
- String jwt = JWT.decode(token).getToken();
-
- IdTokenVerifier.Options opts = configureOptions(jwt);
- new IdTokenVerifier().verify(token, opts);
- }
-
- private IdTokenVerifier.Options configureOptions(String token) {
- DecodedJWT decodedJWT = JWT.decode(token);
- SignatureVerifier verifier = mock(SignatureVerifier.class);
- when(verifier.verifySignature(token)).thenReturn(decodedJWT);
-
- IdTokenVerifier.Options opts = new IdTokenVerifier.Options("https://" + DOMAIN + "/", AUDIENCE, verifier);
- opts.setClock(DEFAULT_CLOCK);
- return opts;
- }
-
- private Date getYesterday() {
- Calendar cal = Calendar.getInstance();
- cal.add(Calendar.DATE, -1);
-
- return cal.getTime();
- }
-
- private Date getTomorrow() {
- Calendar cal = Calendar.getInstance();
- cal.add(Calendar.DATE, 1);
-
- return cal.getTime();
- }
-}
diff --git a/src/test/java/com/auth0/RequestProcessorTest.java b/src/test/java/com/auth0/RequestProcessorTest.java
index c719dea..ec30c56 100644
--- a/src/test/java/com/auth0/RequestProcessorTest.java
+++ b/src/test/java/com/auth0/RequestProcessorTest.java
@@ -3,6 +3,7 @@
import com.auth0.client.auth.AuthAPI;
import com.auth0.exception.Auth0Exception;
import com.auth0.json.auth.TokenHolder;
+import com.auth0.jwk.JwkProvider;
import com.auth0.net.Response;
import com.auth0.net.TokenRequest;
import org.junit.jupiter.api.BeforeEach;
@@ -41,7 +42,7 @@ public class RequestProcessorTest {
@Mock
private DomainProvider mockDomainProvider;
@Mock
- private SignatureVerifier mockSignatureVerifier;
+ private JwkProvider mockJwkProvider;
@Mock
private AuthAPI mockAuthAPI;
@Mock
@@ -70,8 +71,7 @@ public void shouldBuildRequestProcessorWithRequiredParameters() {
mockDomainProvider,
RESPONSE_TYPE_CODE,
CLIENT_ID,
- CLIENT_SECRET,
- mockSignatureVerifier)
+ CLIENT_SECRET)
.build();
assertThat(processor, is(notNullValue()));
@@ -83,8 +83,8 @@ public void shouldBuildRequestProcessorWithAllOptionalParameters() {
mockDomainProvider,
RESPONSE_TYPE_CODE,
CLIENT_ID,
- CLIENT_SECRET,
- mockSignatureVerifier)
+ CLIENT_SECRET)
+ .withJwkProvider(mockJwkProvider)
.withClockSkew(120)
.withAuthenticationMaxAge(3600)
.withCookiePath("/custom")
@@ -111,8 +111,7 @@ public void shouldDisableLegacySameSiteCookie() {
mockDomainProvider,
RESPONSE_TYPE_CODE,
CLIENT_ID,
- CLIENT_SECRET,
- mockSignatureVerifier)
+ CLIENT_SECRET)
.withLegacySameSiteCookie(false)
.build();
@@ -373,20 +372,20 @@ public void shouldThrowOnProcessIfCodeRequestSucceedsButDoesNotPassIdTokenVerifi
MockHttpServletRequest request = getRequest(params);
request.setCookies(new Cookie("com.auth0.state", "1234"));
- // Return a structurally valid JWT with wrong issuer so verification fails
+ // Return a structurally valid JWT with invalid signature so verification fails
String fakeJwt = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3dyb25nLyIsInN1YiI6InVzZXIxMjMiLCJhdWQiOiJ0ZXN0Q2xpZW50SWQiLCJleHAiOjk5OTk5OTk5OTksImlhdCI6MTYwMDAwMDAwMH0.signature";
when(mockTokenHolder.getIdToken()).thenReturn(fakeJwt);
when(mockTokenResponse.getBody()).thenReturn(mockTokenHolder);
when(mockTokenRequest.execute()).thenReturn(mockTokenResponse);
when(mockAuthAPI.exchangeCode(eq("abc123"), anyString())).thenReturn(mockTokenRequest);
- // Use real AlgorithmNameVerifier so signature check passes but claim validation fails
+ // Use mockJwkProvider — token has invalid signature so RS256 verification will fail
RequestProcessor handler = new RequestProcessor.Builder(
mockDomainProvider,
RESPONSE_TYPE_CODE,
CLIENT_ID,
- CLIENT_SECRET,
- new AlgorithmNameVerifier())
+ CLIENT_SECRET)
+ .withJwkProvider(mockJwkProvider)
.build();
RequestProcessor spy = spy(handler);
doReturn(mockAuthAPI).when(spy).createClientForDomain(anyString());
@@ -461,7 +460,7 @@ public void shouldReturnEmptyTokensWhenCodeRequestReturnsNoTokens() throws Excep
public void shouldThrowOnProcessIfIdTokenRequestDoesNotPassIdTokenVerification() {
when(mockDomainProvider.getDomain(any())).thenReturn(DOMAIN);
- // Structurally valid JWT with wrong issuer so claim validation fails
+ // Structurally valid JWT with invalid signature so verification fails
String fakeJwt = "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL3dyb25nLyIsInN1YiI6InVzZXIxMjMiLCJhdWQiOiJ0ZXN0Q2xpZW50SWQiLCJleHAiOjk5OTk5OTk5OTksImlhdCI6MTYwMDAwMDAwMH0.signature";
Map params = new HashMap<>();
@@ -470,13 +469,13 @@ public void shouldThrowOnProcessIfIdTokenRequestDoesNotPassIdTokenVerification()
MockHttpServletRequest request = getRequest(params);
request.setCookies(new Cookie("com.auth0.state", "1234"));
- // Use real AlgorithmNameVerifier so signature check passes but claim validation fails
+ // Use mockJwkProvider — token has invalid signature so RS256 verification will fail
RequestProcessor handler = new RequestProcessor.Builder(
mockDomainProvider,
RESPONSE_TYPE_ID_TOKEN,
CLIENT_ID,
- CLIENT_SECRET,
- new AlgorithmNameVerifier())
+ CLIENT_SECRET)
+ .withJwkProvider(mockJwkProvider)
.build();
IdentityVerificationException e = assertThrows(IdentityVerificationException.class, () -> handler.process(request, response));
@@ -506,8 +505,7 @@ public void shouldBuildAuthorizeUrlWithOrganization() {
mockDomainProvider,
RESPONSE_TYPE_CODE,
CLIENT_ID,
- CLIENT_SECRET,
- mockSignatureVerifier)
+ CLIENT_SECRET)
.withOrganization("org_123")
.build();
@@ -526,8 +524,7 @@ public void shouldBuildAuthorizeUrlWithInvitation() {
mockDomainProvider,
RESPONSE_TYPE_CODE,
CLIENT_ID,
- CLIENT_SECRET,
- mockSignatureVerifier)
+ CLIENT_SECRET)
.withInvitation("inv_456")
.build();
@@ -546,8 +543,7 @@ public void shouldBuildAuthorizeUrlWithCustomCookiePath() {
mockDomainProvider,
RESPONSE_TYPE_CODE,
CLIENT_ID,
- CLIENT_SECRET,
- mockSignatureVerifier)
+ CLIENT_SECRET)
.withCookiePath("/custom")
.build();
@@ -591,8 +587,7 @@ public void shouldSupportOrganizationParameter() {
mockDomainProvider,
RESPONSE_TYPE_CODE,
CLIENT_ID,
- CLIENT_SECRET,
- mockSignatureVerifier)
+ CLIENT_SECRET)
.withOrganization("org_123")
.build();
@@ -605,8 +600,7 @@ public void shouldSupportInvitationParameter() {
mockDomainProvider,
RESPONSE_TYPE_CODE,
CLIENT_ID,
- CLIENT_SECRET,
- mockSignatureVerifier)
+ CLIENT_SECRET)
.withInvitation("inv_456")
.build();
@@ -619,8 +613,7 @@ public void shouldSupportCustomCookiePath() {
mockDomainProvider,
RESPONSE_TYPE_CODE,
CLIENT_ID,
- CLIENT_SECRET,
- mockSignatureVerifier)
+ CLIENT_SECRET)
.withCookiePath("/custom/path")
.build();
@@ -633,8 +626,7 @@ public void shouldSupportClockSkewConfiguration() {
mockDomainProvider,
RESPONSE_TYPE_CODE,
CLIENT_ID,
- CLIENT_SECRET,
- mockSignatureVerifier)
+ CLIENT_SECRET)
.withClockSkew(180)
.build();
@@ -647,8 +639,7 @@ public void shouldSupportAuthenticationMaxAge() {
mockDomainProvider,
RESPONSE_TYPE_CODE,
CLIENT_ID,
- CLIENT_SECRET,
- mockSignatureVerifier)
+ CLIENT_SECRET)
.withAuthenticationMaxAge(7200)
.build();
@@ -662,8 +653,8 @@ private RequestProcessor createDefaultRequestProcessor() {
mockDomainProvider,
RESPONSE_TYPE_CODE,
CLIENT_ID,
- CLIENT_SECRET,
- mockSignatureVerifier)
+ CLIENT_SECRET)
+ .withJwkProvider(mockJwkProvider)
.build();
}
@@ -672,8 +663,8 @@ private RequestProcessor createRequestProcessorWithResponseType(String responseT
mockDomainProvider,
responseType,
CLIENT_ID,
- CLIENT_SECRET,
- mockSignatureVerifier)
+ CLIENT_SECRET)
+ .withJwkProvider(mockJwkProvider)
.build();
}
diff --git a/src/test/java/com/auth0/SignatureVerifierTest.java b/src/test/java/com/auth0/SignatureVerifierTest.java
deleted file mode 100644
index b0b6678..0000000
--- a/src/test/java/com/auth0/SignatureVerifierTest.java
+++ /dev/null
@@ -1,185 +0,0 @@
-package com.auth0;
-
-import com.auth0.jwk.Jwk;
-import com.auth0.jwk.JwkException;
-import com.auth0.jwk.JwkProvider;
-import com.auth0.jwt.interfaces.DecodedJWT;
-import org.junit.jupiter.api.Test;
-
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.FileReader;
-import java.io.IOException;
-import java.nio.file.Paths;
-import java.security.KeyFactory;
-import java.security.PublicKey;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.EncodedKeySpec;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.Base64;
-import java.util.Scanner;
-
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class SignatureVerifierTest {
-
- private static final String EXPIRED_HS_JWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub25jZSI6IjEyMzQiLCJpc3MiOiJodHRwczovL21lLmF1dGgwLmNvbS8iLCJhdWQiOiJkYU9nbkdzUlloa3d1NjIxdmYiLCJzdWIiOiJhdXRoMHx1c2VyMTIzIiwiZXhwIjo5NzE3ODkzMTd9.5_VOXBmOVMSi8OGgonyfyiJSq3A03PwOEuZlPD-Gxik";
- private static final String NONE_JWT = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJub25jZSI6IjEyMzQiLCJpc3MiOiJodHRwczovL21lLmF1dGgwLmNvbS8iLCJhdWQiOiJkYU9nbkdzUlloa3d1NjIxdmYiLCJzdWIiOiJhdXRoMHx1c2VyMTIzIn0.";
- private static final String HS_JWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub25jZSI6IjEyMzQiLCJpc3MiOiJodHRwczovL21lLmF1dGgwLmNvbS8iLCJhdWQiOiJkYU9nbkdzUlloa3d1NjIxdmYiLCJzdWIiOiJhdXRoMHx1c2VyMTIzIn0.a7ayNmFTxS2D-EIoUikoJ6dck7I8veWyxnje_mYD3qY";
- private static final String RS_JWT = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYzEyMyJ9.eyJub25jZSI6IjEyMzQiLCJpc3MiOiJodHRwczovL21lLmF1dGgwLmNvbS8iLCJhdWQiOiJkYU9nbkdzUlloa3d1NjIxdmYiLCJzdWIiOiJhdXRoMHx1c2VyMTIzIn0.PkPWdoZNfXz8EB0SBPH83lNSOhyhdhdqYIgIwgY2nHozUnFOaUjVewlAXxP_3LBGibQ_ng4s5fEEOCJjaKBy04McryvOuL6nqb1dPQseeyxuv2zQitfrs-7kEtfeS3umywM-tV6guw9_W3nmIgaXOiYiF4WJM23ItbdCmvwdXLaf9-xHkQbRY_zEwEFbprFttKUXFbkPt6XjZ3zZwZbNZn64bx2PBiSJ2KMZAE3Lghmci-RXdhi7hXpmN30Tzze1ZsjvVeRRKNzShByKK9ZGZPmQ5yggJOXFy32ehjGkYwFMCqgMQomcGbcYhsd97huKHMHl3HOE5GDYjIq9o9oKRA";
- private static final String RS_PUBLIC_KEY = "src/test/resources/public.pem";
- private static final String RS_PUBLIC_KEY_BAD = "src/test/resources/bad-public.pem";
- private static final String RS_JWT_INVALID_SIGNATURE = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYzEyMyJ9.eyJub25jZSI6IjEyMzQiLCJpc3MiOiJodHRwczovL21lLmF1dGgwLmNvbS8iLCJhdWQiOiJkYU9nbkdzUlloa3d1NjIxdmYiLCJzdWIiOiJhdXRoMHx1c2VyMTIzIn0.PkPWdoZNfXz8EB0SBPH83lNSOhyhdhdqYIgIwgY2nHozUnFOaUjVewlAXxP_3LBGibQ_ng4s5fEEOCJjaKBy04McryvOuL6nqb1dPQseeyxuv2zQitfrs-7kEtfeS3umywM-tV6guw9_W3nmIgaXOiYiF4WJM23ItbdCmvwdXLaf9-xHkQbRY_zEwEFbprFttKUXFbkPt6XjZ3zZwZbNZn64bx2PBiSJ2KMZAE3Lghmci-RXdhi7hXpmN30Tzze1ZsjvVeRRKNzShByKK9ZGZPmQ5yggJOXFy32ehjGkYwFMCqgMQomcGbcYhsd97huKHMHl3HOE5GDYjIq9o9oABC";
- private static final String HS_JWT_INVALID_SIGNATURE = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJub25jZSI6IjEyMzQiLCJpc3MiOiJodHRwczovL21lLmF1dGgwLmNvbS8iLCJhdWQiOiJkYU9nbkdzUlloa3d1NjIxdmYiLCJzdWIiOiJhdXRoMHx1c2VyMTIzIn0.eTxhYFIHNii1zjxGr9QZvPcqofOd_4bHcjxGq7CQluY";
-
- @Test
- public void failsWhenAlgorithmIsNotExpected() {
- SignatureVerifier verifier = new AlgorithmNameVerifier();
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> verifier.verifySignature(NONE_JWT));
- assertEquals("Signature algorithm of \"none\" is not supported. Expected the ID token to be signed with \"[HS256, RS256]\".", e.getMessage());
- }
-
- @Test
- public void failsWhenTokenCannotBeDecoded() {
- SignatureVerifier verifier = new SymmetricSignatureVerifier("secret");
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> verifier.verifySignature("boom"));
- assertEquals("ID token could not be decoded", e.getMessage());
- }
-
- @Test
- public void failsWhenAlgorithmRS256IsNotExpected() {
- SignatureVerifier verifier = new SymmetricSignatureVerifier("secret");
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> verifier.verifySignature(RS_JWT));
- assertEquals("Signature algorithm of \"RS256\" is not supported. Expected the ID token to be signed with \"[HS256]\".", e.getMessage());
- }
-
- @Test
- public void failsWhenAlgorithmHS256IsNotExpected() throws Exception {
- SignatureVerifier verifier = new AsymmetricSignatureVerifier(getRSProvider(RS_PUBLIC_KEY));
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> verifier.verifySignature(HS_JWT));
- assertEquals("Signature algorithm of \"HS256\" is not supported. Expected the ID token to be signed with \"[RS256]\".", e.getMessage());
- }
-
- @Test
- public void succeedsSkippingSignatureCheckOnHS256Token() {
- SignatureVerifier verifier = new AlgorithmNameVerifier();
- DecodedJWT decodedJWT1 = verifier.verifySignature(HS_JWT);
- DecodedJWT decodedJWT2 = verifier.verifySignature(HS_JWT_INVALID_SIGNATURE);
-
- assertThat(decodedJWT1, notNullValue());
- assertThat(decodedJWT2, notNullValue());
- }
-
- @Test
- public void succeedsSkippingSignatureCheckOnRS256Token() {
- SignatureVerifier verifier = new AlgorithmNameVerifier();
- DecodedJWT decodedJWT1 = verifier.verifySignature(RS_JWT);
- DecodedJWT decodedJWT2 = verifier.verifySignature(RS_JWT_INVALID_SIGNATURE);
-
- assertThat(decodedJWT1, notNullValue());
- assertThat(decodedJWT2, notNullValue());
- }
-
- @Test
- public void succeedsWithValidSignatureHS256Token() {
- SignatureVerifier verifier = new SymmetricSignatureVerifier("secret");
- DecodedJWT decodedJWT = verifier.verifySignature(HS_JWT);
-
- assertThat(decodedJWT, notNullValue());
- }
-
- @Test
- public void succeedsAndIgnoresExpiredTokenException() {
- SignatureVerifier verifier = new SymmetricSignatureVerifier("secret");
- DecodedJWT decodedJWT = verifier.verifySignature(EXPIRED_HS_JWT);
-
- assertThat(decodedJWT, notNullValue());
- }
-
- @Test
- public void failsWithInvalidSignatureHS256Token() {
- SignatureVerifier verifier = new SymmetricSignatureVerifier("badsecret");
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> verifier.verifySignature(HS_JWT));
- assertEquals("Invalid token signature", e.getMessage());
- }
-
- @Test
- public void succeedsWithValidSignatureRS256Token() throws Exception {
- SignatureVerifier verifier = new AsymmetricSignatureVerifier(getRSProvider(RS_PUBLIC_KEY));
- DecodedJWT decodedJWT = verifier.verifySignature(RS_JWT);
-
- assertThat(decodedJWT, notNullValue());
- }
-
- @Test
- public void failsWithInvalidSignatureRS256Token() throws Exception {
- SignatureVerifier verifier = new AsymmetricSignatureVerifier(getRSProvider(RS_PUBLIC_KEY_BAD));
-
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> verifier.verifySignature(RS_JWT));
- assertEquals("Invalid token signature", e.getMessage());
- }
-
- @Test
- public void failsWhenErrorGettingJwk() throws Exception {
- JwkProvider jwkProvider = mock(JwkProvider.class);
- when(jwkProvider.get("abc123")).thenThrow(JwkException.class);
-
- SignatureVerifier verifier = new AsymmetricSignatureVerifier(jwkProvider);
- TokenValidationException e = assertThrows(TokenValidationException.class, () -> verifier.verifySignature(RS_JWT));
- assertEquals("Invalid token signature", e.getMessage());
- }
-
- private JwkProvider getRSProvider(String rsaPath) throws Exception {
- JwkProvider jwkProvider = mock(JwkProvider.class);
- Jwk jwk = mock(Jwk.class);
- when(jwkProvider.get("abc123")).thenReturn(jwk);
- RSAPublicKey key = readPublicKeyFromFile(rsaPath);
- when(jwk.getPublicKey()).thenReturn(key);
- return jwkProvider;
- }
-
- private static RSAPublicKey readPublicKeyFromFile(final String path) throws IOException {
- Scanner scanner = null;
- BufferedReader reader = null;
- try {
- scanner = new Scanner(Paths.get(path));
- if (scanner.hasNextLine() && scanner.nextLine().startsWith("-----BEGIN CERTIFICATE-----")) {
- FileInputStream fs = new FileInputStream(path);
- CertificateFactory fact = CertificateFactory.getInstance("X.509");
- X509Certificate cer = (X509Certificate) fact.generateCertificate(fs);
- PublicKey key = cer.getPublicKey();
- fs.close();
- return (RSAPublicKey) key;
- } else {
- reader = new BufferedReader(new FileReader(path));
- StringBuilder pemContent = new StringBuilder();
- String line;
- while ((line = reader.readLine()) != null) {
- if (!line.startsWith("-----BEGIN") && !line.startsWith("-----END")) {
- pemContent.append(line);
- }
- }
- byte[] keyBytes = Base64.getDecoder().decode(pemContent.toString());
- KeyFactory kf = KeyFactory.getInstance("RSA");
- EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
- return (RSAPublicKey) kf.generatePublic(keySpec);
- }
- } catch (Exception e) {
- throw new IOException("Couldn't parse the RSA Public Key / Certificate file.", e);
- } finally {
- if (scanner != null) {
- scanner.close();
- }
- if (reader != null) {
- reader.close();
- }
- }
- }
-}