Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:

jobs:
gradle:
runs-on: ubuntu-22.04-2cpu-8ram-75ssd
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-java@v5
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ on:

jobs:
claude-review:
uses: atko-cic/ai-pr-analyzer-gh-action/.github/workflows/claude-code-review.yml@main
uses: auth0/ai-pr-analyzer-gh-action/.github/workflows/claude-code-review.yml@main
40 changes: 1 addition & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,45 +51,7 @@ The core library (`auth0-api-java`) is currently an internal module used by the
- JWT validation with Auth0 JWKS integration
- DPoP proof validation per [RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)
- Flexible authentication strategies
- Comprehensive claim validation

## 🔧 Advanced Configuration

### Custom Claim Validation

While the Spring Boot integration provides automatic validation, developers can access the underlying `auth0-api-java` validation utilities for custom scenarios:

```java
@RestController
public class AdvancedController {

@Autowired
private AuthClient authClient;

@GetMapping("/api/custom-validation")
public ResponseEntity<String> customValidation(HttpServletRequest request) {
try {
String token = extractTokenFromRequest(request);
JWTValidator validator = new JWTValidator(authClient.getAuthOptions());

DecodedJWT jwt = validator.validateTokenWithClaimEquals(token, "role", "admin");

return ResponseEntity.ok("Advanced validation passed");

} catch (BaseAuthException e) {
return ResponseEntity.status(401).body("Validation failed: " + e.getMessage());
}
}

private String extractTokenFromRequest(HttpServletRequest request) {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
return authHeader.substring(7);
}
throw new IllegalArgumentException("No Bearer token found");
}
}
```

## 📚 Documentation

Expand Down Expand Up @@ -135,7 +97,7 @@ The core library (`auth0-api-java`) is bundled as an internal dependency within
4. Add tests for new functionality
5. Ensure all tests pass: `./gradlew test`
6. Ensure your commits are signed
7. 7Submit a pull request
7. Submit a pull request

