keycloak-uncached

Details

diff --git a/model/api/src/main/java/org/keycloak/models/Constants.java b/model/api/src/main/java/org/keycloak/models/Constants.java
index 0630397..bb9823e 100755
--- a/model/api/src/main/java/org/keycloak/models/Constants.java
+++ b/model/api/src/main/java/org/keycloak/models/Constants.java
@@ -13,4 +13,5 @@ public interface Constants {
     String ACCOUNT_MANAGEMENT_APP = "account";
 
     String INSTALLED_APP_URN = "urn:ietf:wg:oauth:2.0:oob";
+    String INSTALLED_APP_URL = "http://localhost";
 }
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 0cae7c2..1cbdf5f 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -8,6 +8,7 @@ import org.keycloak.OAuthErrorException;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.crypto.RSAProvider;
 import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakTransaction;
 import org.keycloak.models.RealmModel;
@@ -619,7 +620,26 @@ public class TokenService {
             return redirectUri;
         } else {
             String r = redirectUri.indexOf('?') != -1 ? redirectUri.substring(0, redirectUri.indexOf('?')) : redirectUri;
-            return client.getRedirectUris().contains(r) ? redirectUri : null;
+
+            boolean valid = client.getRedirectUris().contains(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());
+
+                    StringBuilder sb = new StringBuilder();
+                    sb.append(r.substring(0, i));
+
+                    i = r.indexOf('/', i);
+                    if (i >= 0) {
+                        sb.append(r.substring(i));
+                    }
+
+                    r = sb.toString();
+
+                valid = client.getRedirectUris().contains(r);
+            }
+
+            return valid ? redirectUri : null;
         }
     }
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
index 4e1f043..932c777 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AuthorizationCodeTest.java
@@ -96,11 +96,7 @@ public class AuthorizationCodeTest {
         keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
             @Override
             public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
-                for (ApplicationModel app : appRealm.getApplications()) {
-                    if (app.getName().equals("test-app")) {
-                        app.addRedirectUri(oauth.getRedirectUri());
-                    }
-                }
+                appRealm.getApplicationByName("test-app").addRedirectUri(oauth.getRedirectUri());
             }
         });
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java
index 55f66fc..0152868 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthRedirectUriTest.java
@@ -26,6 +26,7 @@ import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.Constants;
 import org.keycloak.models.RealmModel;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.OAuthClient;
@@ -49,6 +50,15 @@ public class OAuthRedirectUriTest {
         public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
             ApplicationModel app = appRealm.getApplicationNameMap().get("test-app");
             app.addRedirectUri("http://localhost:8081/app");
+
+            ApplicationModel installedApp = appRealm.addApplication("test-installed");
+            installedApp.setEnabled(true);
+            installedApp.addRedirectUri(Constants.INSTALLED_APP_URN);
+            installedApp.addRedirectUri(Constants.INSTALLED_APP_URL);
+
+            ApplicationModel installedApp2 = appRealm.addApplication("test-installed2");
+            installedApp2.setEnabled(true);
+            installedApp2.addRedirectUri(Constants.INSTALLED_APP_URL + "/myapp");
         }
     });
 
@@ -151,6 +161,15 @@ public class OAuthRedirectUriTest {
     }
 
     @Test
+    public void testValid() throws IOException {
+        oauth.redirectUri("http://localhost:8081/app");
+        OAuthClient.AuthorizationCodeResponse response = oauth.doLogin("test-user@localhost", "password");
+
+        Assert.assertNotNull(response.getCode());
+        Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/app?code="));
+    }
+
+    @Test
     public void testInvalid() throws IOException {
         oauth.redirectUri("http://localhost:8081/app2");
         oauth.openLoginForm();
@@ -168,4 +187,40 @@ public class OAuthRedirectUriTest {
         Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/app?key=value&code="));
     }
 
+
+    @Test
+    public void testLocalhost() throws IOException {
+        oauth.clientId("test-installed");
+
+        checkRedirectUri("urn:ietf:wg:oauth:2.0:oob", true);
+        checkRedirectUri("http://localhost", true);
+
+        checkRedirectUri("http://localhost:8081", true);
+
+        checkRedirectUri("http://localhosts", false);
+        checkRedirectUri("http://localhost/myapp", false);
+        checkRedirectUri("http://localhost:8081/myapp", false);
+
+        oauth.clientId("test-installed2");
+
+        checkRedirectUri("http://localhost/myapp", true);
+        checkRedirectUri("http://localhost:8081/myapp", true);
+
+        checkRedirectUri("http://localhosts/myapp", false);
+        checkRedirectUri("http://localhost", false);
+        checkRedirectUri("http://localhost/myapp2", false);
+    }
+
+    private void checkRedirectUri(String redirectUri, boolean expectValid) {
+        oauth.redirectUri(redirectUri);
+        oauth.openLoginForm();
+
+        if (expectValid) {
+            Assert.assertTrue(loginPage.isCurrent());
+        } else {
+            Assert.assertTrue(errorPage.isCurrent());
+            Assert.assertEquals("Invalid redirect_uri.", errorPage.getError());
+        }
+    }
+
 }