|
16 | 16 |
|
17 | 17 | @Slf4j
|
18 | 18 | class MultipleIssuersJwtDecoder implements JwtDecoder {
|
| 19 | + |
19 | 20 | private final Map<String, NimbusJwtDecoder> decoderMap;
|
20 | 21 |
|
21 | 22 | MultipleIssuersJwtDecoder(List<ResourceServerProperties> properties) {
|
22 |
| - this.decoderMap = properties.stream().collect(Collectors.toMap( |
23 |
| - ResourceServerProperties::getIssuerUri, |
24 |
| - props -> { |
25 |
| - NimbusJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(props.getIssuerUri()); |
26 |
| - jwtDecoder.setJwtValidator(oAuth2TokenValidator(props)); |
27 |
| - return jwtDecoder; |
28 |
| - } |
29 |
| - )); |
| 23 | + decoderMap = properties |
| 24 | + .stream() |
| 25 | + .collect(Collectors.toMap( |
| 26 | + ResourceServerProperties::getIssuerUri, |
| 27 | + MultipleIssuersJwtDecoder::getValidatingDecoder |
| 28 | + )); |
| 29 | + } |
| 30 | + |
| 31 | + private static NimbusJwtDecoder getValidatingDecoder(ResourceServerProperties properties) { |
| 32 | + NimbusJwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(properties.getIssuerUri()); |
| 33 | + jwtDecoder.setJwtValidator(oAuth2TokenValidator(properties)); |
| 34 | + return jwtDecoder; |
30 | 35 | }
|
31 | 36 |
|
32 | 37 | @Override
|
33 | 38 | public Jwt decode(String token) throws JwtException {
|
34 | 39 | try {
|
| 40 | + |
35 | 41 | var issuer = JWTParser
|
36 | 42 | .parse(token)
|
37 | 43 | .getJWTClaimsSet()
|
38 | 44 | .getIssuer();
|
39 |
| - return decoderMap |
40 |
| - .get(issuer) |
41 |
| - .decode(token); |
| 45 | + if (issuer == null || !decoderMap.containsKey(issuer)) { |
| 46 | + throw new JwtException("JWT decoder for issuer %s not found".formatted(issuer)); |
| 47 | + } |
| 48 | + var decoder = decoderMap.get(issuer); |
| 49 | + log.info("Decoding token with issuer {} using decoder {}", issuer, decoder.getClass().getSimpleName()); |
| 50 | + return decoder.decode(token); |
| 51 | + |
42 | 52 | } catch (ParseException e) {
|
43 |
| - log.error("Feil ved parsing av token", e); |
| 53 | + log.error("Error in offset {} when parsing token", e.getErrorOffset(), e); |
44 | 54 | throw new JwtException("Feil ved parsing av token", e);
|
45 | 55 | } catch (JwtValidationException e) {
|
46 |
| - log.error("Feil ved validering av token", e); |
| 56 | + log.error("Error(s) validating token: {}", e.getErrors(), e); |
47 | 57 | throw e;
|
48 | 58 | } catch (Exception e) {
|
49 |
| - log.error("Ukjent feil", e); |
50 |
| - throw e; |
| 59 | + log.error("Unexpected failure", e); |
| 60 | + throw new JwtException("Unexpected failure", e); |
51 | 61 | }
|
52 | 62 | }
|
53 | 63 |
|
54 |
| - private OAuth2TokenValidator<Jwt> oAuth2TokenValidator(ResourceServerProperties properties) { |
55 |
| - OAuth2TokenValidator<Jwt> issuerValidator = JwtValidators.createDefaultWithIssuer(properties.getIssuerUri()); |
56 |
| - OAuth2TokenValidator<Jwt> audienceValidator = token -> |
57 |
| - token.getAudience().stream().anyMatch(audience -> properties.getAcceptedAudience().contains(audience)) ? |
58 |
| - OAuth2TokenValidatorResult.success() : |
59 |
| - OAuth2TokenValidatorResult.failure(createError( |
60 |
| - String.format("Fant ikke påkrevd audience %s i tokenet.", properties.getAcceptedAudience()) |
61 |
| - )); |
62 |
| - return new DelegatingOAuth2TokenValidator<>(issuerValidator, audienceValidator); |
| 64 | + private static OAuth2TokenValidator<Jwt> oAuth2TokenValidator(ResourceServerProperties properties) { |
| 65 | + return new DelegatingOAuth2TokenValidator<>( |
| 66 | + issuerValidator(properties.getIssuerUri()), |
| 67 | + audienceValidator(properties.getAcceptedAudience()) |
| 68 | + ); |
| 69 | + } |
| 70 | + |
| 71 | + private static OAuth2TokenValidator<Jwt> issuerValidator(String issuerUri) { |
| 72 | + return JwtValidators.createDefaultWithIssuer(issuerUri); // Note that this creates and adds a default audience validator. |
63 | 73 | }
|
64 | 74 |
|
65 |
| - private OAuth2Error createError(String msg) { |
66 |
| - return new OAuth2Error("invalid_token", msg, null); |
| 75 | + private static OAuth2TokenValidator<Jwt> audienceValidator(List<String> acceptedAudience) { |
| 76 | + return token -> { |
| 77 | + var audience = token.getAudience(); |
| 78 | + var audienceIsAccepted = audience |
| 79 | + .stream() |
| 80 | + .anyMatch(acceptedAudience::contains); |
| 81 | + if (audienceIsAccepted) { |
| 82 | + log.info("Token audience {} is accepted by {}", audience, acceptedAudience); |
| 83 | + return OAuth2TokenValidatorResult.success(); |
| 84 | + } |
| 85 | + var message = "Token audience %s is not accepted by %s".formatted(audience, acceptedAudience); |
| 86 | + log.warn(message); |
| 87 | + return OAuth2TokenValidatorResult.failure(new OAuth2Error("invalid_token", message, null)); |
| 88 | + }; |
67 | 89 | }
|
68 | 90 |
|
69 | 91 | }
|
0 commit comments