From f474343721ee59663ac6fc26f91200c82be680c0 Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Wed, 11 Mar 2026 18:16:17 +0100 Subject: [PATCH] RFC 9068: Set typ header to at+jwt for JWT access tokens Per RFC 9068 Section 2.1, JWT access tokens MUST include a typ header parameter set to "at+jwt". This change: - Adds TYPE_AT_JWT constant to JoseConstants - Adds AT_JWT member to JoseType enum with proper lookup - Sets the at+jwt type on both JWS and JWE headers when producing JWT access tokens in AbstractOAuthDataProvider Closes #990 Co-Authored-By: Claude Opus 4.6 --- .../apache/cxf/rs/security/jose/common/JoseConstants.java | 1 + .../org/apache/cxf/rs/security/jose/common/JoseType.java | 5 ++++- .../oauth2/provider/AbstractOAuthDataProvider.java | 7 ++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java index 092581d3fd0..017f814411d 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java @@ -43,6 +43,7 @@ public final class JoseConstants extends RSSecurityConstants { public static final String JWS_HEADER_B64_STATUS_HEADER = "b64"; public static final String TYPE_JWT = "JWT"; + public static final String TYPE_AT_JWT = "at+jwt"; public static final String TYPE_JOSE = "JOSE"; public static final String TYPE_JOSE_JSON = "JOSE+JSON"; public static final String MEDIA_TYPE_JOSE = "application/jose"; diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseType.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseType.java index 647fb163fe0..cea5fdf0a8b 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseType.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseType.java @@ -22,7 +22,8 @@ public enum JoseType { JOSE(JoseConstants.TYPE_JOSE), JOSE_JSON(JoseConstants.TYPE_JOSE_JSON), - JWT(JoseConstants.TYPE_JWT); + JWT(JoseConstants.TYPE_JWT), + AT_JWT(JoseConstants.TYPE_AT_JWT); private final String type; JoseType(String type) { @@ -33,6 +34,8 @@ public static JoseType getType(String type) { return null; } else if (JoseConstants.TYPE_JOSE_JSON.equals(type)) { return JOSE_JSON; + } else if (JoseConstants.TYPE_AT_JWT.equals(type)) { + return AT_JWT; } else { return valueOf(type); } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java index 26f74928490..a78f0d9df87 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/AbstractOAuthDataProvider.java @@ -27,6 +27,7 @@ import jakarta.ws.rs.core.MultivaluedMap; import org.apache.cxf.jaxrs.ext.MessageContext; import org.apache.cxf.rs.security.jose.common.JoseConstants; +import org.apache.cxf.rs.security.jose.common.JoseType; import org.apache.cxf.rs.security.jose.jwt.JwtClaims; import org.apache.cxf.rs.security.jose.jwt.JwtConstants; import org.apache.cxf.rs.security.jose.jwt.JwtToken; @@ -669,7 +670,11 @@ protected String processJwtAccessToken(JwtClaims jwtCliams) { // It will JWS-sign (default) and/or JWE-encrypt OAuthJoseJwtProducer processor = getJwtAccessTokenProducer() == null ? new OAuthJoseJwtProducer() : getJwtAccessTokenProducer(); - return processor.processJwt(new JwtToken(jwtCliams)); + JwtToken jwt = new JwtToken(jwtCliams); + // RFC 9068 Section 2.1: JWT access tokens MUST set typ to "at+jwt" + jwt.getJwsHeaders().setType(JoseType.AT_JWT); + jwt.getJweHeaders().setType(JoseType.AT_JWT); + return processor.processJwt(jwt); } public Map getJwtAccessTokenClaimMap() {