This is an automated email from the ASF dual-hosted git repository.

coheigea pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cxf.git


The following commit(s) were added to refs/heads/main by this push:
     new 62623da87f0 Perform validation for OIDC self-issued tokens (#3327)
62623da87f0 is described below

commit 62623da87f0680a01f9a7add9f5dcd807b3ab3a9
Author: Colm O hEigeartaigh <[email protected]>
AuthorDate: Thu Jul 23 09:49:44 2026 +0100

    Perform validation for OIDC self-issued tokens (#3327)
---
 .../rs/security/oidc/rp/OidcClaimsValidator.java   | 111 ++++--
 .../security/oidc/rp/OidcClaimsValidatorTest.java  | 420 +++++++++++++++++++++
 2 files changed, 492 insertions(+), 39 deletions(-)

diff --git 
a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClaimsValidator.java
 
b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClaimsValidator.java
index 2883c2fed46..abc80390e08 100644
--- 
a/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClaimsValidator.java
+++ 
b/rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/OidcClaimsValidator.java
@@ -57,60 +57,93 @@ public class OidcClaimsValidator extends 
OAuthJoseJwtConsumer {
         if (issuer == null && validateClaimsAlways) {
             throw new OAuthServiceException("Invalid issuer");
         }
-        if (supportSelfIssuedProvider && issuerId == null
-            && issuer != null && SELF_ISSUED_ISSUER.equals(issuer)) {
-            validateSelfIssuedProvider(claims, clientId, validateClaimsAlways);
+
+        boolean isSelfIssued = supportSelfIssuedProvider && issuerId == null
+            && issuer != null && SELF_ISSUED_ISSUER.equals(issuer);
+
+        if (isSelfIssued) {
+            // Self-issued specific: validate sub_jwk binding (OIDC Core ยง7)
+            validateSelfIssuedSubJwkBinding(claims);
         } else {
+            // Normal issuer validation
             if (issuer != null && !issuer.equals(issuerId)) {
                 throw new OAuthServiceException("Invalid issuer");
             }
-            // validate subject
-            if (claims.getSubject() == null) {
-                throw new OAuthServiceException("Invalid subject");
-            }
 
-            // validate authorized party
+            // validate authorized party (not applicable for self-issued)
             String authorizedParty = 
(String)claims.getClaim(IdToken.AZP_CLAIM);
             if (authorizedParty != null && !authorizedParty.equals(clientId)) {
                 throw new OAuthServiceException("Invalid authorized party");
             }
-            // validate audience
-            List<String> audiences = claims.getAudiences();
-            if (StringUtils.isEmpty(audiences) && validateClaimsAlways
-                || !StringUtils.isEmpty(audiences) && 
!audiences.contains(clientId)) {
-                throw new OAuthServiceException("Invalid audience");
-            }
+        }
 
-            // If strict time validation: if no issuedTime claim is set then 
an expiresAt claim must be set
-            // Otherwise: validate only if expiresAt claim is set
-            boolean expiredRequired =
-                validateClaimsAlways || strictTimeValidation && 
claims.getIssuedAt() == null;
-            try {
-                JwtUtils.validateJwtExpiry(claims, getClockOffset(), 
expiredRequired);
-            } catch (JwtException ex) {
-                throw new OAuthServiceException("ID Token has expired", ex);
-            }
+        // Common validations for both self-issued and normal issuers
+        validateSubject(claims);
+        validateAudience(claims, clientId, validateClaimsAlways);
+        validateTokenTime(claims, validateClaimsAlways);
+    }
 
-            // If strict time validation: If no expiresAt claim is set then an 
issuedAt claim must be set
-            // Otherwise: validate only if issuedAt claim is set
-            boolean issuedAtRequired =
-                validateClaimsAlways || strictTimeValidation && 
claims.getExpiryTime() == null;
-            try {
-                JwtUtils.validateJwtIssuedAt(claims, getTtl(), 
getClockOffset(), issuedAtRequired);
-            } catch (JwtException ex) {
-                throw new OAuthServiceException("Invalid issuedAt claim", ex);
-            }
+    private void validateSelfIssuedSubJwkBinding(JwtClaims claims) {
+        String subJwk = (String)claims.getClaim("sub_jwk");
+        if (subJwk == null) {
+            throw new OAuthServiceException("Missing sub_jwk claim for 
self-issued token");
+        }
 
-            // Validate nbf - but don't require it to be present
-            try {
-                JwtUtils.validateJwtNotBefore(claims, getClockOffset(), false);
-            } catch (JwtException ex) {
-                throw new OAuthServiceException("ID Token can not be used 
yet", ex);
+        try {
+            JsonWebKey publicKey = JwkUtils.readJwkKey(subJwk);
+            String thumbprint = JwkUtils.getThumbprint(publicKey);
+            String sub = claims.getSubject();
+            if (!thumbprint.equals(sub)) {
+                throw new OAuthServiceException("sub_jwk thumbprint does not 
match sub claim");
             }
+        } catch (OAuthServiceException ex) {
+            throw ex;
+        } catch (RuntimeException ex) {
+            throw new OAuthServiceException("Invalid sub_jwk claim: " + 
ex.getMessage(), ex);
+        }
+    }
+
+    private void validateSubject(JwtClaims claims) {
+        if (claims.getSubject() == null) {
+            throw new OAuthServiceException("Invalid subject");
+        }
+    }
+
+    private void validateAudience(JwtClaims claims, String clientId, boolean 
validateClaimsAlways) {
+        List<String> audiences = claims.getAudiences();
+        if (StringUtils.isEmpty(audiences) && validateClaimsAlways
+            || !StringUtils.isEmpty(audiences) && 
!audiences.contains(clientId)) {
+            throw new OAuthServiceException("Invalid audience");
         }
     }
 
-    private void validateSelfIssuedProvider(JwtClaims claims, String clientId, 
boolean validateClaimsAlways) {
+    private void validateTokenTime(JwtClaims claims, boolean 
validateClaimsAlways) {
+        // If strict time validation: if no issuedTime claim is set then an 
expiresAt claim must be set
+        // Otherwise: validate only if expiresAt claim is set
+        boolean expiredRequired =
+            validateClaimsAlways || strictTimeValidation && 
claims.getIssuedAt() == null;
+        try {
+            JwtUtils.validateJwtExpiry(claims, getClockOffset(), 
expiredRequired);
+        } catch (JwtException ex) {
+            throw new OAuthServiceException("ID Token has expired", ex);
+        }
+
+        // If strict time validation: If no expiresAt claim is set then an 
issuedAt claim must be set
+        // Otherwise: validate only if issuedAt claim is set
+        boolean issuedAtRequired =
+            validateClaimsAlways || strictTimeValidation && 
claims.getExpiryTime() == null;
+        try {
+            JwtUtils.validateJwtIssuedAt(claims, getTtl(), getClockOffset(), 
issuedAtRequired);
+        } catch (JwtException ex) {
+            throw new OAuthServiceException("Invalid issuedAt claim", ex);
+        }
+
+        // Validate nbf - but don't require it to be present
+        try {
+            JwtUtils.validateJwtNotBefore(claims, getClockOffset(), false);
+        } catch (JwtException ex) {
+            throw new OAuthServiceException("ID Token can not be used yet", 
ex);
+        }
     }
 
     public void setIssuerId(String issuerId) {
@@ -124,7 +157,7 @@ public class OidcClaimsValidator extends 
OAuthJoseJwtConsumer {
     @Override
     protected JwsSignatureVerifier getInitializedSignatureVerifier(JwtToken 
jwt) {
         JsonWebKey key = null;
-        if (supportSelfIssuedProvider && 
SELF_ISSUED_ISSUER.equals(jwt.getClaim("issuer"))) {
+        if (supportSelfIssuedProvider && 
SELF_ISSUED_ISSUER.equals(jwt.getClaims().getIssuer())) {
             String publicKeyJson = (String)jwt.getClaim("sub_jwk");
             if (publicKeyJson != null) {
                 JsonWebKey publicKey = JwkUtils.readJwkKey(publicKeyJson);
diff --git 
a/rt/rs/security/sso/oidc/src/test/java/org/apache/cxf/rs/security/oidc/rp/OidcClaimsValidatorTest.java
 
b/rt/rs/security/sso/oidc/src/test/java/org/apache/cxf/rs/security/oidc/rp/OidcClaimsValidatorTest.java
new file mode 100644
index 00000000000..52b1d8537e0
--- /dev/null
+++ 
b/rt/rs/security/sso/oidc/src/test/java/org/apache/cxf/rs/security/oidc/rp/OidcClaimsValidatorTest.java
@@ -0,0 +1,420 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.rs.security.oidc.rp;
+
+import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
+import org.apache.cxf.rs.security.jose.jwk.JwkUtils;
+import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
+import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier;
+import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
+import org.apache.cxf.rs.security.jose.jwt.JwtToken;
+import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+
+public class OidcClaimsValidatorTest {
+
+    private static final String SELF_ISSUED_ISSUER = "https://self-issued.me";;
+    private static final String CLIENT_ID = "client-id";
+
+    // EC P-256 public key for self-issued tests
+    private static final String EC_256_KEY = "{"
+        + "\"kty\": \"EC\","
+        + "\"x\": \"CEuRLUISufhcjrj-32N0Bvl3KPMiHH9iSw4ohN9jxrA\","
+        + "\"y\": \"EldWz_iXSK3l_S7n4w_t3baxos7o9yqX0IjzG959vHc\","
+        + "\"crv\": \"P-256\""
+        + "}";
+
+    // --- validateJwtClaims: self-issued path ---
+
+    @Test
+    public void testSelfIssuedValidTokenAccepted() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setSupportSelfIssuedProvider(true);
+
+        validator.validateJwtClaims(buildValidSelfIssuedClaims(), CLIENT_ID, 
true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testSelfIssuedMissingSubjectRejected() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setSupportSelfIssuedProvider(true);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer(SELF_ISSUED_ISSUER);
+        // sub intentionally absent
+        claims.setAudience(CLIENT_ID);
+        claims.setExpiryTime(now + 300);
+        claims.setIssuedAt(now - 10);
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testSelfIssuedMissingAudienceRejectedWhenRequired() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setSupportSelfIssuedProvider(true);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer(SELF_ISSUED_ISSUER);
+        claims.setSubject("key-thumbprint");
+        // aud intentionally absent
+        claims.setExpiryTime(now + 300);
+        claims.setIssuedAt(now - 10);
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testSelfIssuedWrongAudienceRejected() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setSupportSelfIssuedProvider(true);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer(SELF_ISSUED_ISSUER);
+        claims.setSubject("key-thumbprint");
+        claims.setAudience("wrong-client-id");
+        claims.setExpiryTime(now + 300);
+        claims.setIssuedAt(now - 10);
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testSelfIssuedExpiredTokenRejected() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setSupportSelfIssuedProvider(true);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer(SELF_ISSUED_ISSUER);
+        claims.setSubject("key-thumbprint");
+        claims.setAudience(CLIENT_ID);
+        claims.setExpiryTime(now - 600); // expired 10 minutes ago
+        claims.setIssuedAt(now - 700);
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testSelfIssuedMissingExpiryRejectedWhenRequired() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setSupportSelfIssuedProvider(true);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer(SELF_ISSUED_ISSUER);
+        claims.setSubject("key-thumbprint");
+        claims.setAudience(CLIENT_ID);
+        // exp intentionally absent
+        claims.setIssuedAt(now - 10);
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testSelfIssuedMissingIssuedAtRejectedWhenRequired() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setSupportSelfIssuedProvider(true);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer(SELF_ISSUED_ISSUER);
+        claims.setSubject("key-thumbprint");
+        claims.setAudience(CLIENT_ID);
+        claims.setExpiryTime(now + 300);
+        // iat intentionally absent
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testSelfIssuedMissingSubJwkRejected() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setSupportSelfIssuedProvider(true);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer(SELF_ISSUED_ISSUER);
+        claims.setSubject("key-thumbprint");
+        claims.setAudience(CLIENT_ID);
+        claims.setExpiryTime(now + 300);
+        claims.setIssuedAt(now - 10);
+        // sub_jwk intentionally absent
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testSelfIssuedSubJwkThumbprintMismatchRejected() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setSupportSelfIssuedProvider(true);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer(SELF_ISSUED_ISSUER);
+        claims.setSubject("wrong-thumbprint");
+        claims.setAudience(CLIENT_ID);
+        claims.setExpiryTime(now + 300);
+        claims.setIssuedAt(now - 10);
+        claims.setClaim("sub_jwk", EC_256_KEY);
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    // --- getInitializedSignatureVerifier: self-issued branch ---
+
+    @Test
+    public void testSignatureVerifierUsesSubJwkWhenIssIsSelfIssued() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setSupportSelfIssuedProvider(true);
+
+        String thumbprint = JwkUtils.getThumbprint(EC_256_KEY);
+
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer(SELF_ISSUED_ISSUER);
+        claims.setSubject(thumbprint);
+        claims.setClaim("sub_jwk", EC_256_KEY);
+
+        JwsHeaders headers = new JwsHeaders();
+        headers.setSignatureAlgorithm(SignatureAlgorithm.ES256);
+
+        JwsSignatureVerifier verifier = 
validator.getInitializedSignatureVerifier(new JwtToken(headers, claims));
+        assertNotNull(verifier);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testSignatureVerifierRejectsSubJwkWithWrongThumbprint() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setSupportSelfIssuedProvider(true);
+
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer(SELF_ISSUED_ISSUER);
+        claims.setSubject("wrong-thumbprint");
+        claims.setClaim("sub_jwk", EC_256_KEY);
+
+        JwsHeaders headers = new JwsHeaders();
+        headers.setSignatureAlgorithm(SignatureAlgorithm.ES256);
+
+        validator.getInitializedSignatureVerifier(new JwtToken(headers, 
claims));
+    }
+
+    // --- validateJwtClaims: normal issuer path ---
+
+    @Test
+    public void testNormalIssuerValidTokenAccepted() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setIssuerId("https://issuer.example.com";);
+
+        validator.validateJwtClaims(buildValidNormalIssuerClaims(), CLIENT_ID, 
true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testNormalIssuerMissingIssuerRejectedWhenRequired() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setIssuerId("https://issuer.example.com";);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        // issuer intentionally absent
+        claims.setSubject("subject-value");
+        claims.setAudience(CLIENT_ID);
+        claims.setExpiryTime(now + 300);
+        claims.setIssuedAt(now - 10);
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testNormalIssuerWrongIssuerRejected() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setIssuerId("https://issuer.example.com";);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer("https://wrong-issuer.example.com";);
+        claims.setSubject("subject-value");
+        claims.setAudience(CLIENT_ID);
+        claims.setExpiryTime(now + 300);
+        claims.setIssuedAt(now - 10);
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testNormalIssuerMissingSubjectRejected() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setIssuerId("https://issuer.example.com";);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer("https://issuer.example.com";);
+        // sub intentionally absent
+        claims.setAudience(CLIENT_ID);
+        claims.setExpiryTime(now + 300);
+        claims.setIssuedAt(now - 10);
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testNormalIssuerMissingAudienceRejectedWhenRequired() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setIssuerId("https://issuer.example.com";);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer("https://issuer.example.com";);
+        claims.setSubject("subject-value");
+        // aud intentionally absent
+        claims.setExpiryTime(now + 300);
+        claims.setIssuedAt(now - 10);
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testNormalIssuerWrongAudienceRejected() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setIssuerId("https://issuer.example.com";);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer("https://issuer.example.com";);
+        claims.setSubject("subject-value");
+        claims.setAudience("wrong-client-id");
+        claims.setExpiryTime(now + 300);
+        claims.setIssuedAt(now - 10);
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testNormalIssuerExpiredTokenRejected() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setIssuerId("https://issuer.example.com";);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer("https://issuer.example.com";);
+        claims.setSubject("subject-value");
+        claims.setAudience(CLIENT_ID);
+        claims.setExpiryTime(now - 600); // expired 10 minutes ago
+        claims.setIssuedAt(now - 700);
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testNormalIssuerMissingExpiryRejectedWhenRequired() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setIssuerId("https://issuer.example.com";);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer("https://issuer.example.com";);
+        claims.setSubject("subject-value");
+        claims.setAudience(CLIENT_ID);
+        // exp intentionally absent
+        claims.setIssuedAt(now - 10);
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testNormalIssuerMissingIssuedAtRejectedWhenRequired() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setIssuerId("https://issuer.example.com";);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer("https://issuer.example.com";);
+        claims.setSubject("subject-value");
+        claims.setAudience(CLIENT_ID);
+        claims.setExpiryTime(now + 300);
+        // iat intentionally absent
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test(expected = OAuthServiceException.class)
+    public void testNormalIssuerWrongAzpRejected() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setIssuerId("https://issuer.example.com";);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer("https://issuer.example.com";);
+        claims.setSubject("subject-value");
+        claims.setAudience(CLIENT_ID);
+        claims.setExpiryTime(now + 300);
+        claims.setIssuedAt(now - 10);
+        claims.setClaim("azp", "wrong-client-id"); // azp doesn't match 
clientId
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    @Test
+    public void testNormalIssuerValidTokenWithAzpAccepted() {
+        OidcClaimsValidator validator = new OidcClaimsValidator();
+        validator.setIssuerId("https://issuer.example.com";);
+
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer("https://issuer.example.com";);
+        claims.setSubject("subject-value");
+        claims.setAudience(CLIENT_ID);
+        claims.setExpiryTime(now + 300);
+        claims.setIssuedAt(now - 10);
+        claims.setClaim("azp", CLIENT_ID); // azp matches clientId
+
+        validator.validateJwtClaims(claims, CLIENT_ID, true);
+    }
+
+    // --- helper ---
+
+    private static JwtClaims buildValidNormalIssuerClaims() {
+        long now = System.currentTimeMillis() / 1000;
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer("https://issuer.example.com";);
+        claims.setSubject("subject-value");
+        claims.setAudience(CLIENT_ID);
+        claims.setExpiryTime(now + 300);
+        claims.setIssuedAt(now - 10);
+        return claims;
+    }
+
+    private static JwtClaims buildValidSelfIssuedClaims() {
+        long now = System.currentTimeMillis() / 1000;
+        String thumbprint = JwkUtils.getThumbprint(EC_256_KEY);
+        JwtClaims claims = new JwtClaims();
+        claims.setIssuer(SELF_ISSUED_ISSUER);
+        claims.setSubject(thumbprint);
+        claims.setAudience(CLIENT_ID);
+        claims.setExpiryTime(now + 300);
+        claims.setIssuedAt(now - 10);
+        claims.setClaim("sub_jwk", EC_256_KEY);
+        return claims;
+    }
+}

Reply via email to