## 📄 License

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ protected AbstractAuthentication(JWTValidator jwtValidator, TokenExtractor extra
/**
* Concrete method to validate Bearer token headers and JWT claims.
*/
protected DecodedJWT validateBearerToken(Map<String, String> headers) throws BaseAuthException {
protected DecodedJWT validateBearerToken(Map<String, String> headers, HttpRequestInfo httpRequestInfo) throws BaseAuthException {
AuthToken authToken = extractor.extractBearer(headers);
return jwtValidator.validateToken(authToken.getAccessToken());
return jwtValidator.validateToken(authToken.getAccessToken(), headers, httpRequestInfo);
}

/**
Expand All @@ -42,7 +42,7 @@ protected DecodedJWT validateDpopTokenAndProof(Map<String, String> headers, Http
AuthValidatorHelper.validateHttpMethodAndHttpUrl(requestInfo);

AuthToken authToken = extractor.extractDPoPProofAndDPoPToken(headers);
DecodedJWT decodedJwtToken = jwtValidator.validateToken(authToken.getAccessToken());
DecodedJWT decodedJwtToken = jwtValidator.validateToken(authToken.getAccessToken(), headers, requestInfo);

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public AuthenticationContext authenticate(Map<String, String> headers, HttpReque
scheme = extractor.getScheme(normalizedHeader);

if (scheme.equalsIgnoreCase(AuthConstants.BEARER_SCHEME)) {
DecodedJWT jwtToken = validateBearerToken(normalizedHeader);
DecodedJWT jwtToken = validateBearerToken(normalizedHeader, requestInfo);
AuthValidatorHelper.validateNoDpopPresence(normalizedHeader, jwtToken);
return buildContext(jwtToken);
}
Expand Down
4 changes: 2 additions & 2 deletions auth0-api-java/src/main/java/com/auth0/AuthClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import com.auth0.exception.BaseAuthException;
import com.auth0.models.AuthenticationContext;
import com.auth0.validators.DPoPProofValidator;
import com.auth0.validators.JWTValidator;
import com.auth0.models.AuthOptions;
import com.auth0.models.HttpRequestInfo;
import com.auth0.validators.DPoPProofValidator;
import com.auth0.validators.JWTValidator;

import java.util.Map;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public AuthenticationContext authenticate(Map<String, String> headers, HttpReque

Map<String, String> normalizedHeader = normalize(headers);
try {
DecodedJWT jwt = validateBearerToken(normalizedHeader);
DecodedJWT jwt = validateBearerToken(normalizedHeader, requestInfo);

return buildContext(jwt);
} catch (BaseAuthException ex){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import com.auth0.models.AuthOptions;
import com.auth0.models.AuthenticationContext;
import com.auth0.models.HttpRequestInfo;
import com.auth0.validators.JWTValidator;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
import com.auth0.jwk.UrlJwkProvider;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.models.HttpRequestInfo;

import java.security.interfaces.RSAPublicKey;
import java.util.Map;

import static com.auth0.jwt.JWT.require;

Expand Down Expand Up @@ -64,7 +67,7 @@ public JWTValidator(AuthOptions authOptions, JwkProvider jwkProvider) {
* @return the decoded and verified JWT
* @throws BaseAuthException if validation fails
*/
public DecodedJWT validateToken(String token) throws BaseAuthException {
public DecodedJWT validateToken(String token, Map<String, String> headers, HttpRequestInfo httpRequestInfo) throws BaseAuthException {

if (token == null || token.trim().isEmpty()) {
throw new MissingRequiredArgumentException("access_token");
Expand All @@ -89,9 +92,9 @@ public DecodedJWT validateToken(String token) throws BaseAuthException {
/**
* Validates a JWT and ensures all required scopes are present.
*/
public DecodedJWT validateTokenWithRequiredScopes(String token, String... requiredScopes)
public DecodedJWT validateTokenWithRequiredScopes(String token, Map<String, String> headers, HttpRequestInfo httpRequestInfo, String... requiredScopes)
throws BaseAuthException {
DecodedJWT jwt = validateToken(token);
DecodedJWT jwt = validateToken(token, headers, httpRequestInfo);
try {
ClaimValidator.checkRequiredScopes(jwt, requiredScopes);
return jwt;
Expand All @@ -103,9 +106,9 @@ public DecodedJWT validateTokenWithRequiredScopes(String token, String... requir
/**
* Validates a JWT and ensures it has *any* of the provided scopes.
*/
public DecodedJWT validateTokenWithAnyScope(String token, String... scopes)
public DecodedJWT validateTokenWithAnyScope(String token, Map<String, String> headers, HttpRequestInfo httpRequestInfo, String... scopes)
throws BaseAuthException {
DecodedJWT jwt = validateToken(token);
DecodedJWT jwt = validateToken(token, headers, httpRequestInfo);
try {
ClaimValidator.checkAnyScope(jwt, scopes);
return jwt;
Expand All @@ -117,9 +120,9 @@ public DecodedJWT validateTokenWithAnyScope(String token, String... scopes)
/**
* Validates a JWT and ensures a claim equals the expected value.
*/
public DecodedJWT validateTokenWithClaimEquals(String token, String claim, Object expected)
public DecodedJWT validateTokenWithClaimEquals(String token, Map<String, String> headers, HttpRequestInfo httpRequestInfo, String claim, Object expected)
throws BaseAuthException {
DecodedJWT jwt = validateToken(token);
DecodedJWT jwt = validateToken(token, headers, httpRequestInfo);
try {
ClaimValidator.checkClaimEquals(jwt, claim, expected);
return jwt;
Expand All @@ -131,9 +134,9 @@ public DecodedJWT validateTokenWithClaimEquals(String token, String claim, Objec
/**
* Validates a JWT and ensures a claim includes all expected values.
*/
public DecodedJWT validateTokenWithClaimIncludes(String token, String claim, Object... expectedValues)
public DecodedJWT validateTokenWithClaimIncludes(String token, Map<String, String> headers, HttpRequestInfo httpRequestInfo, String claim, Object... expectedValues)
throws BaseAuthException {
DecodedJWT jwt = validateToken(token);
DecodedJWT jwt = validateToken(token, headers, httpRequestInfo);
try {
ClaimValidator.checkClaimIncludes(jwt, claim, expectedValues);
return jwt;
Expand All @@ -142,9 +145,9 @@ public DecodedJWT validateTokenWithClaimIncludes(String token, String claim, Obj
}
}

public DecodedJWT validateTokenWithClaimIncludesAny(String token, String claim, Object... expectedValues)
public DecodedJWT validateTokenWithClaimIncludesAny(String token, Map<String, String> headers, HttpRequestInfo httpRequestInfo, String claim, Object... expectedValues)
throws BaseAuthException {
DecodedJWT jwt = validateToken(token);
DecodedJWT jwt = validateToken(token, headers, httpRequestInfo);
try {
ClaimValidator.checkClaimIncludesAny(jwt, claim, expectedValues);
return jwt;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@ public void validateBearerToken_shouldExtractAndValidate() throws Exception {
DecodedJWT jwt = mock(DecodedJWT.class);

when(extractor.extractBearer(anyMap())).thenReturn(token);
when(jwtValidator.validateToken("access")).thenReturn(jwt);
when(jwtValidator.validateToken(eq("access"), anyMap(), any())).thenReturn(jwt);

Map<String, String> headers = new HashMap<>();
headers.put("authorization", "Bearer access");

DecodedJWT result = authSystem.validateBearerToken(headers);
DecodedJWT result = authSystem.validateBearerToken(headers, null);

assertThat(result).isSameAs(jwt);
}
Expand All @@ -98,7 +98,7 @@ public void validateDpopTokenAndProof_shouldValidateEverything() throws Exceptio
new HttpRequestInfo("GET", "https://api.example.com", null);

when(extractor.extractDPoPProofAndDPoPToken(anyMap())).thenReturn(token);
when(jwtValidator.validateToken("access")).thenReturn(jwt);
when(jwtValidator.validateToken(eq("access"), anyMap(), any())).thenReturn(jwt);

Map<String, String> headers = new HashMap<>();
headers.put("authorization", "DPoP access");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,19 @@ public void authenticate_shouldAcceptBearerToken() throws Exception {
when(extractor.extractBearer(anyMap())).thenReturn(
new AuthToken("token", null, null)
);
when(jwtValidator.validateToken("token")).thenReturn(jwt);

Map<String, String> normalizedHeaders = new HashMap<>();
normalizedHeaders.put("authorization", "Bearer token");

when(jwtValidator.validateToken(eq("token"), eq(normalizedHeaders), any())).thenReturn(jwt);

Map<String, String> headers = new HashMap<>();
headers.put("authorization", "Bearer token");

AuthenticationContext ctx = auth.authenticate(headers, null);

assertThat(ctx).isNotNull();
verify(jwtValidator).validateToken("token");
verify(jwtValidator).validateToken("token", normalizedHeaders, null);
verifyNoInteractions(dpopProofValidator);
}

Expand All @@ -69,7 +73,7 @@ public void authenticate_shouldAcceptDpopToken() throws Exception {
when(extractor.extractDPoPProofAndDPoPToken(anyMap())).thenReturn(
new com.auth0.models.AuthToken("token", "proof", null)
);
when(jwtValidator.validateToken("token")).thenReturn(jwt);
when(jwtValidator.validateToken(eq("token"), anyMap(), any())).thenReturn(jwt);
Map<String, String> headers = new HashMap<>();
headers.put("authorization", "DPoP token");
headers.put("dpop", "proof");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ public void authenticate_shouldAcceptBearerToken() throws Exception {
when(extractor.extractBearer(anyMap())).thenReturn(
new com.auth0.models.AuthToken("token", null, null)
);
when(jwtValidator.validateToken("token")).thenReturn(jwt);

Map<String, String> normalizedHeaders = new HashMap<>();
normalizedHeaders.put("authorization", "Bearer token");

when(jwtValidator.validateToken(eq("token"), eq(normalizedHeaders), any())).thenReturn(jwt);
Map<String, String> headers = new HashMap<>();
headers.put("authorization", "Bearer token");

Expand All @@ -43,7 +47,7 @@ public void authenticate_shouldAcceptBearerToken() throws Exception {


assertThat(ctx).isNotNull();
verify(jwtValidator).validateToken("token");
verify(jwtValidator).validateToken("token", normalizedHeaders, null);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public void authenticate_shouldAcceptDpopToken() throws Exception {
when(extractor.extractDPoPProofAndDPoPToken(anyMap())).thenReturn(
new com.auth0.models.AuthToken("token", "proof", null)
);
when(jwtValidator.validateToken("token")).thenReturn(jwt);
when(jwtValidator.validateToken(eq("token"), anyMap(), any())).thenReturn(jwt);

Map<String, String> headers = new HashMap<>();
headers.put("authorization", "DPoP token");
Expand Down
Loading
Loading