Skip to content

Commit 76eba9b

Browse files
committed
Add withIssuerLocation
Closes gh-10309
1 parent 9ee8202 commit 76eba9b

File tree

11 files changed

+667
-67
lines changed

11 files changed

+667
-67
lines changed

docs/modules/ROOT/pages/reactive/oauth2/resource-server/jwt.adoc

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ This is handy when you need deeper configuration, such as <<webflux-oauth2resour
327327
==== Exposing a `ReactiveJwtDecoder` `@Bean`
328328

329329
Alternately, exposing a `ReactiveJwtDecoder` `@Bean` has the same effect as `decoder()`:
330+
You can construct one with a `jwkSetUri` like so:
330331

331332
====
332333
.Java
@@ -343,7 +344,51 @@ public ReactiveJwtDecoder jwtDecoder() {
343344
----
344345
@Bean
345346
fun jwtDecoder(): ReactiveJwtDecoder {
346-
return ReactiveJwtDecoders.fromIssuerLocation(issuerUri)
347+
return NimbusReactiveJwtDecoder.withJwkSetUri(jwkSetUri).build()
348+
}
349+
----
350+
====
351+
352+
or you can use the issuer and have `NimbusReactiveJwtDecoder` look up the `jwkSetUri` when `build()` is invoked, like the following:
353+
354+
====
355+
.Java
356+
[source,java,role="primary"]
357+
----
358+
@Bean
359+
public ReactiveJwtDecoder jwtDecoder() {
360+
return NimbusReactiveJwtDecoder.withIssuerLocation(issuer).build();
361+
}
362+
----
363+
364+
.Kotlin
365+
[source,kotlin,role="secondary"]
366+
----
367+
@Bean
368+
fun jwtDecoder(): ReactiveJwtDecoder {
369+
return NimbusReactiveJwtDecoder.withIssuerLocation(issuer).build()
370+
}
371+
----
372+
====
373+
374+
Or, if the defaults work for you, you can also use `JwtDecoders`, which does the above in addition to configuring the decoder's validator:
375+
376+
====
377+
.Java
378+
[source,java,role="primary"]
379+
----
380+
@Bean
381+
public ReactiveJwtDecoder jwtDecoder() {
382+
return ReactiveJwtDecoders.fromIssuerLocation(issuer);
383+
}
384+
----
385+
386+
.Kotlin
387+
[source,kotlin,role="secondary"]
388+
----
389+
@Bean
390+
fun jwtDecoder(): ReactiveJwtDecoder {
391+
return ReactiveJwtDecoders.fromIssuerLocation(issuer)
347392
}
348393
----
349394
====
@@ -384,7 +429,7 @@ For greater power, though, we can use a builder that ships with `NimbusReactiveJ
384429
----
385430
@Bean
386431
ReactiveJwtDecoder jwtDecoder() {
387-
return NimbusReactiveJwtDecoder.withJwkSetUri(this.jwkSetUri)
432+
return NimbusReactiveJwtDecoder.withIssuerLocation(this.issuer)
388433
.jwsAlgorithm(RS512).build();
389434
}
390435
----
@@ -394,7 +439,7 @@ ReactiveJwtDecoder jwtDecoder() {
394439
----
395440
@Bean
396441
fun jwtDecoder(): ReactiveJwtDecoder {
397-
return NimbusReactiveJwtDecoder.withJwkSetUri(this.jwkSetUri)
442+
return NimbusReactiveJwtDecoder.withIssuerLocation(this.issuer)
398443
.jwsAlgorithm(RS512).build()
399444
}
400445
----
@@ -408,7 +453,7 @@ Calling `jwsAlgorithm` more than once configures `NimbusReactiveJwtDecoder` to t
408453
----
409454
@Bean
410455
ReactiveJwtDecoder jwtDecoder() {
411-
return NimbusReactiveJwtDecoder.withJwkSetUri(this.jwkSetUri)
456+
return NimbusReactiveJwtDecoder.withIssuerLocation(this.issuer)
412457
.jwsAlgorithm(RS512).jwsAlgorithm(ES512).build();
413458
}
414459
----
@@ -418,7 +463,7 @@ ReactiveJwtDecoder jwtDecoder() {
418463
----
419464
@Bean
420465
fun jwtDecoder(): ReactiveJwtDecoder {
421-
return NimbusReactiveJwtDecoder.withJwkSetUri(this.jwkSetUri)
466+
return NimbusReactiveJwtDecoder.withIssuerLocation(this.issuer)
422467
.jwsAlgorithm(RS512).jwsAlgorithm(ES512).build()
423468
}
424469
----
@@ -432,7 +477,7 @@ Alternately, you can call `jwsAlgorithms`:
432477
----
433478
@Bean
434479
ReactiveJwtDecoder jwtDecoder() {
435-
return NimbusReactiveJwtDecoder.withJwkSetUri(this.jwkSetUri)
480+
return NimbusReactiveJwtDecoder.withIssuerLocation(this.jwkSetUri)
436481
.jwsAlgorithms(algorithms -> {
437482
algorithms.add(RS512);
438483
algorithms.add(ES512);
@@ -445,7 +490,7 @@ ReactiveJwtDecoder jwtDecoder() {
445490
----
446491
@Bean
447492
fun jwtDecoder(): ReactiveJwtDecoder {
448-
return NimbusReactiveJwtDecoder.withJwkSetUri(this.jwkSetUri)
493+
return NimbusReactiveJwtDecoder.withIssuerLocation(this.jwkSetUri)
449494
.jwsAlgorithms {
450495
it.add(RS512)
451496
it.add(ES512)

docs/modules/ROOT/pages/servlet/oauth2/resource-server/jwt.adoc

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,8 @@ This is handy when deeper configuration, like <<oauth2resourceserver-jwt-validat
430430
[[oauth2resourceserver-jwt-decoder-bean]]
431431
=== Exposing a `JwtDecoder` `@Bean`
432432

433-
Or, exposing a <<oauth2resourceserver-jwt-architecture-jwtdecoder,`JwtDecoder`>> `@Bean` has the same effect as `decoder()`:
433+
Or, exposing a <<oauth2resourceserver-jwt-architecture-jwtdecoder,`JwtDecoder`>> `@Bean` has the same effect as `decoder()`.
434+
You can construct one with a `jwkSetUri` like so:
434435

435436
====
436437
.Java
@@ -452,6 +453,50 @@ fun jwtDecoder(): JwtDecoder {
452453
----
453454
====
454455

456+
or you can use the issuer and have `NimbusJwtDecoder` look up the `jwkSetUri` when `build()` is invoked, like the following:
457+
458+
====
459+
.Java
460+
[source,java,role="primary"]
461+
----
462+
@Bean
463+
public JwtDecoder jwtDecoder() {
464+
return NimbusJwtDecoder.withIssuerLocation(issuer).build();
465+
}
466+
----
467+
468+
.Kotlin
469+
[source,kotlin,role="secondary"]
470+
----
471+
@Bean
472+
fun jwtDecoder(): JwtDecoder {
473+
return NimbusJwtDecoder.withIssuerLocation(issuer).build()
474+
}
475+
----
476+
====
477+
478+
Or, if the defaults work for you, you can also use `JwtDecoders`, which does the above in addition to configuring the decoder's validator:
479+
480+
====
481+
.Java
482+
[source,java,role="primary"]
483+
----
484+
@Bean
485+
public JwtDecoders jwtDecoder() {
486+
return JwtDecoders.fromIssuerLocation(issuer);
487+
}
488+
----
489+
490+
.Kotlin
491+
[source,kotlin,role="secondary"]
492+
----
493+
@Bean
494+
fun jwtDecoder(): JwtDecoders {
495+
return JwtDecoders.fromIssuerLocation(issuer)
496+
}
497+
----
498+
====
499+
455500
[[oauth2resourceserver-jwt-decoder-algorithm]]
456501
== Configuring Trusted Algorithms
457502

@@ -486,7 +531,7 @@ For greater power, though, we can use a builder that ships with `NimbusJwtDecode
486531
----
487532
@Bean
488533
JwtDecoder jwtDecoder() {
489-
return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri)
534+
return NimbusJwtDecoder.withIssuerLocation(this.issuer)
490535
.jwsAlgorithm(RS512).build();
491536
}
492537
----
@@ -496,7 +541,7 @@ JwtDecoder jwtDecoder() {
496541
----
497542
@Bean
498543
fun jwtDecoder(): JwtDecoder {
499-
return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri)
544+
return NimbusJwtDecoder.withIssuerLocation(this.issuer)
500545
.jwsAlgorithm(RS512).build()
501546
}
502547
----
@@ -510,7 +555,7 @@ Calling `jwsAlgorithm` more than once will configure `NimbusJwtDecoder` to trust
510555
----
511556
@Bean
512557
JwtDecoder jwtDecoder() {
513-
return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri)
558+
return NimbusJwtDecoder.withIssuerLocation(this.issuer)
514559
.jwsAlgorithm(RS512).jwsAlgorithm(ES512).build();
515560
}
516561
----
@@ -520,7 +565,7 @@ JwtDecoder jwtDecoder() {
520565
----
521566
@Bean
522567
fun jwtDecoder(): JwtDecoder {
523-
return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri)
568+
return NimbusJwtDecoder.withIssuerLocation(this.issuer)
524569
.jwsAlgorithm(RS512).jwsAlgorithm(ES512).build()
525570
}
526571
----
@@ -534,7 +579,7 @@ Or, you can call `jwsAlgorithms`:
534579
----
535580
@Bean
536581
JwtDecoder jwtDecoder() {
537-
return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri)
582+
return NimbusJwtDecoder.withIssuerLocation(this.issuer)
538583
.jwsAlgorithms(algorithms -> {
539584
algorithms.add(RS512);
540585
algorithms.add(ES512);
@@ -547,7 +592,7 @@ JwtDecoder jwtDecoder() {
547592
----
548593
@Bean
549594
fun jwtDecoder(): JwtDecoder {
550-
return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri)
595+
return NimbusJwtDecoder.withIssuerLocation(this.issuer)
551596
.jwsAlgorithms {
552597
it.add(RS512)
553598
it.add(ES512)
@@ -1207,7 +1252,7 @@ An individual claim's conversion strategy can be configured using `MappedJwtClai
12071252
----
12081253
@Bean
12091254
JwtDecoder jwtDecoder() {
1210-
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
1255+
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();
12111256
12121257
MappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter
12131258
.withDefaults(Collections.singletonMap("sub", this::lookupUserIdBySub));
@@ -1222,7 +1267,7 @@ JwtDecoder jwtDecoder() {
12221267
----
12231268
@Bean
12241269
fun jwtDecoder(): JwtDecoder {
1225-
val jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build()
1270+
val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build()
12261271
12271272
val converter = MappedJwtClaimSetConverter
12281273
.withDefaults(mapOf("sub" to this::lookupUserIdBySub))
@@ -1319,7 +1364,7 @@ And then, the instance can be supplied like normal:
13191364
----
13201365
@Bean
13211366
JwtDecoder jwtDecoder() {
1322-
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
1367+
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();
13231368
jwtDecoder.setClaimSetConverter(new UsernameSubClaimAdapter());
13241369
return jwtDecoder;
13251370
}
@@ -1330,7 +1375,7 @@ JwtDecoder jwtDecoder() {
13301375
----
13311376
@Bean
13321377
fun jwtDecoder(): JwtDecoder {
1333-
val jwtDecoder: NimbusJwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build()
1378+
val jwtDecoder: NimbusJwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build()
13341379
jwtDecoder.setClaimSetConverter(UsernameSubClaimAdapter())
13351380
return jwtDecoder
13361381
}
@@ -1358,7 +1403,7 @@ public JwtDecoder jwtDecoder(RestTemplateBuilder builder) {
13581403
.setReadTimeout(Duration.ofSeconds(60))
13591404
.build();
13601405
1361-
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).restOperations(rest).build();
1406+
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).restOperations(rest).build();
13621407
return jwtDecoder;
13631408
}
13641409
----
@@ -1372,7 +1417,7 @@ fun jwtDecoder(builder: RestTemplateBuilder): JwtDecoder {
13721417
.setConnectTimeout(Duration.ofSeconds(60))
13731418
.setReadTimeout(Duration.ofSeconds(60))
13741419
.build()
1375-
return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).restOperations(rest).build()
1420+
return NimbusJwtDecoder.withIssuerLocation(issuer).restOperations(rest).build()
13761421
}
13771422
----
13781423
====
@@ -1388,7 +1433,7 @@ To adjust the way in which Resource Server caches the JWK set, `NimbusJwtDecoder
13881433
----
13891434
@Bean
13901435
public JwtDecoder jwtDecoder(CacheManager cacheManager) {
1391-
return NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
1436+
return NimbusJwtDecoder.withIssuerLocation(issuer)
13921437
.cache(cacheManager.getCache("jwks"))
13931438
.build();
13941439
}
@@ -1399,7 +1444,7 @@ public JwtDecoder jwtDecoder(CacheManager cacheManager) {
13991444
----
14001445
@Bean
14011446
fun jwtDecoder(cacheManager: CacheManager): JwtDecoder {
1402-
return NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
1447+
return NimbusJwtDecoder.withIssuerLocation(issuer)
14031448
.cache(cacheManager.getCache("jwks"))
14041449
.build()
14051450
}

oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2023 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.
@@ -42,6 +42,7 @@
4242
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
4343
import org.springframework.util.Assert;
4444
import org.springframework.web.client.HttpClientErrorException;
45+
import org.springframework.web.client.RestOperations;
4546
import org.springframework.web.client.RestTemplate;
4647
import org.springframework.web.util.UriComponentsBuilder;
4748

@@ -71,12 +72,16 @@ private JwtDecoderProviderConfigurationUtils() {
7172
}
7273

7374
static Map<String, Object> getConfigurationForOidcIssuerLocation(String oidcIssuerLocation) {
74-
return getConfiguration(oidcIssuerLocation, oidc(URI.create(oidcIssuerLocation)));
75+
return getConfiguration(oidcIssuerLocation, rest, oidc(URI.create(oidcIssuerLocation)));
7576
}
7677

77-
static Map<String, Object> getConfigurationForIssuerLocation(String issuer) {
78+
static Map<String, Object> getConfigurationForIssuerLocation(String issuer, RestOperations rest) {
7879
URI uri = URI.create(issuer);
79-
return getConfiguration(issuer, oidc(uri), oidcRfc8414(uri), oauth(uri));
80+
return getConfiguration(issuer, rest, oidc(uri), oidcRfc8414(uri), oauth(uri));
81+
}
82+
83+
static Map<String, Object> getConfigurationForIssuerLocation(String issuer) {
84+
return getConfigurationForIssuerLocation(issuer, rest);
8085
}
8186

8287
static void validateIssuer(Map<String, Object> configuration, String issuer) {
@@ -142,7 +147,7 @@ private static String getMetadataIssuer(Map<String, Object> configuration) {
142147
return "(unavailable)";
143148
}
144149

145-
private static Map<String, Object> getConfiguration(String issuer, URI... uris) {
150+
private static Map<String, Object> getConfiguration(String issuer, RestOperations rest, URI... uris) {
146151
String errorMessage = "Unable to resolve the Configuration with the provided Issuer of " + "\"" + issuer + "\"";
147152
for (URI uri : uris) {
148153
try {

oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2023 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.
@@ -89,9 +89,10 @@ public static <T extends JwtDecoder> T fromOidcIssuerLocation(String oidcIssuerL
8989
@SuppressWarnings("unchecked")
9090
public static <T extends JwtDecoder> T fromIssuerLocation(String issuer) {
9191
Assert.hasText(issuer, "issuer cannot be empty");
92-
Map<String, Object> configuration = JwtDecoderProviderConfigurationUtils
93-
.getConfigurationForIssuerLocation(issuer);
94-
return (T) withProviderConfiguration(configuration, issuer);
92+
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();
93+
OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);
94+
jwtDecoder.setJwtValidator(jwtValidator);
95+
return (T) jwtDecoder;
9596
}
9697

9798
/**

0 commit comments

Comments
 (0)