This is an automated email from the ASF dual-hosted git repository.
manikumar pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/kafka.git
The following commit(s) were added to refs/heads/trunk by this push:
new 160a6ab4cb KAFKA-13730: OAuth access token validation fails if it does
not contain the "sub" claim (#11886)
160a6ab4cb is described below
commit 160a6ab4cb0bd4c3e303329b5bb30a13c196b030
Author: Daniel Fonai <[email protected]>
AuthorDate: Wed Jul 27 13:14:54 2022 +0200
KAFKA-13730: OAuth access token validation fails if it does not contain the
"sub" claim (#11886)
Removes the requirement of presence of sub claim in JWT access tokens, when
clients authenticate via OAuth.
This does not interfere with OAuth specifications and is to ensure wider
compatibility with OAuth providers.
Unit test added.
Reviewers: Kirk True <[email protected]>, Viktor Somogyi-Vass
<[email protected]>, Manikumar Reddy <[email protected]>
---
.../secured/ValidatorAccessTokenValidator.java | 1 -
.../oauthbearer/secured/AccessTokenBuilder.java | 24 +++++++++++++++++++++-
.../secured/ValidatorAccessTokenValidatorTest.java | 19 +++++++++++++++++
3 files changed, 42 insertions(+), 2 deletions(-)
diff --git
a/clients/src/main/java/org/apache/kafka/common/security/oauthbearer/secured/ValidatorAccessTokenValidator.java
b/clients/src/main/java/org/apache/kafka/common/security/oauthbearer/secured/ValidatorAccessTokenValidator.java
index 7668438614..71d549153b 100644
---
a/clients/src/main/java/org/apache/kafka/common/security/oauthbearer/secured/ValidatorAccessTokenValidator.java
+++
b/clients/src/main/java/org/apache/kafka/common/security/oauthbearer/secured/ValidatorAccessTokenValidator.java
@@ -131,7 +131,6 @@ public class ValidatorAccessTokenValidator implements
AccessTokenValidator {
.setJwsAlgorithmConstraints(DISALLOW_NONE)
.setRequireExpirationTime()
.setRequireIssuedAt()
- .setRequireSubject()
.setVerificationKeyResolver(verificationKeyResolver)
.build();
this.scopeClaimName = scopeClaimName;
diff --git
a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/AccessTokenBuilder.java
b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/AccessTokenBuilder.java
index 24a40aa5b6..5387d40abf 100644
---
a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/AccessTokenBuilder.java
+++
b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/AccessTokenBuilder.java
@@ -22,6 +22,9 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
import org.apache.kafka.common.utils.MockTime;
import org.apache.kafka.common.utils.Time;
import org.jose4j.jwk.PublicJsonWebKey;
@@ -39,7 +42,7 @@ public class AccessTokenBuilder {
private String subject = "jdoe";
- private final String subjectClaimName = ReservedClaimNames.SUBJECT;
+ private String subjectClaimName = ReservedClaimNames.SUBJECT;
private Object scope = "engineering";
@@ -51,6 +54,8 @@ public class AccessTokenBuilder {
private PublicJsonWebKey jwk;
+ private final Map<String, String> customClaims = new HashMap<>();
+
public AccessTokenBuilder() {
this(new MockTime());
}
@@ -87,6 +92,11 @@ public class AccessTokenBuilder {
return subjectClaimName;
}
+ public AccessTokenBuilder subjectClaimName(String subjectClaimName) {
+ this.subjectClaimName = subjectClaimName;
+ return this;
+ }
+
public Object scope() {
return scope;
}
@@ -133,6 +143,14 @@ public class AccessTokenBuilder {
return this;
}
+ public AccessTokenBuilder addCustomClaim(String name, String value) {
+ String validatedName =
ClaimValidationUtils.validateClaimNameOverride("claim name", name);
+ String validatedValue =
ClaimValidationUtils.validateClaimNameOverride(validatedName, value);
+
+ customClaims.put(validatedName, validatedValue);
+ return this;
+ }
+
@SuppressWarnings("unchecked")
public String build() throws JoseException, IOException {
ObjectNode node = objectMapper.createObjectNode();
@@ -162,6 +180,10 @@ public class AccessTokenBuilder {
if (expirationSeconds != null)
node.put(ReservedClaimNames.EXPIRATION_TIME, expirationSeconds);
+ for (Map.Entry<String, String> claim : customClaims.entrySet()) {
+ node.put(claim.getKey(), claim.getValue());
+ }
+
String json = objectMapper.writeValueAsString(node);
JsonWebSignature jws = new JsonWebSignature();
diff --git
a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/ValidatorAccessTokenValidatorTest.java
b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/ValidatorAccessTokenValidatorTest.java
index a48198879e..f24bd590ac 100644
---
a/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/ValidatorAccessTokenValidatorTest.java
+++
b/clients/src/test/java/org/apache/kafka/common/security/oauthbearer/secured/ValidatorAccessTokenValidatorTest.java
@@ -59,6 +59,25 @@ public class ValidatorAccessTokenValidatorTest extends
AccessTokenValidatorTest
"fake is an unknown, unsupported or unavailable alg algorithm");
}
+ @Test
+ public void testMissingSubShouldBeValid() throws Exception {
+ String subClaimName = "client_id";
+ String subject = "otherSub";
+ PublicJsonWebKey jwk = createRsaJwk();
+ AccessTokenBuilder tokenBuilder = new AccessTokenBuilder()
+ .jwk(jwk)
+ .alg(AlgorithmIdentifiers.RSA_USING_SHA256)
+ .addCustomClaim(subClaimName, subject)
+ .subjectClaimName(subClaimName)
+ .subject(null);
+ AccessTokenValidator validator =
createAccessTokenValidator(tokenBuilder);
+
+ // Validation should succeed (e.g. signature verification) even if sub
claim is missing
+ OAuthBearerToken token = validator.validate(tokenBuilder.build());
+
+ assertEquals(subject, token.principalName());
+ }
+
private void testEncryptionAlgorithm(PublicJsonWebKey jwk, String alg)
throws Exception {
AccessTokenBuilder builder = new
AccessTokenBuilder().jwk(jwk).alg(alg);
AccessTokenValidator validator = createAccessTokenValidator(builder);