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 e72eec50a06..30c7e557db2 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 98f8837e806..aacaa603683 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 dbfaa3e0ae5..743af426053 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 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 resolveValidRedirects = new HashSet(); + 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; } }