1
1
/*
2
- * Copyright 2002-2021 the original author or authors.
2
+ * Copyright 2002-2025 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
46
46
import com .nimbusds .jwt .JWTClaimsSet ;
47
47
import com .nimbusds .jwt .SignedJWT ;
48
48
49
+ import org .springframework .core .convert .converter .Converter ;
49
50
import org .springframework .security .oauth2 .jose .jws .SignatureAlgorithm ;
50
51
import org .springframework .util .Assert ;
51
52
import org .springframework .util .CollectionUtils ;
@@ -86,6 +87,19 @@ public final class NimbusJwtEncoder implements JwtEncoder {
86
87
87
88
private final JWKSource <SecurityContext > jwkSource ;
88
89
90
+ private Converter <List <JWK >, JWK > jwkSelector = (jwks )->{
91
+ if (jwks .size () > 1 ) {
92
+ throw new JwtEncodingException (String .format (
93
+ "Failed to select a key since there are multiple for the signing algorithm [%s]; " +
94
+ "please specify a selector in NimbusJwsEncoder#setJwkSelector" ,jwks .get (0 ).getAlgorithm ()));
95
+ }
96
+ if (jwks .isEmpty ()) {
97
+ throw new JwtEncodingException (
98
+ String .format (ENCODING_ERROR_MESSAGE_TEMPLATE , "Failed to select a JWK signing key" ));
99
+ }
100
+ return jwks .get (0 );
101
+ };
102
+
89
103
/**
90
104
* Constructs a {@code NimbusJwtEncoder} using the provided parameters.
91
105
* @param jwkSource the {@code com.nimbusds.jose.jwk.source.JWKSource}
@@ -94,6 +108,18 @@ public NimbusJwtEncoder(JWKSource<SecurityContext> jwkSource) {
94
108
Assert .notNull (jwkSource , "jwkSource cannot be null" );
95
109
this .jwkSource = jwkSource ;
96
110
}
111
+ /**
112
+ * Use this strategy to reduce the list of matching JWKs down to a since one.
113
+ * <p> For example, you can call {@code setJwkSelector(List::getFirst)} in order
114
+ * to have this encoder select the first match.
115
+ *
116
+ * <p> By default, the class with throw an exception if there is more than one result.
117
+ * @since 6.5
118
+ */
119
+ public void setJwkSelector (Converter <List <JWK >, JWK > jwkSelector ) {
120
+ if (null !=jwkSelector )
121
+ this .jwkSelector = jwkSelector ;
122
+ }
97
123
98
124
@ Override
99
125
public Jwt encode (JwtEncoderParameters parameters ) throws JwtEncodingException {
@@ -123,18 +149,7 @@ private JWK selectJwk(JwsHeader headers) {
123
149
throw new JwtEncodingException (String .format (ENCODING_ERROR_MESSAGE_TEMPLATE ,
124
150
"Failed to select a JWK signing key -> " + ex .getMessage ()), ex );
125
151
}
126
-
127
- if (jwks .size () > 1 ) {
128
- throw new JwtEncodingException (String .format (ENCODING_ERROR_MESSAGE_TEMPLATE ,
129
- "Found multiple JWK signing keys for algorithm '" + headers .getAlgorithm ().getName () + "'" ));
130
- }
131
-
132
- if (jwks .isEmpty ()) {
133
- throw new JwtEncodingException (
134
- String .format (ENCODING_ERROR_MESSAGE_TEMPLATE , "Failed to select a JWK signing key" ));
135
- }
136
-
137
- return jwks .get (0 );
152
+ return this .jwkSelector .convert (jwks );
138
153
}
139
154
140
155
private String serialize (JwsHeader headers , JwtClaimsSet claims , JWK jwk ) {
0 commit comments