11/* 
2-  * Copyright 2002-2021  the original author or authors. 
2+  * Copyright 2002-2025  the original author or authors. 
33 * 
44 * Licensed under the Apache License, Version 2.0 (the "License"); 
55 * you may not use this file except in compliance with the License. 
4646import  com .nimbusds .jwt .JWTClaimsSet ;
4747import  com .nimbusds .jwt .SignedJWT ;
4848
49+ import  org .springframework .core .convert .converter .Converter ;
4950import  org .springframework .security .oauth2 .jose .jws .SignatureAlgorithm ;
5051import  org .springframework .util .Assert ;
5152import  org .springframework .util .CollectionUtils ;
@@ -86,6 +87,19 @@ public final class NimbusJwtEncoder implements JwtEncoder {
8687
8788	private  final  JWKSource <SecurityContext > jwkSource ;
8889
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+ 
89103	/** 
90104	 * Constructs a {@code NimbusJwtEncoder} using the provided parameters. 
91105	 * @param jwkSource the {@code com.nimbusds.jose.jwk.source.JWKSource} 
@@ -94,6 +108,18 @@ public NimbusJwtEncoder(JWKSource<SecurityContext> jwkSource) {
94108		Assert .notNull (jwkSource , "jwkSource cannot be null" );
95109		this .jwkSource  = jwkSource ;
96110	}
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+ 	}
97123
98124	@ Override 
99125	public  Jwt  encode (JwtEncoderParameters  parameters ) throws  JwtEncodingException  {
@@ -123,18 +149,7 @@ private JWK selectJwk(JwsHeader headers) {
123149			throw  new  JwtEncodingException (String .format (ENCODING_ERROR_MESSAGE_TEMPLATE ,
124150					"Failed to select a JWK signing key -> "  + ex .getMessage ()), ex );
125151		}
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 );
138153	}
139154
140155	private  String  serialize (JwsHeader  headers , JwtClaimsSet  claims , JWK  jwk ) {
0 commit comments