Skip to content

Commit 07f9621

Browse files
committed
Fix DPoP jkt claim to be JWK SHA-256 thumbprint
Closes gh-2007
1 parent 40d503a commit 07f9621

File tree

4 files changed

+10
-15
lines changed

4 files changed

+10
-15
lines changed

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/DefaultOAuth2TokenCustomizers.java

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,13 @@
1616
package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers;
1717

1818
import java.security.MessageDigest;
19-
import java.security.PublicKey;
2019
import java.security.cert.X509Certificate;
2120
import java.util.Base64;
2221
import java.util.Collections;
2322
import java.util.HashMap;
2423
import java.util.LinkedHashMap;
2524
import java.util.Map;
2625

27-
import com.nimbusds.jose.jwk.AsymmetricJWK;
2826
import com.nimbusds.jose.jwk.JWK;
2927

3028
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
@@ -91,25 +89,22 @@ private static void customize(OAuth2TokenContext tokenContext, Map<String, Objec
9189
// Add 'cnf' claim for OAuth 2.0 Demonstrating Proof of Possession (DPoP)
9290
Jwt dPoPProofJwt = tokenContext.get(OAuth2TokenContext.DPOP_PROOF_KEY);
9391
if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenContext.getTokenType()) && dPoPProofJwt != null) {
94-
PublicKey publicKey = null;
92+
JWK jwk = null;
9593
@SuppressWarnings("unchecked")
9694
Map<String, Object> jwkJson = (Map<String, Object>) dPoPProofJwt.getHeaders().get("jwk");
9795
try {
98-
JWK jwk = JWK.parse(jwkJson);
99-
if (jwk instanceof AsymmetricJWK asymmetricJWK) {
100-
publicKey = asymmetricJWK.toPublicKey();
101-
}
96+
jwk = JWK.parse(jwkJson);
10297
}
10398
catch (Exception ignored) {
10499
}
105-
if (publicKey == null) {
100+
if (jwk == null) {
106101
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_DPOP_PROOF,
107102
"jwk header is missing or invalid.", null);
108103
throw new OAuth2AuthenticationException(error);
109104
}
110105

111106
try {
112-
String sha256Thumbprint = computeSHA256Thumbprint(publicKey);
107+
String sha256Thumbprint = jwk.computeThumbprint().toString();
113108
if (cnfClaims == null) {
114109
cnfClaims = new HashMap<>();
115110
}
@@ -149,10 +144,4 @@ private static String computeSHA256Thumbprint(X509Certificate x509Certificate) t
149144
return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
150145
}
151146

152-
private static String computeSHA256Thumbprint(PublicKey publicKey) throws Exception {
153-
MessageDigest md = MessageDigest.getInstance("SHA-256");
154-
byte[] digest = md.digest(publicKey.getEncoded());
155-
return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
156-
}
157-
158147
}

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationCodeGrantTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,8 @@ public void requestWhenTokenRequestWithDPoPProofThenReturnDPoPBoundAccessToken()
10111011
@SuppressWarnings("unchecked")
10121012
Map<String, Object> cnfClaims = (Map<String, Object>) authorization.getAccessToken().getClaims().get("cnf");
10131013
assertThat(cnfClaims).containsKey("jkt");
1014+
String jwkThumbprintClaim = (String) cnfClaims.get("jkt");
1015+
assertThat(jwkThumbprintClaim).isEqualTo(TestJwks.DEFAULT_EC_JWK.toPublicJWK().computeThumbprint().toString());
10141016
}
10151017

10161018
@Test

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceCodeGrantTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,8 @@ public void requestWhenAccessTokenRequestWithDPoPProofThenReturnDPoPBoundAccessT
605605
@SuppressWarnings("unchecked")
606606
Map<String, Object> cnfClaims = (Map<String, Object>) authorization.getAccessToken().getClaims().get("cnf");
607607
assertThat(cnfClaims).containsKey("jkt");
608+
String jwkThumbprintClaim = (String) cnfClaims.get("jkt");
609+
assertThat(jwkThumbprintClaim).isEqualTo(TestJwks.DEFAULT_EC_JWK.toPublicJWK().computeThumbprint().toString());
608610
}
609611

610612
private static String generateDPoPProof(String tokenEndpointUri) {

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2RefreshTokenGrantTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ public void requestWhenRefreshTokenRequestWithPublicClientAndDPoPProofThenReturn
319319
@SuppressWarnings("unchecked")
320320
Map<String, Object> cnfClaims = (Map<String, Object>) authorization.getAccessToken().getClaims().get("cnf");
321321
assertThat(cnfClaims).containsKey("jkt");
322+
String jwkThumbprintClaim = (String) cnfClaims.get("jkt");
323+
assertThat(jwkThumbprintClaim).isEqualTo(TestJwks.DEFAULT_EC_JWK.toPublicJWK().computeThumbprint().toString());
322324
}
323325

324326
@Test

0 commit comments

Comments
 (0)