Skip to content

Commit 71a9c85

Browse files
committed
Allow logout+jwt JWT type
Closes spring-projectsgh-15003
1 parent 5bf1ba5 commit 71a9c85

File tree

2 files changed

+37
-7
lines changed

2 files changed

+37
-7
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutAuthenticationProvider.java

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616

1717
package org.springframework.security.config.annotation.web.configurers.oauth2.client;
1818

19+
import com.nimbusds.jose.JOSEObjectType;
20+
import com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier;
21+
import com.nimbusds.jose.proc.JOSEObjectTypeVerifier;
22+
import com.nimbusds.jose.proc.SecurityContext;
23+
1924
import org.springframework.security.authentication.AuthenticationProvider;
2025
import org.springframework.security.authentication.AuthenticationServiceException;
2126
import org.springframework.security.core.Authentication;
@@ -26,11 +31,14 @@
2631
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
2732
import org.springframework.security.oauth2.core.OAuth2Error;
2833
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
34+
import org.springframework.security.oauth2.core.converter.ClaimTypeConverter;
2935
import org.springframework.security.oauth2.jwt.BadJwtException;
3036
import org.springframework.security.oauth2.jwt.Jwt;
3137
import org.springframework.security.oauth2.jwt.JwtDecoder;
3238
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
39+
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
3340
import org.springframework.util.Assert;
41+
import org.springframework.util.StringUtils;
3442

3543
/**
3644
* An {@link AuthenticationProvider} that authenticates an OIDC Logout Token; namely
@@ -56,9 +64,27 @@ final class OidcBackChannelLogoutAuthenticationProvider implements Authenticatio
5664
* Construct an {@link OidcBackChannelLogoutAuthenticationProvider}
5765
*/
5866
OidcBackChannelLogoutAuthenticationProvider() {
59-
OidcIdTokenDecoderFactory logoutTokenDecoderFactory = new OidcIdTokenDecoderFactory();
60-
logoutTokenDecoderFactory.setJwtValidatorFactory(new DefaultOidcLogoutTokenValidatorFactory());
61-
this.logoutTokenDecoderFactory = logoutTokenDecoderFactory;
67+
DefaultOidcLogoutTokenValidatorFactory jwtValidator = new DefaultOidcLogoutTokenValidatorFactory();
68+
this.logoutTokenDecoderFactory = (clientRegistration) -> {
69+
String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
70+
if (!StringUtils.hasText(jwkSetUri)) {
71+
OAuth2Error oauth2Error = new OAuth2Error("missing_signature_verifier",
72+
"Failed to find a Signature Verifier for Client Registration: '"
73+
+ clientRegistration.getRegistrationId()
74+
+ "'. Check to ensure you have configured the JwkSet URI.",
75+
null);
76+
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
77+
}
78+
JOSEObjectTypeVerifier<SecurityContext> typeVerifier = new DefaultJOSEObjectTypeVerifier<>(null,
79+
JOSEObjectType.JWT, new JOSEObjectType("logout+jwt"));
80+
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
81+
.jwtProcessorCustomizer((processor) -> processor.setJWSTypeVerifier(typeVerifier))
82+
.build();
83+
decoder.setJwtValidator(jwtValidator.apply(clientRegistration));
84+
decoder.setClaimSetConverter(
85+
new ClaimTypeConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverters()));
86+
return decoder;
87+
};
6288
}
6389

6490
/**

config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutConfigurerTests.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@
7373
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
7474
import org.springframework.security.oauth2.core.oidc.TestOidcIdTokens;
7575
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
76+
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
77+
import org.springframework.security.oauth2.jwt.JwsHeader;
7678
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
7779
import org.springframework.security.oauth2.jwt.JwtEncoder;
7880
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
@@ -513,8 +515,9 @@ String jwks() {
513515
String logoutToken(@AuthenticationPrincipal OidcUser user) {
514516
OidcLogoutToken token = TestOidcLogoutTokens.withUser(user)
515517
.audience(List.of(this.registration.getClientId())).build();
516-
JwtEncoderParameters parameters = JwtEncoderParameters
517-
.from(JwtClaimsSet.builder().claims((claims) -> claims.putAll(token.getClaims())).build());
518+
JwsHeader header = JwsHeader.with(SignatureAlgorithm.RS256).type("logout+jwt").build();
519+
JwtClaimsSet claims = JwtClaimsSet.builder().claims((c) -> c.putAll(token.getClaims())).build();
520+
JwtEncoderParameters parameters = JwtEncoderParameters.from(header, claims);
518521
return this.encoder.encode(parameters).getTokenValue();
519522
}
520523

@@ -523,8 +526,9 @@ String logoutTokenAll(@AuthenticationPrincipal OidcUser user) {
523526
OidcLogoutToken token = TestOidcLogoutTokens.withUser(user)
524527
.audience(List.of(this.registration.getClientId()))
525528
.claims((claims) -> claims.remove(LogoutTokenClaimNames.SID)).build();
526-
JwtEncoderParameters parameters = JwtEncoderParameters
527-
.from(JwtClaimsSet.builder().claims((claims) -> claims.putAll(token.getClaims())).build());
529+
JwsHeader header = JwsHeader.with(SignatureAlgorithm.RS256).type("JWT").build();
530+
JwtClaimsSet claims = JwtClaimsSet.builder().claims((c) -> c.putAll(token.getClaims())).build();
531+
JwtEncoderParameters parameters = JwtEncoderParameters.from(header, claims);
528532
return this.encoder.encode(parameters).getTokenValue();
529533
}
530534
}

0 commit comments

Comments
 (0)