Browse Source

Allow localhost in redirect_uri

Closes gh-651
Joe Grandja 2 years ago
parent
commit
ef4c5d7b6f

+ 7 - 19
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationValidator.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2022 the original author or authors.
+ * Copyright 2020-2023 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.
@@ -100,23 +100,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme
 						authorizationCodeRequestAuthentication, registeredClient);
 			}
 
-			String requestedRedirectHost = requestedRedirect.getHost();
-			if (requestedRedirectHost == null || requestedRedirectHost.equals("localhost")) {
-				// As per https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#section-9.7.1
-				// While redirect URIs using localhost (i.e., "http://localhost:{port}/{path}")
-				// function similarly to loopback IP redirects described in Section 10.3.3,
-				// the use of "localhost" is NOT RECOMMENDED.
-				OAuth2Error error = new OAuth2Error(
-						OAuth2ErrorCodes.INVALID_REQUEST,
-						"localhost is not allowed for the redirect_uri (" + requestedRedirectUri + "). " +
-								"Use the IP literal (127.0.0.1) instead.",
-						"https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#section-9.7.1");
-				throwError(error, OAuth2ParameterNames.REDIRECT_URI,
-						authorizationCodeRequestAuthentication, registeredClient);
-			}
-
-			if (!isLoopbackAddress(requestedRedirectHost)) {
-				// As per https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#section-9.7
+			if (!isLoopbackAddress(requestedRedirect.getHost())) {
+				// As per https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-22#section-4.1.3
 				// When comparing client redirect URIs against pre-registered URIs,
 				// authorization servers MUST utilize exact string matching.
 				if (!registeredClient.getRedirectUris().contains(requestedRedirectUri)) {
@@ -124,7 +109,7 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme
 							authorizationCodeRequestAuthentication, registeredClient);
 				}
 			} else {
-				// As per https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#section-10.3.3
+				// As per https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-08#section-8.4.2
 				// The authorization server MUST allow any port to be specified at the
 				// time of the request for loopback IP redirect URIs, to accommodate
 				// clients that obtain an available ephemeral port from the operating
@@ -157,6 +142,9 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme
 	}
 
 	private static boolean isLoopbackAddress(String host) {
+		if (!StringUtils.hasText(host)) {
+			return false;
+		}
 		// IPv6 loopback address should either be "0:0:0:0:0:0:0:1" or "::1"
 		if ("[0:0:0:0:0:0:0:1]".equals(host) || "[::1]".equals(host)) {
 			return true;

+ 0 - 21
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProviderTests.java

@@ -181,27 +181,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests {
 				);
 	}
 
-	// gh-243
-	@Test
-	public void authenticateWhenRedirectUriLocalhostThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
-		RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
-		when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
-				.thenReturn(registeredClient);
-		OAuth2AuthorizationCodeRequestAuthenticationToken authentication =
-				new OAuth2AuthorizationCodeRequestAuthenticationToken(
-						AUTHORIZATION_URI, registeredClient.getClientId(), principal,
-						"https://localhost:5000", STATE, registeredClient.getScopes(), null);
-		assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
-				.isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class)
-				.satisfies(ex ->
-						assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex,
-								OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI, null)
-				)
-				.extracting(ex -> ((OAuth2AuthorizationCodeRequestAuthenticationException) ex).getError())
-				.satisfies(error ->
-						assertThat(error.getDescription()).isEqualTo("localhost is not allowed for the redirect_uri (https://localhost:5000). Use the IP literal (127.0.0.1) instead."));
-	}
-
 	@Test
 	public void authenticateWhenUnregisteredRedirectUriThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() {
 		RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();