|
| 1 | +# Copilot Instructions for Auth0 Java SDK |
| 2 | + |
| 3 | +## Architecture Overview |
| 4 | + |
| 5 | +Multi-module Gradle project implementing OAuth2/JWT authentication with DPoP support: |
| 6 | + |
| 7 | +- `auth0-api-java`: Java 8 compatible core library (JWT validation, JWKS, DPoP proofs) |
| 8 | +- `auth0-springboot-api`: Java 17 Spring Boot auto-configuration and filters |
| 9 | +- `auth0-springboot-api-playground`: Working example application with security integration |
| 10 | + |
| 11 | +**Key Design**: Strategy pattern for authentication modes, factory-based client creation, comprehensive JWT validation with Auth0 JWKS integration. |
| 12 | + |
| 13 | +## Core Authentication Pattern |
| 14 | + |
| 15 | +**Main Entry Point**: `AuthClient.from(AuthOptions)` → creates orchestrator with strategy pattern: |
| 16 | + |
| 17 | +```java |
| 18 | +// Factory method selects authentication strategy based on DPoP mode |
| 19 | +AuthClient client = AuthClient.from( |
| 20 | + new AuthOptions.Builder() |
| 21 | + .domain("tenant.auth0.com") |
| 22 | + .audience("https://api.example.com") |
| 23 | + .dpopMode(DPoPMode.ALLOWED) // DISABLED, ALLOWED (default), REQUIRED |
| 24 | + .dpopIatOffsetSeconds(300) // DPoP proof time window |
| 25 | + .dpopIatLeewaySeconds(60) // DPoP proof time leeway |
| 26 | + .build() |
| 27 | +); |
| 28 | + |
| 29 | +// Single verification method for all authentication modes |
| 30 | +AuthenticationContext context = client.verifyRequest(headers, httpRequestInfo); |
| 31 | +``` |
| 32 | + |
| 33 | +**Three Authentication Strategies** extending `AbstractAuthentication`: |
| 34 | + |
| 35 | +- `DisabledDPoPAuthentication`: Bearer tokens only |
| 36 | +- `AllowedDPoPAuthentication`: Bearer OR DPoP tokens (auto-detects scheme) |
| 37 | +- `RequiredDPoPAuthentication`: DPoP tokens only |
| 38 | + |
| 39 | +**Strategy Selection** happens in `AuthClient` constructor based on `dpopMode`. |
| 40 | + |
| 41 | +## JWT Validation & Claims Patterns |
| 42 | + |
| 43 | +**Always use `JWTValidator` convenience methods** - they handle JWKS fetching and validation: |
| 44 | + |
| 45 | +```java |
| 46 | +// Scope validation |
| 47 | +DecodedJWT jwt = validator.validateTokenWithRequiredScopes(token, "read:users", "write:users"); |
| 48 | +DecodedJWT jwt = validator.validateTokenWithAnyScope(token, "admin", "user"); |
| 49 | + |
| 50 | +// Claim validation |
| 51 | +DecodedJWT jwt = validator.validateTokenWithClaimEquals(token, "role", "admin"); |
| 52 | +DecodedJWT jwt = validator.validateTokenWithClaimIncludes(token, "permissions", "create"); |
| 53 | + |
| 54 | +// Manual validation for complex scenarios |
| 55 | +DecodedJWT jwt = validator.validateToken(token); |
| 56 | +ClaimValidators.checkRequiredScopes(jwt, "admin"); |
| 57 | +``` |
| 58 | + |
| 59 | +**Key Classes**: |
| 60 | + |
| 61 | +- `JWTValidator`: Core JWT validation with JWKS integration (uses Auth0's `jwks-rsa` library) |
| 62 | +- `ClaimValidator`: Manual claim validation helpers |
| 63 | +- `DPoPProofValidator`: ES256 signature and proof claim validation |
| 64 | +- `TokenExtractor`: Extracts Bearer/DPoP tokens from Authorization headers |
| 65 | + |
| 66 | +## Build & Test Workflows |
| 67 | + |
| 68 | +**Module-specific commands** (root gradle builds are disabled): |
| 69 | + |
| 70 | +```bash |
| 71 | +# Core library |
| 72 | +./gradlew :auth0-api-java:build |
| 73 | +./gradlew :auth0-api-java:test |
| 74 | + |
| 75 | +# Spring Boot integration |
| 76 | +./gradlew :auth0-springboot-api:build |
| 77 | +./gradlew :auth0-springboot-api:test |
| 78 | + |
| 79 | +# Playground/example |
| 80 | +./gradlew :auth0-springboot-api-playground:bootRun |
| 81 | +``` |
| 82 | + |
| 83 | +**Test Patterns**: |
| 84 | + |
| 85 | +- **JUnit versions**: JUnit 4 (auth0-api-java), JUnit 5 (Spring Boot modules) |
| 86 | +- **JWT testing**: Mock `JwkProvider`, generate RSA keypairs, use real JWT/JWKS validation |
| 87 | +- **DPoP testing**: Generate ES256 keypairs, create valid/invalid DPoP proofs |
| 88 | +- **AssertJ**: Preferred assertion library (`assertThat()`, `assertThatThrownBy()`) |
| 89 | + |
| 90 | +**JAR artifacts**: All modules auto-generate sources/javadoc JARs via `withSourcesJar()`/`withJavadocJar()` |
| 91 | + |
| 92 | +## Spring Boot Integration |
| 93 | + |
| 94 | +**Auto-Configuration**: Add dependency, configure properties, inject beans: |
| 95 | + |
| 96 | +```yaml |
| 97 | +# application.yml |
| 98 | +auth0: |
| 99 | + domain: "dev-tenant.us.auth0.com" |
| 100 | + audience: "https://api.example.com/v2/" |
| 101 | + dpopMode: ALLOWED |
| 102 | + dpopIatOffsetSeconds: 300 |
| 103 | + dpopIatLeewaySeconds: 60 |
| 104 | +``` |
| 105 | +
|
| 106 | +```java |
| 107 | +// Auto-configured beans available for injection |
| 108 | +@Autowired private AuthClient authClient; |
| 109 | +@Autowired private AuthOptions authOptions; |
| 110 | +@Autowired private Auth0AuthenticationFilter authFilter; |
| 111 | +``` |
| 112 | + |
| 113 | +**Security Configuration Pattern**: |
| 114 | + |
| 115 | +```java |
| 116 | +@Configuration |
| 117 | +public class SecurityConfig { |
| 118 | + @Bean |
| 119 | + SecurityFilterChain apiSecurity(HttpSecurity http, Auth0AuthenticationFilter authFilter) throws Exception { |
| 120 | + return http.csrf(csrf -> csrf.disable()) |
| 121 | + .sessionManagement(s -> s.sessionCreationPolicy(STATELESS)) |
| 122 | + .authorizeHttpRequests(auth -> auth |
| 123 | + .requestMatchers("/api/protected").authenticated() |
| 124 | + .anyRequest().permitAll()) |
| 125 | + .addFilterBefore(authFilter, UsernamePasswordAuthenticationFilter.class) |
| 126 | + .build(); |
| 127 | + } |
| 128 | +} |
| 129 | +``` |
| 130 | + |
| 131 | +**Classes**: `Auth0AutoConfiguration`, `Auth0Properties`, `Auth0AuthenticationFilter`, `Auth0AuthenticationToken` |
| 132 | + |
| 133 | +## DPoP (Demonstration of Proof-of-Possession) Patterns |
| 134 | + |
| 135 | +**DPoP Mode Behavior**: |
| 136 | + |
| 137 | +- `DISABLED`: Only Bearer tokens accepted |
| 138 | +- `ALLOWED` (default): Bearer OR DPoP tokens accepted (auto-detects Authorization scheme) |
| 139 | +- `REQUIRED`: Only DPoP tokens accepted |
| 140 | + |
| 141 | +**Critical Validation Rules**: |
| 142 | + |
| 143 | +```java |
| 144 | +// ALLOWED mode: Bearer token + DPoP proof header = invalid_request |
| 145 | +if (scheme.equals(BEARER) && dpopProofPresent) { |
| 146 | + throw new JWTValidationException("Bearer scheme cannot include DPoP proof header"); |
| 147 | +} |
| 148 | + |
| 149 | +// DPoP bound token (cnf.jkt claim) with Bearer scheme = invalid_token |
| 150 | +if (tokenIsDPoPBound && scheme.equals(BEARER)) { |
| 151 | + throw new JWTValidationException("DPoP bound token requires DPoP scheme"); |
| 152 | +} |
| 153 | +``` |
| 154 | + |
| 155 | +**DPoP Proof Validation**: |
| 156 | + |
| 157 | +- **Header**: `typ: "dpop+jwt"`, `alg: "ES256"`, embedded JWK required |
| 158 | +- **Claims**: `htm` (HTTP method), `htu` (HTTP URI), `iat` (issued at with time windows) |
| 159 | +- **Token Binding**: Access token's `cnf.jkt` must match DPoP proof JWK thumbprint (RFC 7638) |
| 160 | + |
| 161 | +**WWW-Authenticate Challenges**: |
| 162 | + |
| 163 | +- ALLOWED/DISABLED: `Bearer realm=api; DPoP algs=ES256` |
| 164 | +- REQUIRED: `DPoP algs=ES256` |
| 165 | +- DPoP errors: `DPoP error=invalid_dpop_proof; error_description="..."` |
| 166 | + |
| 167 | +## Package Structure & Key Classes |
| 168 | + |
| 169 | +**Core (`auth0-api-java/com/auth0/`)**: |
| 170 | + |
| 171 | +- Root: `AuthClient` (main entry), `AbstractAuthentication` (strategy base), authentication strategies |
| 172 | +- `validators/`: `JWTValidator`, `DPoPProofValidator`, `ClaimValidator` |
| 173 | +- `models/`: `AuthOptions`, `AuthenticationContext`, `HttpRequestInfo`, `AuthToken` |
| 174 | +- `exception/`: `BaseAuthException` hierarchy (`InvalidTokenException`, `InsufficientScopeException`, etc.) |
| 175 | +- `enums/`: `DPoPMode`, `AuthScheme` |
| 176 | +- `examples/`: `Auth0ApiExample` (working HTTP server example) |
| 177 | + |
| 178 | +**Spring Boot (`auth0-springboot-api/com/auth0/spring/boot/`)**: |
| 179 | + |
| 180 | +- `Auth0AutoConfiguration`: Bean definitions and auto-configuration |
| 181 | +- `Auth0Properties`: YAML configuration binding (`@ConfigurationProperties`) |
| 182 | +- `Auth0AuthenticationFilter`: Spring Security filter integration |
| 183 | +- `Auth0AuthenticationToken`: Spring Security authentication token |
| 184 | + |
| 185 | +**Dependencies**: |
| 186 | + |
| 187 | +- Core: `jackson-databind`, `httpclient`, `java-jwt`, `jwks-rsa` |
| 188 | +- Spring: `spring-boot-starter-web`, `spring-boot-starter-security` |
| 189 | + |
| 190 | +## Essential Reference Files |
| 191 | + |
| 192 | +- **`JWT_VALIDATION_GUIDE.md`**: Complete JWT validation examples with working code |
| 193 | +- **`auth0-api-java/src/main/java/com/auth0/examples/Auth0ApiExample.java`**: HTTP server with authentication |
| 194 | +- **`auth0-springboot-api-playground/`**: Full Spring Boot integration example with SecurityConfig |
| 195 | +- **Test files**: Comprehensive patterns for mocking JWKS, generating keypairs, testing DPoP proofs |
0 commit comments