Details
diff --git a/forms/login-api/src/main/java/org/keycloak/login/LoginForms.java b/forms/login-api/src/main/java/org/keycloak/login/LoginForms.java
index 2eb3704..a02df45 100755
--- a/forms/login-api/src/main/java/org/keycloak/login/LoginForms.java
+++ b/forms/login-api/src/main/java/org/keycloak/login/LoginForms.java
@@ -43,6 +43,8 @@ public interface LoginForms {
public LoginForms setClient(ClientModel client);
+ public LoginForms setQueryParams(MultivaluedMap<String, String> queryParams);
+
public LoginForms setFormData(MultivaluedMap<String, String> formData);
public LoginForms setStatus(Response.Status status);
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java
index 69e17fc..a91aaec 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java
@@ -51,6 +51,7 @@ public class FreeMarkerLoginForms implements LoginForms {
private Response.Status status = Response.Status.OK;
private List<RoleModel> realmRolesRequested;
private MultivaluedMap<String, RoleModel> resourceRolesRequested;
+ private MultivaluedMap<String, String> queryParams;
public static enum MessageType {SUCCESS, WARNING, ERROR}
@@ -114,7 +115,7 @@ public class FreeMarkerLoginForms implements LoginForms {
}
private Response createResponse(LoginFormsPages page) {
- MultivaluedMap<String, String> queryParameterMap = uriInfo.getQueryParameters();
+ MultivaluedMap<String, String> queryParameterMap = queryParams != null ? queryParams : uriInfo.getQueryParameters();
String requestURI = uriInfo.getBaseUri().getPath();
UriBuilder uriBuilder = UriBuilder.fromUri(requestURI);
@@ -276,4 +277,9 @@ public class FreeMarkerLoginForms implements LoginForms {
return this;
}
+ @Override
+ public LoginForms setQueryParams(MultivaluedMap<String, String> queryParams) {
+ this.queryParams = queryParams;
+ return this;
+ }
}
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 4566c2e..d8ff9b1 100755
--- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java
@@ -23,7 +23,6 @@ package org.keycloak.services.resources;
import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
-import org.jboss.resteasy.spi.HttpResponse;
import org.keycloak.models.AccountRoles;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
@@ -32,22 +31,20 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.services.managers.AppAuthManager;
-import org.keycloak.services.managers.Auth;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.managers.SocialRequestManager;
import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.OAuthFlows;
import org.keycloak.services.resources.flows.Urls;
import org.keycloak.social.AuthCallback;
-import org.keycloak.social.AuthRequest;
import org.keycloak.social.RequestDetails;
+import org.keycloak.social.SocialAccessDeniedException;
import org.keycloak.social.SocialLoader;
import org.keycloak.social.SocialProvider;
import org.keycloak.social.SocialProviderConfig;
import org.keycloak.social.SocialProviderException;
-import org.keycloak.services.managers.SocialRequestManager;
import org.keycloak.social.SocialUser;
import javax.ws.rs.GET;
@@ -57,6 +54,7 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.container.ResourceContext;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
@@ -141,6 +139,14 @@ public class SocialResource {
SocialUser socialUser;
try {
socialUser = provider.processCallback(config, callback);
+ } catch (SocialAccessDeniedException e) {
+ MultivaluedHashMap<String, String> queryParms = new MultivaluedHashMap<String, String>();
+ queryParms.putSingle("client_id", requestData.getClientAttribute("clientId"));
+ queryParms.putSingle("state", requestData.getClientAttribute("state"));
+ queryParms.putSingle("scope", requestData.getClientAttribute("scope"));
+ queryParms.putSingle("redirect_uri", requestData.getClientAttribute("redirectUri"));
+ queryParms.putSingle("response_type", requestData.getClientAttribute("responseType"));
+ return Flows.forms(realm, request, uriInfo).setQueryParams(queryParms).setWarning("Access denied").createLogin();
} catch (SocialProviderException e) {
logger.warn("Failed to process social callback", e);
return oauth.forwardToSecurityFailure("Failed to process social callback");
@@ -210,7 +216,7 @@ public class SocialResource {
public Response redirectToProviderAuth(@PathParam("realm") final String realmName,
@QueryParam("provider_id") final String providerId, @QueryParam("client_id") final String clientId,
@QueryParam("scope") final String scope, @QueryParam("state") final String state,
- @QueryParam("redirect_uri") String redirectUri) {
+ @QueryParam("redirect_uri") String redirectUri, @QueryParam("response_type") String responseType) {
RealmManager realmManager = new RealmManager(session);
RealmModel realm = realmManager.getRealmByName(realmName);
@@ -239,20 +245,24 @@ public class SocialResource {
.putClientAttribute("realm", realmName)
.putClientAttribute("clientId", clientId).putClientAttribute("scope", scope)
.putClientAttribute("state", state).putClientAttribute("redirectUri", redirectUri)
- .redirectToSocialProvider();
+ .putClientAttribute("responseType", responseType).redirectToSocialProvider();
} catch (Throwable t) {
return Flows.forms(realm, request, uriInfo).setError("Failed to redirect to social auth").createErrorPage();
}
}
private RequestDetails getRequestDetails(Map<String, String[]> queryParams) {
- for (SocialProvider provider : SocialLoader.load()) {
- if (queryParams.containsKey(provider.getRequestIdParamName())) {
- String requestId = queryParams.get(provider.getRequestIdParamName())[0];
- if (socialRequestManager.isRequestId(requestId)) {
- return socialRequestManager.retrieveData(requestId);
- }
- }
+ String requestId = null;
+ if (queryParams.containsKey("state")) {
+ requestId = queryParams.get("state")[0];
+ } else if (queryParams.containsKey("oauth_token")) {
+ requestId = queryParams.get("oauth_token")[0];
+ } else if (queryParams.containsKey("denied")) {
+ requestId = queryParams.get("denied")[0];
+ }
+
+ if (requestId != null && socialRequestManager.isRequestId(requestId)) {
+ return socialRequestManager.retrieveData(requestId);
}
return null;
diff --git a/social/core/src/main/java/org/keycloak/social/AbstractOAuth2Provider.java b/social/core/src/main/java/org/keycloak/social/AbstractOAuth2Provider.java
index ae8a0a5..7c6d2d7 100644
--- a/social/core/src/main/java/org/keycloak/social/AbstractOAuth2Provider.java
+++ b/social/core/src/main/java/org/keycloak/social/AbstractOAuth2Provider.java
@@ -51,6 +51,15 @@ public abstract class AbstractOAuth2Provider implements SocialProvider {
@Override
public SocialUser processCallback(SocialProviderConfig config, AuthCallback callback) throws SocialProviderException {
+ String error = callback.getQueryParam("error");
+ if (error != null) {
+ if (error.equals("access_denied")) {
+ throw new SocialAccessDeniedException();
+ } else {
+ throw new SocialProviderException(error);
+ }
+ }
+
try {
String code = callback.getQueryParam(CODE);
@@ -82,9 +91,4 @@ public abstract class AbstractOAuth2Provider implements SocialProvider {
}
}
- @Override
- public String getRequestIdParamName() {
- return STATE;
- }
-
}
diff --git a/social/core/src/main/java/org/keycloak/social/SocialAccessDeniedException.java b/social/core/src/main/java/org/keycloak/social/SocialAccessDeniedException.java
new file mode 100644
index 0000000..6f9d0c7
--- /dev/null
+++ b/social/core/src/main/java/org/keycloak/social/SocialAccessDeniedException.java
@@ -0,0 +1,7 @@
+package org.keycloak.social;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class SocialAccessDeniedException extends SocialProviderException {
+}
diff --git a/social/core/src/main/java/org/keycloak/social/SocialProvider.java b/social/core/src/main/java/org/keycloak/social/SocialProvider.java
index 70da76d..73a8f21 100644
--- a/social/core/src/main/java/org/keycloak/social/SocialProvider.java
+++ b/social/core/src/main/java/org/keycloak/social/SocialProvider.java
@@ -31,8 +31,6 @@ public interface SocialProvider {
AuthRequest getAuthUrl(SocialProviderConfig config) throws SocialProviderException;
- String getRequestIdParamName();
-
String getName();
SocialUser processCallback(SocialProviderConfig config, AuthCallback callback) throws SocialProviderException;
diff --git a/social/core/src/main/java/org/keycloak/social/SocialProviderException.java b/social/core/src/main/java/org/keycloak/social/SocialProviderException.java
index 98cd2c3..5697046 100644
--- a/social/core/src/main/java/org/keycloak/social/SocialProviderException.java
+++ b/social/core/src/main/java/org/keycloak/social/SocialProviderException.java
@@ -28,6 +28,9 @@ public class SocialProviderException extends Exception {
private static final long serialVersionUID = 1L;
+ protected SocialProviderException() {
+ }
+
public SocialProviderException(String message) {
super(message);
}
diff --git a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java
index 7842ff4..10df5cd 100755
--- a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java
+++ b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java
@@ -23,6 +23,7 @@ package org.keycloak.social.twitter;
import org.keycloak.social.AuthCallback;
import org.keycloak.social.AuthRequest;
+import org.keycloak.social.SocialAccessDeniedException;
import org.keycloak.social.SocialProvider;
import org.keycloak.social.SocialProviderConfig;
import org.keycloak.social.SocialProviderException;
@@ -67,6 +68,10 @@ public class TwitterProvider implements SocialProvider {
@Override
public SocialUser processCallback(SocialProviderConfig config, AuthCallback callback) throws SocialProviderException {
+ if (callback.getQueryParam("denied") != null) {
+ throw new SocialAccessDeniedException();
+ }
+
try {
Twitter twitter = new TwitterFactory().getInstance();
twitter.setOAuthConsumer(config.getKey(), config.getSecret());
@@ -86,9 +91,4 @@ public class TwitterProvider implements SocialProvider {
}
}
- @Override
- public String getRequestIdParamName() {
- return "oauth_token";
- }
-
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java
index 71a3563..5ad7cf6 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java
@@ -2,6 +2,7 @@ package org.keycloak.testsuite;
import org.keycloak.social.AuthCallback;
import org.keycloak.social.AuthRequest;
+import org.keycloak.social.SocialAccessDeniedException;
import org.keycloak.social.SocialProvider;
import org.keycloak.social.SocialProviderConfig;
import org.keycloak.social.SocialProviderException;
@@ -27,17 +28,17 @@ public class DummySocial implements SocialProvider {
}
@Override
- public String getRequestIdParamName() {
- return "state";
- }
-
- @Override
public String getName() {
return "Dummy Provider";
}
@Override
public SocialUser processCallback(SocialProviderConfig config, AuthCallback callback) throws SocialProviderException {
+ String error = callback.getQueryParam("error");
+ if (error != null) {
+ throw new SocialAccessDeniedException();
+ }
+
if (!callback.getQueryParam("state").equals(callback.getAttribute("state"))) {
throw new SocialProviderException("Invalid state");
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java
index cc93a39..e87ce1d 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java
@@ -27,8 +27,8 @@ public class DummySocialServlet extends HttpServlet {
pw.print("<label for=\"firstname\">First Name</label><input type=\"text\" id=\"firstname\" name=\"firstname\" />");
pw.print("<label for=\"lastname\">Last Name</label><input type=\"text\" id=\"lastname\" name=\"lastname\" />");
pw.print("<label for=\"email\">Email</label><input type=\"text\" id=\"email\" name=\"email\" />");
- pw.print("<input type=\"submit\" id=\"submit\" value=\"login\" />");
- pw.print("<input type=\"submit\" id=\"cancel\" value=\"cancel\" />");
+ pw.print("<input type=\"submit\" id=\"login\" name=\"login\" value=\"login\" />");
+ pw.print("<input type=\"submit\" id=\"cancel\" name=\"cancel\" value=\"cancel\" />");
pw.print("</form>");
pw.print("</body>");
pw.print("</html>");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
index 3671ae0..a266b8a 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
@@ -62,6 +62,9 @@ public class LoginPage extends AbstractPage {
@FindBy(className = "feedback-error")
private WebElement loginErrorMessage;
+ @FindBy(className = "feedback-warning")
+ private WebElement loginWarningMessage;
+
public void login(String username, String password) {
usernameInput.clear();
usernameInput.sendKeys(username);
@@ -80,6 +83,11 @@ public class LoginPage extends AbstractPage {
return loginErrorMessage != null ? loginErrorMessage.getText() : null;
}
+ public String getWarning() {
+ return loginWarningMessage != null ? loginWarningMessage.getText() : null;
+ }
+
+
public boolean isCurrent() {
return driver.getTitle().equals("Log in to test");
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java
index 01d136f..18c4281 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java
@@ -102,7 +102,7 @@ public class SocialLoginTest {
driver.findElement(By.id("firstname")).sendKeys("Bob");
driver.findElement(By.id("lastname")).sendKeys("Builder");
driver.findElement(By.id("email")).sendKeys("bob@builder.com");
- driver.findElement(By.id("submit")).click();
+ driver.findElement(By.id("login")).click();
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
@@ -128,19 +128,12 @@ public class SocialLoginTest {
driver.findElement(By.id("cancel")).click();
- Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
-
- AccessTokenResponse response = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get("code"), "password");
-
- AccessToken token = oauth.verifyToken(response.getAccessToken());
- Assert.assertEquals(36, token.getSubject().length());
+ Assert.assertTrue(loginPage.isCurrent());
+ Assert.assertEquals("Access denied", loginPage.getWarning());
- UserRepresentation profile = keycloakRule.getUserById("test", token.getSubject());
- Assert.assertEquals(36, profile.getUsername().length());
+ loginPage.login("test-user@localhost", "password");
- Assert.assertEquals("Bob", profile.getFirstName());
- Assert.assertEquals("Builder", profile.getLastName());
- Assert.assertEquals("bob@builder.com", profile.getEmail());
+ Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
}
@Test
@@ -162,7 +155,7 @@ public class SocialLoginTest {
driver.findElement(By.id("firstname")).sendKeys("Bob");
driver.findElement(By.id("lastname")).sendKeys("Builder");
driver.findElement(By.id("email")).sendKeys("bob@builder.com");
- driver.findElement(By.id("submit")).click();
+ driver.findElement(By.id("login")).click();
profilePage.isCurrent();