From 505882c944b4c7d8540e1b4814e8d9cbb4b68029 Mon Sep 17 00:00:00 2001 From: Thomas Vitale Date: Mon, 26 Aug 2019 20:41:03 +0200 Subject: [PATCH] Consolidate shared code between JwtDecoders and ReactiveJwtDecoders Extract duplicated code from JwtDecoders and ReactiveJwtDecoders into a package-private class. Fixes gh-7263 --- .../JwtDecoderProviderConfigurationUtils.java | 101 ++++++++++++++++++ .../security/oauth2/jwt/JwtDecoders.java | 68 ++---------- .../oauth2/jwt/ReactiveJwtDecoders.java | 80 ++++---------- 3 files changed, 125 insertions(+), 124 deletions(-) create mode 100644 oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java new file mode 100644 index 0000000000..ad1b59d54f --- /dev/null +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoderProviderConfigurationUtils.java @@ -0,0 +1,101 @@ +/* + * Copyright 2002-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.oauth2.jwt; + +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; +import java.util.Collections; +import java.util.Map; + +/** + * Allows resolving configuration from an + * OpenID Provider Configuration or + * Authorization Server Metadata Request based on provided + * issuer and method invoked. + * + * @author Thomas Vitale + * @since 5.2 + */ +class JwtDecoderProviderConfigurationUtils { + private static final String OIDC_METADATA_PATH = "/.well-known/openid-configuration"; + private static final String OAUTH_METADATA_PATH = "/.well-known/oauth-authorization-server"; + private static final RestTemplate rest = new RestTemplate(); + private static final ParameterizedTypeReference> typeReference = + new ParameterizedTypeReference>() {}; + + static Map getConfigurationForOidcIssuerLocation(String oidcIssuerLocation) { + return getConfiguration(oidcIssuerLocation, oidc(URI.create(oidcIssuerLocation))); + } + + static Map getConfigurationForIssuerLocation(String issuer) { + URI uri = URI.create(issuer); + return getConfiguration(issuer, oidc(uri), oidcRfc8414(uri), oauth(uri)); + } + + static void validateIssuer(Map configuration, String issuer) { + String metadataIssuer = "(unavailable)"; + if (configuration.containsKey("issuer")) { + metadataIssuer = configuration.get("issuer").toString(); + } + if (!issuer.equals(metadataIssuer)) { + throw new IllegalStateException("The Issuer \"" + metadataIssuer + "\" provided in the configuration did not " + + "match the requested issuer \"" + issuer + "\""); + } + } + + private static Map getConfiguration(String issuer, URI... uris) { + String errorMessage = "Unable to resolve the Configuration with the provided Issuer of " + + "\"" + issuer + "\""; + for (URI uri : uris) { + try { + RequestEntity request = RequestEntity.get(uri).build(); + ResponseEntity> response = rest.exchange(request, typeReference); + return response.getBody(); + } catch (RuntimeException e) { + if (!(e instanceof HttpClientErrorException && + ((HttpClientErrorException) e).getStatusCode().is4xxClientError())) { + throw new IllegalArgumentException(errorMessage, e); + } + // else try another endpoint + } + } + throw new IllegalArgumentException(errorMessage); + } + + private static URI oidc(URI issuer) { + return UriComponentsBuilder.fromUri(issuer) + .replacePath(issuer.getPath() + OIDC_METADATA_PATH) + .build(Collections.emptyMap()); + } + + private static URI oidcRfc8414(URI issuer) { + return UriComponentsBuilder.fromUri(issuer) + .replacePath(OIDC_METADATA_PATH + issuer.getPath()) + .build(Collections.emptyMap()); + } + + private static URI oauth(URI issuer) { + return UriComponentsBuilder.fromUri(issuer) + .replacePath(OAUTH_METADATA_PATH + issuer.getPath()) + .build(Collections.emptyMap()); + } +} diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java index 685f5fb29a..0d2f233198 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java @@ -15,18 +15,10 @@ */ package org.springframework.security.oauth2.jwt; -import java.net.URI; -import java.util.Collections; import java.util.Map; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.core.OAuth2TokenValidator; import org.springframework.util.Assert; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withJwkSetUri; @@ -41,11 +33,6 @@ import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withJwkSe * @since 5.1 */ public final class JwtDecoders { - private static final String OIDC_METADATA_PATH = "/.well-known/openid-configuration"; - private static final String OAUTH_METADATA_PATH = "/.well-known/oauth-authorization-server"; - private static final RestTemplate rest = new RestTemplate(); - private static final ParameterizedTypeReference> typeReference = - new ParameterizedTypeReference>() {}; /** * Creates a {@link JwtDecoder} using the provided @@ -60,7 +47,7 @@ public final class JwtDecoders { */ public static JwtDecoder fromOidcIssuerLocation(String oidcIssuerLocation) { Assert.hasText(oidcIssuerLocation, "oidcIssuerLocation cannot be empty"); - Map configuration = getConfiguration(oidcIssuerLocation, oidc(URI.create(oidcIssuerLocation))); + Map configuration = JwtDecoderProviderConfigurationUtils.getConfigurationForOidcIssuerLocation(oidcIssuerLocation); return withProviderConfiguration(configuration, oidcIssuerLocation); } @@ -93,70 +80,27 @@ public final class JwtDecoders { * Note that the second endpoint is the equivalent of calling * {@link JwtDecoders#fromOidcIssuerLocation(String)} * - * @param issuer + * @param issuer the Issuer * @return a {@link JwtDecoder} that was initialized by one of the described endpoints */ public static JwtDecoder fromIssuerLocation(String issuer) { Assert.hasText(issuer, "issuer cannot be empty"); - URI uri = URI.create(issuer); - Map configuration = getConfiguration(issuer, oidc(uri), oidcRfc8414(uri), oauth(uri)); + Map configuration = JwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(issuer); return withProviderConfiguration(configuration, issuer); } - private static URI oidc(URI issuer) { - return UriComponentsBuilder.fromUri(issuer) - .replacePath(issuer.getPath() + OIDC_METADATA_PATH).build(Collections.emptyMap()); - } - - private static URI oidcRfc8414(URI issuer) { - return UriComponentsBuilder.fromUri(issuer) - .replacePath(OIDC_METADATA_PATH + issuer.getPath()).build(Collections.emptyMap()); - } - - private static URI oauth(URI issuer) { - return UriComponentsBuilder.fromUri(issuer) - .replacePath(OAUTH_METADATA_PATH + issuer.getPath()).build(Collections.emptyMap()); - } - - private static Map getConfiguration(String issuer, URI... uris) { - String errorMessage = "Unable to resolve the Configuration with the provided Issuer of " + - "\"" + issuer + "\""; - for (URI uri : uris) { - try { - RequestEntity request = RequestEntity.get(uri).build(); - ResponseEntity> response = rest.exchange(request, typeReference); - return response.getBody(); - } catch (RuntimeException e) { - if (!(e instanceof HttpClientErrorException && - ((HttpClientErrorException) e).getStatusCode().is4xxClientError())) { - throw new IllegalArgumentException(errorMessage, e); - } - // else try another endpoint - } - } - throw new IllegalArgumentException(errorMessage); - } - /** * Validate provided issuer and build {@link JwtDecoder} from * OpenID Provider * Configuration Response and Authorization Server Metadata * Response. * - * @param configuration - * @param issuer + * @param configuration the configuration values + * @param issuer the Issuer * @return {@link JwtDecoder} */ private static JwtDecoder withProviderConfiguration(Map configuration, String issuer) { - String metadataIssuer = "(unavailable)"; - if (configuration.containsKey("issuer")) { - metadataIssuer = configuration.get("issuer").toString(); - } - if (!issuer.equals(metadataIssuer)) { - throw new IllegalStateException("The Issuer \"" + metadataIssuer + "\" provided in the configuration did not " - + "match the requested issuer \"" + issuer + "\""); - } - + JwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer); OAuth2TokenValidator jwtValidator = JwtValidators.createDefaultWithIssuer(issuer); NimbusJwtDecoder jwtDecoder = withJwkSetUri(configuration.get("jwks_uri").toString()).build(); jwtDecoder.setJwtValidator(jwtValidator); diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.java index df7e015841..d062b515cf 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoders.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,34 +15,23 @@ */ package org.springframework.security.oauth2.jwt; -import java.net.URI; -import java.util.Collections; import java.util.Map; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.RequestEntity; -import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.core.OAuth2TokenValidator; import org.springframework.util.Assert; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; import static org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder.withJwkSetUri; /** * Allows creating a {@link ReactiveJwtDecoder} from an - * OpenID Provider Configuration. + * OpenID Provider Configuration or + * Authorization Server Metadata Request based on provided + * issuer and method invoked. * * @author Josh Cummings * @since 5.1 */ public final class ReactiveJwtDecoders { - private static final String OIDC_METADATA_PATH = "/.well-known/openid-configuration"; - private static final String OAUTH_METADATA_PATH = "/.well-known/oauth-authorization-server"; - private static final RestTemplate rest = new RestTemplate(); - private static final ParameterizedTypeReference> typeReference = - new ParameterizedTypeReference>() {}; /** * Creates a {@link ReactiveJwtDecoder} using the provided @@ -57,7 +46,7 @@ public final class ReactiveJwtDecoders { */ public static ReactiveJwtDecoder fromOidcIssuerLocation(String oidcIssuerLocation) { Assert.hasText(oidcIssuerLocation, "oidcIssuerLocation cannot be empty"); - Map configuration = getConfiguration(oidcIssuerLocation, oidc(URI.create(oidcIssuerLocation))); + Map configuration = JwtDecoderProviderConfigurationUtils.getConfigurationForOidcIssuerLocation(oidcIssuerLocation); return withProviderConfiguration(configuration, oidcIssuerLocation); } @@ -90,60 +79,27 @@ public final class ReactiveJwtDecoders { * Note that the second endpoint is the equivalent of calling * {@link ReactiveJwtDecoders#fromOidcIssuerLocation(String)} * - * @param issuer + * @param issuer the Issuer * @return a {@link ReactiveJwtDecoder} that was initialized by one of the described endpoints */ public static ReactiveJwtDecoder fromIssuerLocation(String issuer) { Assert.hasText(issuer, "issuer cannot be empty"); - URI uri = URI.create(issuer); - Map configuration = getConfiguration(issuer, oidc(uri), oidcRfc8414(uri), oauth(uri)); + Map configuration = JwtDecoderProviderConfigurationUtils.getConfigurationForIssuerLocation(issuer); return withProviderConfiguration(configuration, issuer); } - private static URI oidc(URI issuer) { - return UriComponentsBuilder.fromUri(issuer) - .replacePath(issuer.getPath() + OIDC_METADATA_PATH).build(Collections.emptyMap()); - } - - private static URI oidcRfc8414(URI issuer) { - return UriComponentsBuilder.fromUri(issuer) - .replacePath(OIDC_METADATA_PATH + issuer.getPath()).build(Collections.emptyMap()); - } - - private static URI oauth(URI issuer) { - return UriComponentsBuilder.fromUri(issuer) - .replacePath(OAUTH_METADATA_PATH + issuer.getPath()).build(Collections.emptyMap()); - } - - private static Map getConfiguration(String issuer, URI... uris) { - String errorMessage = "Unable to resolve the Configuration with the provided Issuer of " + - "\"" + issuer + "\""; - for (URI uri : uris) { - try { - RequestEntity request = RequestEntity.get(uri).build(); - ResponseEntity> response = rest.exchange(request, typeReference); - return response.getBody(); - } catch (RuntimeException e) { - if (!(e instanceof HttpClientErrorException && - ((HttpClientErrorException) e).getStatusCode().is4xxClientError())) { - throw new IllegalArgumentException(errorMessage, e); - } - // else try another endpoint - } - } - throw new IllegalArgumentException(errorMessage); - } - + /** + * Build {@link ReactiveJwtDecoder} from + * OpenID Provider + * Configuration Response and Authorization Server Metadata + * Response. + * + * @param configuration the configuration values + * @param issuer the Issuer + * @return {@link ReactiveJwtDecoder} + */ private static ReactiveJwtDecoder withProviderConfiguration(Map configuration, String issuer) { - String metadataIssuer = "(unavailable)"; - if (configuration.containsKey("issuer")) { - metadataIssuer = configuration.get("issuer").toString(); - } - if (!issuer.equals(metadataIssuer)) { - throw new IllegalStateException("The Issuer \"" + metadataIssuer + "\" provided in the configuration did not " - + "match the requested issuer \"" + issuer + "\""); - } - + JwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer); OAuth2TokenValidator jwtValidator = JwtValidators.createDefaultWithIssuer(issuer); NimbusReactiveJwtDecoder jwtDecoder = withJwkSetUri(configuration.get("jwks_uri").toString()).build(); jwtDecoder.setJwtValidator(jwtValidator);