11package io .scalecube .security .tokens .jwt ;
22
3- import static io .scalecube .security .tokens .jwt .Utils .toRsaPublicKey ;
4-
53import com .fasterxml .jackson .annotation .JsonAutoDetect ;
64import com .fasterxml .jackson .annotation .JsonInclude ;
75import com .fasterxml .jackson .annotation .PropertyAccessor ;
119import java .io .BufferedInputStream ;
1210import java .io .IOException ;
1311import java .io .InputStream ;
12+ import java .math .BigInteger ;
1413import java .net .HttpURLConnection ;
1514import java .net .URL ;
1615import java .security .Key ;
16+ import java .security .KeyFactory ;
17+ import java .security .spec .KeySpec ;
18+ import java .security .spec .RSAPublicKeySpec ;
1719import java .time .Duration ;
20+ import java .util .Base64 ;
21+ import java .util .Base64 .Decoder ;
1822import java .util .Optional ;
1923import org .slf4j .Logger ;
2024import org .slf4j .LoggerFactory ;
2125import reactor .core .Exceptions ;
2226import reactor .core .publisher .Mono ;
23- import reactor .core .scheduler .Scheduler ;
2427import reactor .core .scheduler .Schedulers ;
2528
2629public final class JwksKeyProvider implements KeyProvider {
2730
2831 private static final Logger LOGGER = LoggerFactory .getLogger (JwksKeyProvider .class );
2932
30- private static final Duration CONNECT_TIMEOUT = Duration .ofSeconds (10 );
31- private static final Duration READ_TIMEOUT = Duration .ofSeconds (10 );
32-
3333 private static final ObjectMapper OBJECT_MAPPER = newObjectMapper ();
3434
35- private final Scheduler scheduler ;
36- private final String jwksUri ;
37- private final long connectTimeoutMillis ;
38- private final long readTimeoutMillis ;
35+ private String jwksUri ;
36+ private Duration connectTimeout = Duration .ofSeconds (10 );
37+ private Duration readTimeout = Duration .ofSeconds (10 );
38+
39+ public JwksKeyProvider () {}
40+
41+ private JwksKeyProvider (JwksKeyProvider other ) {
42+ this .jwksUri = other .jwksUri ;
43+ this .connectTimeout = other .connectTimeout ;
44+ this .readTimeout = other .readTimeout ;
45+ }
3946
4047 /**
41- * Constructor .
48+ * Setter for jwksUri .
4249 *
4350 * @param jwksUri jwksUri
51+ * @return new instance with applied setting
4452 */
45- public JwksKeyProvider (String jwksUri ) {
46- this (jwksUri , newScheduler (), CONNECT_TIMEOUT , READ_TIMEOUT );
53+ public JwksKeyProvider jwksUri (String jwksUri ) {
54+ final JwksKeyProvider c = copy ();
55+ c .jwksUri = jwksUri ;
56+ return c ;
4757 }
4858
4959 /**
50- * Constructor .
60+ * Setter for connectTimeout .
5161 *
52- * @param jwksUri jwksUri
53- * @param scheduler scheduler
5462 * @param connectTimeout connectTimeout
63+ * @return new instance with applied setting
64+ */
65+ public JwksKeyProvider connectTimeout (Duration connectTimeout ) {
66+ final JwksKeyProvider c = copy ();
67+ c .connectTimeout = connectTimeout ;
68+ return c ;
69+ }
70+
71+ /**
72+ * Setter for readTimeout.
73+ *
5574 * @param readTimeout readTimeout
75+ * @return new instance with applied setting
5676 */
57- public JwksKeyProvider (
58- String jwksUri , Scheduler scheduler , Duration connectTimeout , Duration readTimeout ) {
59- this .jwksUri = jwksUri ;
60- this .scheduler = scheduler ;
61- this .connectTimeoutMillis = connectTimeout .toMillis ();
62- this .readTimeoutMillis = readTimeout .toMillis ();
77+ public JwksKeyProvider readTimeout (Duration readTimeout ) {
78+ final JwksKeyProvider c = copy ();
79+ c .readTimeout = readTimeout ;
80+ return c ;
6381 }
6482
6583 @ Override
6684 public Mono <Key > findKey (String kid ) {
6785 return computeKey (kid )
6886 .switchIfEmpty (Mono .error (new KeyNotFoundException ("Key was not found, kid: " + kid )))
6987 .doOnSubscribe (s -> LOGGER .debug ("[findKey] Looking up key in jwks, kid: {}" , kid ))
70- .subscribeOn (scheduler );
88+ .subscribeOn (Schedulers .boundedElastic ())
89+ .publishOn (Schedulers .boundedElastic ());
7190 }
7291
7392 private Mono <Key > computeKey (String kid ) {
@@ -78,8 +97,8 @@ private Mono<Key> computeKey(String kid) {
7897
7998 private JwkInfoList computeKeyList () throws IOException {
8099 HttpURLConnection httpClient = (HttpURLConnection ) new URL (jwksUri ).openConnection ();
81- httpClient .setConnectTimeout ((int ) connectTimeoutMillis );
82- httpClient .setReadTimeout ((int ) readTimeoutMillis );
100+ httpClient .setConnectTimeout ((int ) connectTimeout . toMillis () );
101+ httpClient .setReadTimeout ((int ) readTimeout . toMillis () );
83102
84103 int responseCode = httpClient .getResponseCode ();
85104 if (responseCode != 200 ) {
@@ -106,6 +125,18 @@ private Optional<Key> findRsaKey(JwkInfoList list, String kid) {
106125 .map (info -> toRsaPublicKey (info .modulus (), info .exponent ()));
107126 }
108127
128+ static Key toRsaPublicKey (String n , String e ) {
129+ Decoder b64Decoder = Base64 .getUrlDecoder ();
130+ BigInteger modulus = new BigInteger (1 , b64Decoder .decode (n ));
131+ BigInteger exponent = new BigInteger (1 , b64Decoder .decode (e ));
132+ KeySpec keySpec = new RSAPublicKeySpec (modulus , exponent );
133+ try {
134+ return KeyFactory .getInstance ("RSA" ).generatePublic (keySpec );
135+ } catch (Exception ex ) {
136+ throw Exceptions .propagate (ex );
137+ }
138+ }
139+
109140 private static ObjectMapper newObjectMapper () {
110141 ObjectMapper mapper = new ObjectMapper ();
111142 mapper .configure (DeserializationFeature .FAIL_ON_UNKNOWN_PROPERTIES , false );
@@ -117,7 +148,7 @@ private static ObjectMapper newObjectMapper() {
117148 return mapper ;
118149 }
119150
120- private static Scheduler newScheduler () {
121- return Schedulers . newElastic ( "jwks-key-provider" , 60 , true );
151+ private JwksKeyProvider copy () {
152+ return new JwksKeyProvider ( this );
122153 }
123154}
0 commit comments