3737import org .springframework .security .oauth2 .core .oidc .IdTokenClaimNames ;
3838import org .springframework .util .Assert ;
3939import org .springframework .web .client .HttpClientErrorException ;
40+ import org .springframework .web .client .RestOperations ;
4041import org .springframework .web .client .RestTemplate ;
4142import org .springframework .web .util .UriComponents ;
4243import org .springframework .web .util .UriComponentsBuilder ;
@@ -148,8 +149,50 @@ public static ClientRegistration.Builder fromOidcConfiguration(Map<String, Objec
148149 * Provider Configuration.
149150 */
150151 public static ClientRegistration .Builder fromOidcIssuerLocation (String issuer ) {
152+ return fromOidcIssuerLocation (issuer , rest );
153+ }
154+
155+ /**
156+ * Creates a {@link ClientRegistration.Builder} using the provided <a href=
157+ * "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
158+ * by making an <a href=
159+ * "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest">OpenID
160+ * Provider Configuration Request</a> and using the values in the <a href=
161+ * "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse">OpenID
162+ * Provider Configuration Response</a> to initialize the
163+ * {@link ClientRegistration.Builder}.
164+ *
165+ * <p>
166+ * For example, if the issuer provided is "https://example.com", then an "OpenID
167+ * Provider Configuration Request" will be made to
168+ * "https://example.com/.well-known/openid-configuration". The result is expected to
169+ * be an "OpenID Provider Configuration Response".
170+ * </p>
171+ *
172+ * This method uses the provided {@link RestOperations} to query oidc issuer
173+ * configuration.
174+ *
175+ * <p>
176+ * Example usage:
177+ * </p>
178+ * <pre>
179+ * RestTemplate rest = new RestTemplate();
180+ * ClientRegistration registration = ClientRegistrations.fromOidcIssuerLocation("https://example.com", rest)
181+ * .clientId("client-id")
182+ * .clientSecret("client-secret")
183+ * .build();
184+ * </pre>
185+ * @param issuer the <a href=
186+ * "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
187+ * @param restOperations the {@link RestOperations} to use
188+ * @return a {@link ClientRegistration.Builder} that was initialized by the OpenID
189+ * Provider Configuration.
190+ * @since 7.2
191+ */
192+ public static ClientRegistration .Builder fromOidcIssuerLocation (String issuer , RestOperations restOperations ) {
151193 Assert .hasText (issuer , "issuer cannot be empty" );
152- return getBuilder (issuer , oidc (issuer ));
194+ Assert .notNull (restOperations , "restOperations cannot be null" );
195+ return getBuilder (issuer , oidc (issuer , restOperations ));
153196 }
154197
155198 /**
@@ -191,11 +234,65 @@ public static ClientRegistration.Builder fromOidcIssuerLocation(String issuer) {
191234 * described endpoints
192235 */
193236 public static ClientRegistration .Builder fromIssuerLocation (String issuer ) {
237+ return fromIssuerLocation (issuer , rest );
238+ }
239+
240+ /**
241+ * Creates a {@link ClientRegistration.Builder} using the provided <a href=
242+ * "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
243+ * by querying three different discovery endpoints serially, using the values in the
244+ * first successful response to initialize. If an endpoint returns anything other than
245+ * a 200 or a 4xx, the method will exit without attempting subsequent endpoints.
246+ *
247+ * <p>
248+ * The three endpoints are computed as follows, given that the {@code issuer} is
249+ * composed of a {@code host} and a {@code path}:
250+ * </p>
251+ *
252+ * <ol>
253+ * <li>{@code host/.well-known/openid-configuration/path}, as defined in
254+ * <a href="https://tools.ietf.org/html/rfc8414#section-5">RFC 8414's Compatibility
255+ * Notes</a>.</li>
256+ * <li>{@code issuer/.well-known/openid-configuration}, as defined in <a href=
257+ * "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationRequest">
258+ * OpenID Provider Configuration</a>.</li>
259+ * <li>{@code host/.well-known/oauth-authorization-server/path}, as defined in
260+ * <a href="https://tools.ietf.org/html/rfc8414#section-3.1">Authorization Server
261+ * Metadata Request</a>.</li>
262+ * </ol>
263+ *
264+ * Note that the second endpoint is the equivalent of calling
265+ * {@link ClientRegistrations#fromOidcIssuerLocation(String)}.
266+ *
267+ * <p>
268+ * This method uses the provided {@link RestOperations} to query issuer configuration.
269+ * </p>
270+ *
271+ * <p>
272+ * Example usage:
273+ * </p>
274+ * <pre>
275+ * RestTemplate rest = new RestTemplate();
276+ * ClientRegistration registration = ClientRegistrations.fromIssuerLocation("https://example.com", rest)
277+ * .clientId("client-id")
278+ * .clientSecret("client-secret")
279+ * .build();
280+ * </pre>
281+ * @param issuer the <a href=
282+ * "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a>
283+ * @param restOperations the {@link RestOperations} to use
284+ * @return a {@link ClientRegistration.Builder} that was initialized by one of the
285+ * described endpoints
286+ * @since 7.2
287+ */
288+ public static ClientRegistration .Builder fromIssuerLocation (String issuer , RestOperations restOperations ) {
194289 Assert .hasText (issuer , "issuer cannot be empty" );
195- return getBuilder (issuer , oidc (issuer ), oidcRfc8414 (issuer ), oauth (issuer ));
290+ Assert .notNull (restOperations , "restOperations cannot be null" );
291+ return getBuilder (issuer , oidc (issuer , restOperations ), oidcRfc8414 (issuer , restOperations ),
292+ oauth (issuer , restOperations ));
196293 }
197294
198- static Supplier <ClientRegistration .Builder > oidc (String issuer ) {
295+ static Supplier <ClientRegistration .Builder > oidc (String issuer , RestOperations rest ) {
199296 UriComponents uri = oidcUri (issuer );
200297 // @formatter:on
201298 return () -> {
@@ -220,10 +317,10 @@ static UriComponents oidcUri(String issuer) {
220317 .build ();
221318 }
222319
223- static Supplier <ClientRegistration .Builder > oidcRfc8414 (String issuer ) {
320+ static Supplier <ClientRegistration .Builder > oidcRfc8414 (String issuer , RestOperations rest ) {
224321 UriComponents uri = oidcRfc8414Uri (issuer );
225322 // @formatter:on
226- return getRfc8414Builder (issuer , uri );
323+ return getRfc8414Builder (issuer , uri , rest );
227324 }
228325
229326 static UriComponents oidcRfc8414Uri (String issuer ) {
@@ -234,9 +331,9 @@ static UriComponents oidcRfc8414Uri(String issuer) {
234331 .build ();
235332 }
236333
237- static Supplier <ClientRegistration .Builder > oauth (String issuer ) {
334+ static Supplier <ClientRegistration .Builder > oauth (String issuer , RestOperations rest ) {
238335 UriComponents uri = oauthUri (issuer );
239- return getRfc8414Builder (issuer , uri );
336+ return getRfc8414Builder (issuer , uri , rest );
240337 }
241338
242339 static UriComponents oauthUri (String issuer ) {
@@ -248,7 +345,8 @@ static UriComponents oauthUri(String issuer) {
248345 // @formatter:on
249346 }
250347
251- private static Supplier <ClientRegistration .Builder > getRfc8414Builder (String issuer , UriComponents uri ) {
348+ private static Supplier <ClientRegistration .Builder > getRfc8414Builder (String issuer , UriComponents uri ,
349+ RestOperations rest ) {
252350 return () -> {
253351 RequestEntity <Void > request = RequestEntity .get (uri .toUriString ()).build ();
254352 Map <String , Object > configuration = rest .exchange (request , typeReference ).getBody ();
@@ -317,9 +415,6 @@ private static <T> T parse(Map<String, Object> body, ThrowingFunction<JSONObject
317415 private static ClientRegistration .Builder withProviderConfiguration (AuthorizationServerMetadata metadata ,
318416 String issuer ) {
319417 String metadataIssuer = metadata .getIssuer ().getValue ();
320- Assert .state (issuer .equals (metadataIssuer ),
321- () -> "The Issuer \" " + metadataIssuer + "\" provided in the configuration metadata did "
322- + "not match the requested issuer \" " + issuer + "\" " );
323418 String name = URI .create (issuer ).getHost ();
324419 ClientAuthenticationMethod method = getClientAuthenticationMethod (metadata .getTokenEndpointAuthMethods ());
325420 URI authorizationEndpointURI = metadata .getAuthorizationEndpointURI ();
@@ -336,6 +431,7 @@ private static ClientRegistration.Builder withProviderConfiguration(Authorizatio
336431 .authorizationUri ((authorizationEndpointURI != null ) ? authorizationEndpointURI .toASCIIString () : null )
337432 .providerConfigurationMetadata (configurationMetadata )
338433 .issuerUri (issuer )
434+ .trustedIssuer (metadataIssuer )
339435 .clientName (issuer );
340436 if (tokenEndpointURI != null ) {
341437 builder .tokenUri (tokenEndpointURI .toASCIIString ());
0 commit comments