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 .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.0-beta.1
1.0.0-beta.0
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
![Java Version](https://img.shields.io/badge/java-8%2B-blue)
![License](https://img.shields.io/badge/license-MIT-green)

A comprehensive Java library for Auth0 JWT authentication with built-in **DPoP (Demonstration of Proof-of-Possession)** support. This multi-module project provides both a core authentication library and Spring Boot integration for secure API development.
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.

## 🏗️ Architecture Overview

Expand Down Expand Up @@ -37,15 +37,15 @@ If you're building a Spring Boot application, use the Spring Boot integration:
<dependency>
<groupId>com.auth0</groupId>
<artifactId>auth0-springboot-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
<version>1.0.0-beta.0</version>
</dependency>
```

**👉 [Get started with Spring Boot integration →](./auth0-springboot-api/README.md)**

### For Core Java Applications

The core library (`auth0-api-java`) is currently an internal module used by the Spring Boot integration. It provides:
It provides:

- JWT validation with Auth0 JWKS integration
- DPoP proof validation per [RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)
Expand Down Expand Up @@ -78,11 +78,11 @@ This project uses Gradle with a multi-module setup:

## 📦 Publishing

Only the Spring Boot integration module is published as a public artifact:
Spring Boot integration module is published as a public artifact:

| Module | Group ID | Artifact ID | Version | Status |
| ---------------------- | ----------- | ---------------------- | ---------------- | ---------------- |
| `auth0-springboot-api` | `com.auth0` | `auth0-springboot-api` | `1.0.0-SNAPSHOT` | 📦 **Published** |
| Module | Group ID | Artifact ID | Version | Status |
| ---------------------- | ----------- | ---------------------- |----------------| ---------------- |
| `auth0-springboot-api` | `com.auth0` | `auth0-springboot-api` | `1.0.0-beta.0` | 📦 **Published** |

The core library (`auth0-api-java`) is bundled as an internal dependency within the Spring Boot module and is not published separately.

Expand Down
48 changes: 2 additions & 46 deletions auth0-springboot-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ This library builds on top of the standard Spring Security JWT authentication, p

## Requirements

- This library currently supports **Java 8+** for core functionality
- **Spring Boot 3.2+** (requires Java 17+) for Spring Boot integration

## Getting Started
Expand All @@ -24,15 +23,15 @@ Add the dependency via Maven:
<dependency>
<groupId>com.auth0</groupId>
<artifactId>auth0-springboot-api</artifactId>
<version>1.0.0</version>
<version>1.0.0-beta.0</version>
</dependency>
```

or Gradle:

```gradle
dependencies {
implementation 'com.auth0:auth0-springboot-api:1.0.0'
implementation 'com.auth0:auth0-springboot-api:1.0.0-beta.0'
}
```

Expand Down Expand Up @@ -176,49 +175,6 @@ curl -H "Authorization: DPoP <jwt_token>" \

## Advanced Features

### Manual JWT Validation

For scenarios requiring manual token validation, inject and use the `AuthClient`:

```java
@RestController
public class CustomController {

@Autowired
private AuthClient authClient;

@PostMapping("/api/custom-validation")
public ResponseEntity<String> customValidation(HttpServletRequest request) {
try {
// Extract headers and request info
Map<String, String> headers = extractHeaders(request);
HttpRequestInfo requestInfo = new HttpRequestInfo(
request.getMethod(),
request.getRequestURL().toString(),
null
);

// Manual validation
AuthenticationContext context = authClient.verifyRequest(headers, requestInfo);
String userId = (String) context.getClaims().get("sub");

return ResponseEntity.ok("Token valid for user: " + userId);

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

private Map<String, String> extractHeaders(HttpServletRequest request) {
Map<String, String> headers = new HashMap<>();
Collections.list(request.getHeaderNames()).forEach(headerName ->
headers.put(headerName, request.getHeader(headerName))
);
return headers;
}
}
```

### Custom Claim Validation

Access JWT claims directly through `Auth0AuthenticationToken`'s clean API:
Expand Down
8 changes: 8 additions & 0 deletions auth0-springboot-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
id 'java-library'
id 'org.springframework.boot' version '3.2.0'
id 'io.spring.dependency-management' version '1.1.4'
id "com.diffplug.spotless" version "6.25.0"
}

apply from: rootProject.file('gradle/versioning.gradle')
Expand Down Expand Up @@ -50,5 +51,12 @@ jar {
archiveClassifier = ''
}

spotless {
java {
googleJavaFormat()
target 'src/**/*.java'
}
}


logger.lifecycle("Using version ${version} for ${name} group ${group}")
Original file line number Diff line number Diff line change
Expand Up @@ -11,134 +11,127 @@
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.*;

public class Auth0AuthenticationFilter extends OncePerRequestFilter {

private final AuthClient authClient;
private final AuthClient authClient;

private final Auth0Properties auth0Properties;
private final Auth0Properties auth0Properties;

public Auth0AuthenticationFilter(AuthClient authClient, Auth0Properties auth0Properties) {
this.authClient = authClient;
this.auth0Properties = auth0Properties;
}
public Auth0AuthenticationFilter(AuthClient authClient, Auth0Properties auth0Properties) {
this.authClient = authClient;
this.auth0Properties = auth0Properties;
}

@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain chain
) throws ServletException, IOException {
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {

try {
try {

Map<String, String> headers = extractHeaders(request);
Map<String, String> headers = extractHeaders(request);

String authorizationHeader = headers.get("authorization");
if (authorizationHeader == null || authorizationHeader.trim().isEmpty()) {
chain.doFilter(request, response);
return;
}
String authorizationHeader = headers.get("authorization");
if (authorizationHeader == null || authorizationHeader.trim().isEmpty()) {
chain.doFilter(request, response);
return;
}

HttpRequestInfo requestInfo = extractRequestInfo(request);
HttpRequestInfo requestInfo = extractRequestInfo(request);

AuthenticationContext ctx = authClient.verifyRequest(headers, requestInfo);
AuthenticationContext ctx = authClient.verifyRequest(headers, requestInfo);

Auth0AuthenticationToken authentication = new Auth0AuthenticationToken(ctx);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
Auth0AuthenticationToken authentication = new Auth0AuthenticationToken(ctx);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

SecurityContextHolder.getContext().setAuthentication(authentication);
SecurityContextHolder.getContext().setAuthentication(authentication);

chain.doFilter(request, response);
chain.doFilter(request, response);

} catch (BaseAuthException ex) {
response.setStatus(ex.getStatusCode());
} catch (BaseAuthException ex) {
response.setStatus(ex.getStatusCode());

Map<String, String> exceptionHeaders = ex.getHeaders();
String wwwAuthenticate = exceptionHeaders.get("WWW-Authenticate");
Map<String, String> exceptionHeaders = ex.getHeaders();
String wwwAuthenticate = exceptionHeaders.get("WWW-Authenticate");

if (wwwAuthenticate != null) {
response.addHeader("WWW-Authenticate", wwwAuthenticate);
}
SecurityContextHolder.clearContext();
}
if (wwwAuthenticate != null) {
response.addHeader("WWW-Authenticate", wwwAuthenticate);
}
SecurityContextHolder.clearContext();
}
}

Map<String, String> extractHeaders(HttpServletRequest request)
throws MissingAuthorizationException {
Map<String, String> extractHeaders(HttpServletRequest request)
throws MissingAuthorizationException {

List<String> authHeaders = Collections.list(request.getHeaders("Authorization"));
if (authHeaders != null && authHeaders.size() > 1) {
String firstValue = authHeaders.get(0);
List<String> authHeaders = Collections.list(request.getHeaders("Authorization"));
if (authHeaders != null && authHeaders.size() > 1) {
String firstValue = authHeaders.get(0);

MissingAuthorizationException ex = new MissingAuthorizationException();
MissingAuthorizationException ex = new MissingAuthorizationException();

String[] parts = firstValue.trim().split("\\s+", 2);
String[] parts = firstValue.trim().split("\\s+", 2);

DPoPMode dpopMode = auth0Properties.getDpopMode();
if (dpopMode == null) {
dpopMode = DPoPMode.ALLOWED; // default fallback
}
DPoPMode dpopMode = auth0Properties.getDpopMode();
if (dpopMode == null) {
dpopMode = DPoPMode.ALLOWED; // default fallback
}

List<String> challenges = WWWAuthenticateBuilder.buildChallenges(
ex.getErrorCode(),
ex.getErrorDescription(),
dpopMode,
parts[0].toLowerCase(Locale.ROOT)
);
List<String> challenges =
WWWAuthenticateBuilder.buildChallenges(
ex.getErrorCode(),
ex.getErrorDescription(),
dpopMode,
parts[0].toLowerCase(Locale.ROOT));

if (!challenges.isEmpty()) {
ex.addHeader("WWW-Authenticate", String.join(", ", challenges));
}
if (!challenges.isEmpty()) {
ex.addHeader("WWW-Authenticate", String.join(", ", challenges));
}

throw ex;
}

Map<String, String> headers = new HashMap<>();
Enumeration<String> names = request.getHeaderNames();

if (names != null) {
while (names.hasMoreElements()) {
String name = names.nextElement();
headers.put(
name.toLowerCase(Locale.ROOT),
request.getHeader(name)
);
}
}

return headers;
throw ex;
}

HttpRequestInfo extractRequestInfo(HttpServletRequest request) {
String htu = buildHtu(request);
return new HttpRequestInfo(request.getMethod(), htu, null);
Map<String, String> headers = new HashMap<>();
Enumeration<String> names = request.getHeaderNames();

if (names != null) {
while (names.hasMoreElements()) {
String name = names.nextElement();
headers.put(name.toLowerCase(Locale.ROOT), request.getHeader(name));
}
}

static String buildHtu(HttpServletRequest request) {
String scheme = request.getScheme().toLowerCase(Locale.ROOT);
String host = request.getServerName().toLowerCase(Locale.ROOT);
return headers;
}

int port = request.getServerPort();
boolean defaultPort =
(scheme.equals("http") && port == 80) ||
(scheme.equals("https") && port == 443);
HttpRequestInfo extractRequestInfo(HttpServletRequest request) {
String htu = buildHtu(request);
return new HttpRequestInfo(request.getMethod(), htu, null);
}

StringBuilder htu = new StringBuilder();
htu.append(scheme).append("://").append(host);
static String buildHtu(HttpServletRequest request) {
String scheme = request.getScheme().toLowerCase(Locale.ROOT);
String host = request.getServerName().toLowerCase(Locale.ROOT);

if (!defaultPort) {
htu.append(":").append(port);
}
int port = request.getServerPort();
boolean defaultPort =
(scheme.equals("http") && port == 80) || (scheme.equals("https") && port == 443);

htu.append(request.getRequestURI());
StringBuilder htu = new StringBuilder();
htu.append(scheme).append("://").append(host);

return htu.toString();
if (!defaultPort) {
htu.append(":").append(port);
}

htu.append(request.getRequestURI());

return htu.toString();
}
}
Loading
Loading