diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index e72eec5..30c7e55 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -512,7 +512,7 @@ public class AccountService {
ApplicationModel application = realm.getApplicationByName(referrer);
if (application != null) {
if (referrerUri != null) {
- referrerUri = TokenService.verifyRedirectUri(referrerUri, application);
+ referrerUri = TokenService.verifyRedirectUri(uriInfo, referrerUri, application);
} else {
referrerUri = application.getBaseUrl();
}
@@ -523,7 +523,7 @@ public class AccountService {
} else if (referrerUri != null) {
ClientModel client = realm.getOAuthClient(referrer);
if (client != null) {
- referrerUri = TokenService.verifyRedirectUri(referrerUri, application);
+ referrerUri = TokenService.verifyRedirectUri(uriInfo, referrerUri, application);
if (referrerUri != null) {
return new String[]{referrer, referrerUri};
diff --git a/services/src/main/java/org/keycloak/services/resources/SocialResource.java b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
index 98f8837..aacaa60 100755
--- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
@@ -290,7 +290,7 @@ public class SocialResource {
logger.warn("Login requester not enabled.");
return Flows.forms(realm, uriInfo).setError("Login requester not enabled.").createErrorPage();
}
- redirectUri = TokenService.verifyRedirectUri(redirectUri, client);
+ redirectUri = TokenService.verifyRedirectUri(uriInfo, redirectUri, client);
if (redirectUri == null) {
audit.error(Errors.INVALID_REDIRECT_URI);
return Flows.forms(realm, uriInfo).setError("Invalid redirect_uri.").createErrorPage();
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index dbfaa3e..743af42 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -63,7 +63,9 @@ import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Providers;
+import java.net.URI;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -288,7 +290,7 @@ public class TokenService {
return oauth.forwardToSecurityFailure("Login requester not enabled.");
}
- redirect = verifyRedirectUri(redirect, client);
+ redirect = verifyRedirectUri(uriInfo, redirect, client);
if (redirect == null) {
audit.error(Errors.INVALID_REDIRECT_URI);
return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
@@ -377,7 +379,7 @@ public class TokenService {
return oauth.forwardToSecurityFailure("Login requester not enabled.");
}
- redirect = verifyRedirectUri(redirect, client);
+ redirect = verifyRedirectUri(uriInfo, redirect, client);
if (redirect == null) {
audit.error(Errors.INVALID_REDIRECT_URI);
return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
@@ -636,7 +638,7 @@ public class TokenService {
audit.error(Errors.CLIENT_DISABLED);
return oauth.forwardToSecurityFailure("Login requester not enabled.");
}
- redirect = verifyRedirectUri(redirect, client);
+ redirect = verifyRedirectUri(uriInfo, redirect, client);
if (redirect == null) {
audit.error(Errors.INVALID_REDIRECT_URI);
return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
@@ -690,7 +692,7 @@ public class TokenService {
return oauth.forwardToSecurityFailure("Login requester not enabled.");
}
- redirect = verifyRedirectUri(redirect, client);
+ redirect = verifyRedirectUri(uriInfo, redirect, client);
if (redirect == null) {
audit.error(Errors.INVALID_REDIRECT_URI);
return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
@@ -813,10 +815,11 @@ public class TokenService {
return false;
}
- public static String verifyRedirectUri(String redirectUri, ClientModel client) {
+ public static String verifyRedirectUri(UriInfo uriInfo, String redirectUri, ClientModel client) {
+ Set<String> validRedirects = client.getRedirectUris();
if (redirectUri == null) {
- return client.getRedirectUris().size() == 1 ? client.getRedirectUris().iterator().next() : null;
- } else if (client.getRedirectUris().isEmpty()) {
+ return validRedirects.size() == 1 ? validRedirects.iterator().next() : null;
+ } else if (validRedirects.isEmpty()) {
if (client.isPublicClient()) {
logger.error("Client redirect uri must be registered for public client");
return null;
@@ -825,7 +828,22 @@ public class TokenService {
} else {
String r = redirectUri.indexOf('?') != -1 ? redirectUri.substring(0, redirectUri.indexOf('?')) : redirectUri;
- boolean valid = matchesRedirects(client.getRedirectUris(), r);
+ // If the valid redirect URI is relative (no scheme, host, port) then use the request's scheme, host, and port
+ Set<String> resolveValidRedirects = new HashSet<String>();
+ for (String validRedirect : validRedirects) {
+ if (validRedirect.startsWith("/")) {
+ URI baseUri = uriInfo.getBaseUri();
+ String uri = baseUri.getScheme() + "://" + baseUri.getHost();
+ if (baseUri.getPort() != -1) {
+ uri += ":" + baseUri.getPort();
+ }
+ validRedirect = uri + validRedirect;
+ logger.debugv("replacing relative valid redirect with: {0}", validRedirect);
+ }
+ resolveValidRedirects.add(validRedirect);
+ }
+
+ boolean valid = matchesRedirects(resolveValidRedirects, r);
if (!valid && r.startsWith(Constants.INSTALLED_APP_URL) && r.indexOf(':', Constants.INSTALLED_APP_URL.length()) >= 0) {
int i = r.indexOf(':', Constants.INSTALLED_APP_URL.length());
@@ -840,9 +858,8 @@ public class TokenService {
r = sb.toString();
- valid = matchesRedirects(client.getRedirectUris(), r);
+ valid = matchesRedirects(resolveValidRedirects, r);
}
-
return valid ? redirectUri : null;
}
}