Skip to content

Commit 76c4517

Browse files
authored
Merge pull request #21 from auth0/feat/mcd-initials
Feat/mcd initials
2 parents faa32a2 + 49a5d7d commit 76c4517

50 files changed

Lines changed: 3134 additions & 637 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
![Java Version](https://img.shields.io/badge/java-8%2B-blue)
77
![License](https://img.shields.io/badge/license-MIT-green)
88

9-
A comprehensive Java library for Auth0 JWT authentication with built-in **DPoP (Demonstration of Proof-of-Possession)** support. This project provides Spring Boot integration for secure API development.
9+
A comprehensive Java library for Auth0 JWT authentication with built-in **DPoP (Demonstration of Proof-of-Possession)** and **Multi-Custom Domain (MCD)** support. This project provides Spring Boot integration for secure API development.
1010

1111
## 🏗️ Architecture Overview
1212

@@ -49,6 +49,8 @@ It provides:
4949

5050
- JWT validation with Auth0 JWKS integration
5151
- DPoP proof validation per [RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)
52+
- Multiple Custom Domain (MCD) support — static domain lists, or dynamic resolution at request time
53+
- Extensible caching — pluggable `AuthCache` interface for distributed backends (Redis, Memcached)
5254
- Flexible authentication strategies
5355

5456

auth0-api-java/src/main/java/com/auth0/AbstractAuthentication.java

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
import com.auth0.models.AuthToken;
88
import com.auth0.models.AuthenticationContext;
99
import com.auth0.models.HttpRequestInfo;
10-
import com.auth0.validators.DPoPProofValidator;
11-
import com.auth0.validators.JWTValidator;
1210

1311
import java.util.HashMap;
1412
import java.util.List;
@@ -28,21 +26,21 @@ protected AbstractAuthentication(JWTValidator jwtValidator, TokenExtractor extra
2826
/**
2927
* Concrete method to validate Bearer token headers and JWT claims.
3028
*/
31-
protected DecodedJWT validateBearerToken(Map<String, String> headers, HttpRequestInfo httpRequestInfo) throws BaseAuthException {
32-
AuthToken authToken = extractor.extractBearer(headers);
33-
return jwtValidator.validateToken(authToken.getAccessToken(), headers, httpRequestInfo);
29+
protected DecodedJWT validateBearerToken(HttpRequestInfo httpRequestInfo) throws BaseAuthException {
30+
AuthToken authToken = extractor.extractBearer(httpRequestInfo.getHeaders());
31+
return jwtValidator.validateToken(authToken.getAccessToken(), httpRequestInfo);
3432
}
3533

3634
/**
3735
* Concrete method to validate DPoP token headers, JWT claims, and proof.
3836
*/
39-
protected DecodedJWT validateDpopTokenAndProof(Map<String, String> headers, HttpRequestInfo requestInfo)
37+
protected DecodedJWT validateDpopTokenAndProof(HttpRequestInfo requestInfo)
4038
throws BaseAuthException {
4139

4240
AuthValidatorHelper.validateHttpMethodAndHttpUrl(requestInfo);
4341

44-
AuthToken authToken = extractor.extractDPoPProofAndDPoPToken(headers);
45-
DecodedJWT decodedJwtToken = jwtValidator.validateToken(authToken.getAccessToken(), headers, requestInfo);
42+
AuthToken authToken = extractor.extractDPoPProofAndDPoPToken(requestInfo.getHeaders());
43+
DecodedJWT decodedJwtToken = jwtValidator.validateToken(authToken.getAccessToken(), requestInfo);
4644

4745
dpopProofValidator.validate(authToken.getProof(), decodedJwtToken, requestInfo);
4846

@@ -52,9 +50,7 @@ protected DecodedJWT validateDpopTokenAndProof(Map<String, String> headers, Http
5250
/**
5351
* Main abstract method for each concrete strategy.
5452
*/
55-
public abstract AuthenticationContext authenticate(
56-
Map<String, String> headers,
57-
HttpRequestInfo requestInfo
53+
public abstract AuthenticationContext authenticate(HttpRequestInfo requestInfo
5854
) throws BaseAuthException;
5955

6056
/**

auth0-api-java/src/main/java/com/auth0/AllowedDPoPAuthentication.java

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@
66
import com.auth0.jwt.interfaces.DecodedJWT;
77
import com.auth0.models.AuthenticationContext;
88
import com.auth0.models.HttpRequestInfo;
9-
import com.auth0.validators.DPoPProofValidator;
10-
import com.auth0.validators.JWTValidator;
119

12-
import java.util.Map;
1310
class AllowedDPoPAuthentication extends AbstractAuthentication {
1411

1512
public AllowedDPoPAuthentication(JWTValidator jwtValidator,
@@ -20,30 +17,27 @@ public AllowedDPoPAuthentication(JWTValidator jwtValidator,
2017

2118
/**
2219
* Authenticates the request when DPoP Mode is Allowed (Accepts both DPoP and Bearer tokens) .
23-
* @param headers request headers
2420
* @param requestInfo HTTP request info
2521
* @return AuthenticationContext with JWT claims
2622
* @throws BaseAuthException if validation fails
2723
*/
2824
@Override
29-
public AuthenticationContext authenticate(Map<String, String> headers, HttpRequestInfo requestInfo)
25+
public AuthenticationContext authenticate(HttpRequestInfo requestInfo)
3026
throws BaseAuthException {
3127

3228
String scheme = "";
3329

3430
try{
35-
Map<String, String> normalizedHeader = normalize(headers);
36-
37-
scheme = extractor.getScheme(normalizedHeader);
31+
scheme = extractor.getScheme(requestInfo.getHeaders());
3832

3933
if (scheme.equalsIgnoreCase(AuthConstants.BEARER_SCHEME)) {
40-
DecodedJWT jwtToken = validateBearerToken(normalizedHeader, requestInfo);
41-
AuthValidatorHelper.validateNoDpopPresence(normalizedHeader, jwtToken);
34+
DecodedJWT jwtToken = validateBearerToken(requestInfo);
35+
AuthValidatorHelper.validateNoDpopPresence(requestInfo.getHeaders(), jwtToken);
4236
return buildContext(jwtToken);
4337
}
4438

4539
if (scheme.equalsIgnoreCase(AuthConstants.DPOP_SCHEME)) {
46-
DecodedJWT decodedJWT = validateDpopTokenAndProof(normalizedHeader, requestInfo);
40+
DecodedJWT decodedJWT = validateDpopTokenAndProof(requestInfo);
4741
return buildContext(decodedJWT);
4842
}
4943

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.auth0;
2+
3+
/**
4+
* Cache abstraction for storing authentication-related data such as
5+
* OIDC discovery metadata and JWKS providers.
6+
* <p>
7+
* The SDK ships with a default in-memory LRU implementation
8+
* ({@link InMemoryAuthCache}). Users can implement this interface
9+
* to plug in distributed cache backends (e.g., Redis, Memcached) without
10+
* breaking changes to the SDK's public API.
11+
* </p>
12+
*
13+
* <p>
14+
* A single {@code AuthCache<Object>} instance can serve as a unified cache
15+
* for both discovery metadata and JWKS providers by using key prefixes:
16+
* </p>
17+
* <ul>
18+
* <li>{@code discovery:{issuerUrl}} — OIDC discovery metadata</li>
19+
* <li>{@code jwks:{jwksUri}} — JwkProvider instances</li>
20+
* </ul>
21+
*
22+
* <h3>Thread Safety</h3>
23+
* <p>
24+
* All implementations <b>must</b> be thread-safe.
25+
* </p>
26+
*
27+
* @param <V> the type of cached values
28+
*/
29+
public interface AuthCache<V> {
30+
31+
/**
32+
* Retrieves a value from the cache.
33+
*
34+
* @param key the cache key
35+
* @return the cached value, or {@code null} if not present or expired
36+
*/
37+
V get(String key);
38+
39+
/**
40+
* Stores a value in the cache with the cache's default TTL.
41+
*
42+
* @param key the cache key
43+
* @param value the value to cache
44+
*/
45+
void put(String key, V value);
46+
47+
/**
48+
* Removes a specific entry from the cache.
49+
*
50+
* @param key the cache key to remove
51+
*/
52+
void remove(String key);
53+
54+
/**
55+
* Removes all entries from the cache.
56+
*/
57+
void clear();
58+
59+
/**
60+
* Returns the number of entries currently in the cache.
61+
*
62+
* @return the cache size
63+
*/
64+
int size();
65+
}

auth0-api-java/src/main/java/com/auth0/AuthClient.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@
44
import com.auth0.models.AuthenticationContext;
55
import com.auth0.models.AuthOptions;
66
import com.auth0.models.HttpRequestInfo;
7-
import com.auth0.validators.DPoPProofValidator;
8-
import com.auth0.validators.JWTValidator;
9-
10-
import java.util.Map;
117

128
public class AuthClient {
139

@@ -45,12 +41,11 @@ public static AuthClient from(AuthOptions options) {
4541

4642
/**
4743
* Verifies the incoming request headers and HTTP request info.
48-
* @param headers request headers
4944
* @param requestInfo HTTP request info
5045
* @return AuthenticationContext with JWT claims
5146
* @throws BaseAuthException if verification fails
5247
*/
53-
public AuthenticationContext verifyRequest(Map<String, String> headers, HttpRequestInfo requestInfo) throws BaseAuthException {
54-
return orchestrator.process(headers, requestInfo);
48+
public AuthenticationContext verifyRequest(HttpRequestInfo requestInfo) throws BaseAuthException {
49+
return orchestrator.process(requestInfo);
5550
}
5651
}

auth0-api-java/src/main/java/com/auth0/AuthConstants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.auth0;
22

3-
public class AuthConstants {
3+
class AuthConstants {
44
public static final String AUTHORIZATION_HEADER = "authorization";
55
public static final String DPOP_HEADER = "dpop";
66
public static final String BEARER_SCHEME = "bearer";

auth0-api-java/src/main/java/com/auth0/AuthenticationOrchestrator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ public AuthenticationOrchestrator(AbstractAuthentication authStrategy) {
1616
this.authStrategy = authStrategy;
1717
}
1818

19-
public AuthenticationContext process(Map<String, String> headers, HttpRequestInfo requestInfo)
19+
public AuthenticationContext process(HttpRequestInfo requestInfo)
2020
throws BaseAuthException {
21-
return authStrategy.authenticate(headers, requestInfo);
21+
return authStrategy.authenticate(requestInfo);
2222
}
2323
}

auth0-api-java/src/main/java/com/auth0/validators/ClaimValidator.java renamed to auth0-api-java/src/main/java/com/auth0/ClaimValidator.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
package com.auth0.validators;
1+
package com.auth0;
22

33
import com.auth0.exception.*;
44
import com.auth0.jwt.interfaces.DecodedJWT;
55

66
import java.util.*;
77

88
/**
9-
* Utility class for JWT claim validation
10-
*
11-
* Provides functionality to validate JWT claims including scopes and custom
12-
* claim checks.
9+
* Utility class for JWT claim validation. Provides functionality to validate JWT claims including scopes and custom claim checks.
1310
* This is the Java equivalent of the TypeScript claim validation utilities.
1411
*/
1512
class ClaimValidator {
@@ -27,13 +24,11 @@ static Set<String> getClaimValues(DecodedJWT jwt, String claimName) throws BaseA
2724
throw new VerifyAccessTokenException("Required claim is missing");
2825
}
2926

30-
// Case 1: space-separated string
3127
String strValue = jwt.getClaim(claimName).asString();
3228
if (strValue != null) {
3329
return new HashSet<>(Arrays.asList(strValue.trim().split("\\s+")));
3430
}
3531

36-
// Case 2: list of strings
3732
List<String> listValue = jwt.getClaim(claimName).asList(String.class);
3833
if (listValue != null) {
3934
return new HashSet<>(listValue);

auth0-api-java/src/main/java/com/auth0/validators/DPoPProofValidator.java renamed to auth0-api-java/src/main/java/com/auth0/DPoPProofValidator.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.auth0.validators;
1+
package com.auth0;
22

33
import com.auth0.exception.BaseAuthException;
44
import com.auth0.exception.InvalidDpopProofException;
@@ -20,13 +20,13 @@
2020
import java.time.Instant;
2121
import java.util.*;
2222

23-
public class DPoPProofValidator {
23+
class DPoPProofValidator {
2424

2525
private final AuthOptions options;
2626
private final ObjectMapper objectMapper = new ObjectMapper();;
2727

2828

29-
public DPoPProofValidator(AuthOptions options) {
29+
DPoPProofValidator(AuthOptions options) {
3030
this.options = options;
3131
}
3232

@@ -38,7 +38,7 @@ public DPoPProofValidator(AuthOptions options) {
3838
* @param requestInfo HTTP request info: method and URL
3939
* @throws BaseAuthException if the DPoP proof is invalid.
4040
*/
41-
public void validate(String dpopProof, DecodedJWT decodedJwtToken, HttpRequestInfo requestInfo)
41+
void validate(String dpopProof, DecodedJWT decodedJwtToken, HttpRequestInfo requestInfo)
4242
throws BaseAuthException {
4343

4444
DecodedJWT proofJwt = decodeDPoP(dpopProof);
@@ -197,7 +197,7 @@ String calculateJwkThumbprint(Map<String, Object> jwk) throws BaseAuthException
197197
}
198198
}
199199

200-
public static ECPublicKey convertJwkToEcPublicKey(Map<String, Object> jwkMap)
200+
static ECPublicKey convertJwkToEcPublicKey(Map<String, Object> jwkMap)
201201
throws JwkException {
202202

203203
Jwk jwk = Jwk.fromValues(jwkMap);

auth0-api-java/src/main/java/com/auth0/DisabledDPoPAuthentication.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
import com.auth0.jwt.interfaces.DecodedJWT;
66
import com.auth0.models.AuthenticationContext;
77
import com.auth0.models.HttpRequestInfo;
8-
import com.auth0.validators.JWTValidator;
9-
10-
import java.util.Map;
118

129
class DisabledDPoPAuthentication extends AbstractAuthentication {
1310

@@ -17,18 +14,16 @@ public DisabledDPoPAuthentication(JWTValidator jwtValidator, TokenExtractor extr
1714

1815
/**
1916
* Authenticates the request when DPoP Mode is Disabled (Accepts only Bearer tokens) .
20-
* @param headers request headers
2117
* @param requestInfo HTTP request info
2218
* @return AuthenticationContext with JWT claims
2319
* @throws BaseAuthException if validation fails
2420
*/
2521
@Override
26-
public AuthenticationContext authenticate(Map<String, String> headers, HttpRequestInfo requestInfo)
22+
public AuthenticationContext authenticate(HttpRequestInfo requestInfo)
2723
throws BaseAuthException {
2824

29-
Map<String, String> normalizedHeader = normalize(headers);
3025
try {
31-
DecodedJWT jwt = validateBearerToken(normalizedHeader, requestInfo);
26+
DecodedJWT jwt = validateBearerToken(requestInfo);
3227

3328
return buildContext(jwt);
3429
} catch (BaseAuthException ex){

0 commit comments

Comments
 (0)