keycloak-uncached

For installed app urn redirect to a page instead of returning

5/21/2014 6:32:03 AM

Details

diff --git a/forms/common-themes/src/main/resources/theme/login/base/code.ftl b/forms/common-themes/src/main/resources/theme/login/base/code.ftl
index 84a98a5..23ca6a8 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/code.ftl
+++ b/forms/common-themes/src/main/resources/theme/login/base/code.ftl
@@ -12,7 +12,7 @@
                 <p>Please copy this code and paste it into your application:</p>
                 <textarea id="code" class="${properties.kcTextareaClass!}">${code.code}</textarea>
             <#else>
-                <p>${code.error}</p>
+                <p id="error">${code.error}</p>
             </#if>
         </div>
     </#if>
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 5ff6ca3..47be8e9 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -560,7 +560,7 @@ public class AccountService {
         ApplicationModel application = realm.getApplicationByName(referrer);
         if (application != null) {
             if (referrerUri != null) {
-                referrerUri = TokenService.verifyRedirectUri(uriInfo, referrerUri, application);
+                referrerUri = TokenService.verifyRedirectUri(uriInfo, referrerUri, realm, application);
             } else {
                 referrerUri = ResolveRelative.resolveRelativeUri(uriInfo.getRequestUri(), application.getBaseUrl());
             }
@@ -571,7 +571,7 @@ public class AccountService {
         } else if (referrerUri != null) {
             ClientModel client = realm.getOAuthClient(referrer);
             if (client != null) {
-                referrerUri = TokenService.verifyRedirectUri(uriInfo, referrerUri, application);
+                referrerUri = TokenService.verifyRedirectUri(uriInfo, referrerUri, realm, application);
 
                 if (referrerUri != null) {
                     return new String[]{referrer, referrerUri};
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
index 7bf80fa..65e1bb0 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java
@@ -86,34 +86,25 @@ public class OAuthFlows {
 
     public Response redirectAccessCode(AccessCodeEntry accessCode, UserSessionModel session, String state, String redirect, boolean rememberMe) {
         String code = accessCode.getCode();
-
-        if (Constants.INSTALLED_APP_URN.equals(redirect)) {
-            return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getId(), code).createCode();
-        } else {
-            UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.CODE, code);
-            log.debugv("redirectAccessCode: state: {0}", state);
-            if (state != null)
-                redirectUri.queryParam(OAuth2Constants.STATE, state);
-            Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
-            Cookie remember = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
-            rememberMe = rememberMe || remember != null;
-            // refresh the cookies!
-            authManager.createLoginCookie(realm, accessCode.getUser(), session, uriInfo, rememberMe);
-            if (rememberMe) authManager.createRememberMeCookie(realm, uriInfo);
-            return location.build();
-        }
+        UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.CODE, code);
+        log.debugv("redirectAccessCode: state: {0}", state);
+        if (state != null)
+            redirectUri.queryParam(OAuth2Constants.STATE, state);
+        Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
+        Cookie remember = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
+        rememberMe = rememberMe || remember != null;
+        // refresh the cookies!
+        authManager.createLoginCookie(realm, accessCode.getUser(), session, uriInfo, rememberMe);
+        if (rememberMe) authManager.createRememberMeCookie(realm, uriInfo);
+        return location.build();
     }
 
     public Response redirectError(ClientModel client, String error, String state, String redirect) {
-        if (Constants.INSTALLED_APP_URN.equals(redirect)) {
-            return Flows.forms(providerSession, realm, uriInfo).setError(error).createCode();
-        } else {
-            UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, error);
-            if (state != null) {
-                redirectUri.queryParam(OAuth2Constants.STATE, state);
-            }
-            return Response.status(302).location(redirectUri.build()).build();
+        UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, error);
+        if (state != null) {
+            redirectUri.queryParam(OAuth2Constants.STATE, state);
         }
+        return Response.status(302).location(redirectUri.build()).build();
     }
 
     public Response processAccessCode(String scopeParam, String state, String redirect, ClientModel client, UserModel user, UserSessionModel session, String username, boolean rememberMe, String authMethod, Audit audit) {
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
index 961fbcb..fa311fa 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
@@ -148,6 +148,10 @@ public class Urls {
         return tokenBase(baseUri).path(TokenService.class, "registerPage").build(realmId);
     }
 
+    public static URI realmInstalledAppUrnCallback(URI baseUri, String realmId) {
+        return tokenBase(baseUri).path(TokenService.class, "installedAppUrnCallback").build(realmId);
+    }
+
     public static URI realmOauthAction(URI baseUri, String realmId) {
         return tokenBase(baseUri).path(TokenService.class, "processOAuth").build(realmId);
     }
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 c7c5d23..8129ff1 100755
--- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
@@ -122,6 +122,10 @@ public class SocialResource {
         Map<String, String[]> queryParams = getQueryParams();
 
         RequestDetails requestData = getRequestDetails(queryParams);
+        if (requestData == null) {
+            Flows.forms(providerSession, null, uriInfo).setError("Unexpected callback").createErrorPage();
+        }
+
         SocialProvider provider = SocialLoader.load(requestData.getProviderId());
 
         String realmName = requestData.getClientAttribute("realm");
@@ -296,7 +300,7 @@ public class SocialResource {
             logger.warn("Login requester not enabled.");
             return Flows.forms(providerSession, realm, uriInfo).setError("Login requester not enabled.").createErrorPage();
         }
-        redirectUri = TokenService.verifyRedirectUri(uriInfo, redirectUri, client);
+        redirectUri = TokenService.verifyRedirectUri(uriInfo, redirectUri, realm, client);
         if (redirectUri == null) {
             audit.error(Errors.INVALID_REDIRECT_URI);
             return Flows.forms(providerSession, 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 f7feddc..0a5b33c 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -18,6 +18,7 @@ import org.keycloak.authentication.AuthenticationProviderException;
 import org.keycloak.authentication.AuthenticationProviderManager;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.crypto.RSAProvider;
+import org.keycloak.login.LoginFormsProvider;
 import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.Constants;
@@ -42,6 +43,7 @@ import org.keycloak.services.managers.TokenManager;
 import org.keycloak.services.messages.Messages;
 import org.keycloak.services.resources.flows.Flows;
 import org.keycloak.services.resources.flows.OAuthFlows;
+import org.keycloak.services.resources.flows.Urls;
 import org.keycloak.services.validation.Validation;
 import org.keycloak.util.BasicAuthHelper;
 import org.keycloak.util.Time;
@@ -363,7 +365,7 @@ public class TokenService {
             return oauth.forwardToSecurityFailure("Login requester not enabled.");
         }
 
-        redirect = verifyRedirectUri(uriInfo, redirect, client);
+        redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
         if (redirect == null) {
             audit.error(Errors.INVALID_REDIRECT_URI);
             return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
@@ -457,7 +459,7 @@ public class TokenService {
             return oauth.forwardToSecurityFailure("Login requester not enabled.");
         }
 
-        redirect = verifyRedirectUri(uriInfo, redirect, client);
+        redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
         if (redirect == null) {
             audit.error(Errors.INVALID_REDIRECT_URI);
             return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
@@ -754,7 +756,7 @@ public class TokenService {
             audit.error(Errors.NOT_ALLOWED);
             return oauth.forwardToSecurityFailure("Bearer-only applications are not allowed to initiate login");
         }
-        redirect = verifyRedirectUri(uriInfo, redirect, client);
+        redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
         if (redirect == null) {
             audit.error(Errors.INVALID_REDIRECT_URI);
             return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
@@ -811,7 +813,7 @@ public class TokenService {
             return oauth.forwardToSecurityFailure("Login requester not enabled.");
         }
 
-        redirect = verifyRedirectUri(uriInfo, redirect, client);
+        redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
         if (redirect == null) {
             audit.error(Errors.INVALID_REDIRECT_URI);
             return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
@@ -937,6 +939,17 @@ public class TokenService {
         return oauth.redirectAccessCode(accessCodeEntry, session, state, redirect);
     }
 
+    @Path("oauth/oob")
+    @GET
+    public Response installedAppUrnCallback(final @QueryParam("code") String code, final @QueryParam("error") String error, final @QueryParam("error_description") String errorDescription) {
+        LoginFormsProvider forms = Flows.forms(providerSession, realm, uriInfo);
+        if (code != null) {
+            return forms.setAccessCode(null, code).createCode();
+        } else {
+            return forms.setError(error).createCode();
+        }
+    }
+
     protected Response redirectAccessDenied(String redirect, String state) {
         UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, "access_denied");
         if (state != null)
@@ -961,7 +974,7 @@ public class TokenService {
         return false;
     }
 
-    public static String verifyRedirectUri(UriInfo uriInfo, String redirectUri, ClientModel client) {
+    public static String verifyRedirectUri(UriInfo uriInfo, String redirectUri, RealmModel realm, ClientModel client) {
         Set<String> validRedirects = client.getRedirectUris();
         if (redirectUri == null) {
             if (validRedirects.size() != 1) return null;
@@ -970,10 +983,10 @@ public class TokenService {
             if (idx > -1) {
                 validRedirect = validRedirect.substring(0, idx);
             }
-            return validRedirect;
+            redirectUri = validRedirect;
         } else if (validRedirects.isEmpty()) {
             logger.error("Redirect URI is required for client: " + client.getClientId());
-            return null;
+            redirectUri = null;
         } else {
             String r = redirectUri.indexOf('?') != -1 ? redirectUri.substring(0, redirectUri.indexOf('?')) : redirectUri;
             Set<String> resolveValidRedirects = resolveValidRedirects(uriInfo, validRedirects);
@@ -996,7 +1009,13 @@ public class TokenService {
 
                 valid = matchesRedirects(resolveValidRedirects, r);
             }
-            return valid ? redirectUri : null;
+            redirectUri = valid ? redirectUri : null;
+        }
+
+        if (Constants.INSTALLED_APP_URN.equals(redirectUri)) {
+            return Urls.realmInstalledAppUrnCallback(uriInfo.getBaseUri(), realm.getName()).toString();
+        } else {
+            return redirectUri;
         }
     }
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java
index 73f185f..3d04ffc 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java
@@ -34,7 +34,7 @@ import org.keycloak.models.UserModel;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.AssertEvents;
 import org.keycloak.testsuite.OAuthClient;
-import org.keycloak.testsuite.org.keycloak.testsuite.util.MailUtil;
+import org.keycloak.testsuite.MailUtil;
 import org.keycloak.testsuite.pages.AppPage;
 import org.keycloak.testsuite.pages.AppPage.RequestType;
 import org.keycloak.testsuite.pages.LoginPage;
@@ -50,8 +50,6 @@ import org.openqa.selenium.WebDriver;
 import javax.mail.MessagingException;
 import javax.mail.internet.MimeMessage;
 import java.io.IOException;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
index 719b98c..ae5cfce 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
@@ -36,7 +36,7 @@ import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.testsuite.AssertEvents;
 import org.keycloak.testsuite.OAuthClient;
-import org.keycloak.testsuite.org.keycloak.testsuite.util.MailUtil;
+import org.keycloak.testsuite.MailUtil;
 import org.keycloak.testsuite.pages.AppPage;
 import org.keycloak.testsuite.pages.AppPage.RequestType;
 import org.keycloak.testsuite.pages.LoginPage;
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 dd11d41..214ff5b 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
@@ -113,6 +113,35 @@ public class AuthorizationCodeTest {
     }
 
     @Test
+    public void authorizationRequestInstalledAppCancel() throws IOException {
+        keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                appRealm.getApplicationNameMap().get("test-app").addRedirectUri(Constants.INSTALLED_APP_URN);
+            }
+        });
+        oauth.redirectUri(Constants.INSTALLED_APP_URN);
+
+        oauth.openLoginForm();
+        driver.findElement(By.name("cancel")).click();
+
+        String title = driver.getTitle();
+        Assert.assertTrue(title.equals("Error error=access_denied"));
+
+        String error = driver.findElement(By.id(OAuth2Constants.ERROR)).getText();
+        Assert.assertEquals("access_denied", error);
+
+        events.expectLogin().error("rejected_by_user").user((String) null).session((String) null).removeDetail(Details.USERNAME).removeDetail(Details.CODE_ID).detail(Details.REDIRECT_URI, Constants.INSTALLED_APP_URN).assertEvent().getDetails().get(Details.CODE_ID);
+
+        keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                appRealm.getApplicationNameMap().get("test-app").removeRedirectUri(Constants.INSTALLED_APP_URN);
+            }
+        });
+    }
+
+    @Test
     public void authorizationValidRedirectUri() throws IOException {
         keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
             @Override