keycloak-aplcache
Changes
connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_2_0_Beta1.java 4(+4 -0)
examples/broker/twitter-authentication/src/main/java/org/keycloak/examples/broker/twitter/TwitterShowUserServlet.java 2(+1 -1)
examples/saml/pom.xml 2(+1 -1)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-kerberos.html 7(+7 -0)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-oidc.html 7(+7 -0)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html 7(+7 -0)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-social.html 7(+7 -0)
forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/IdentityProviderBean.java 2(+1 -1)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java 3(+2 -1)
services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java 7(+6 -1)
services/src/main/java/org/keycloak/services/resources/AuthenticationBrokerResource.java 495(+0 -495)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java 17(+11 -6)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java 21(+19 -2)
Details
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/FederatedIdentity.java b/broker/core/src/main/java/org/keycloak/broker/provider/FederatedIdentity.java
index 8f81a6f..761decc 100644
--- a/broker/core/src/main/java/org/keycloak/broker/provider/FederatedIdentity.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/FederatedIdentity.java
@@ -31,6 +31,7 @@ public class FederatedIdentity {
private String lastName;
private String email;
private String token;
+ private String identityProviderId;
public FederatedIdentity(String id) {
if (id == null) {
@@ -92,4 +93,25 @@ public class FederatedIdentity {
public String getToken() {
return this.token;
}
+
+ public String getIdentityProviderId() {
+ return this.identityProviderId;
+ }
+
+ public void setIdentityProviderId(String identityProviderId) {
+ this.identityProviderId = identityProviderId;
+ }
+
+ @Override
+ public String toString() {
+ return "{" +
+ "id='" + id + '\'' +
+ ", username='" + username + '\'' +
+ ", firstName='" + firstName + '\'' +
+ ", lastName='" + lastName + '\'' +
+ ", email='" + email + '\'' +
+ ", token='" + token + '\'' +
+ ", identityProviderId='" + identityProviderId + '\'' +
+ '}';
+ }
}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityBrokerException.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityBrokerException.java
new file mode 100644
index 0000000..858c820
--- /dev/null
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityBrokerException.java
@@ -0,0 +1,32 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.provider;
+
+/**
+ * @author pedroigor
+ */
+public class IdentityBrokerException extends RuntimeException {
+
+ public IdentityBrokerException(String message) {
+ super(message);
+ }
+
+ public IdentityBrokerException(String message, Throwable t) {
+ super(message, t);
+ }
+}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
index f84765a..ac1e3dc 100644
--- a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
@@ -68,5 +68,12 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
*/
AuthenticationResponse handleResponse(AuthenticationRequest request);
+ /**
+ * <p>Returns a {@link javax.ws.rs.core.Response} containing the token previously stored during the authentication process for a
+ * specific user.</p>
+ *
+ * @param identity
+ * @return
+ */
Response retrieveToken(FederatedIdentityModel identity);
}
diff --git a/broker/kerberos/src/main/java/org/keycloak/broker/kerberos/KerberosIdentityProvider.java b/broker/kerberos/src/main/java/org/keycloak/broker/kerberos/KerberosIdentityProvider.java
index 56b30fd..84ceb22 100644
--- a/broker/kerberos/src/main/java/org/keycloak/broker/kerberos/KerberosIdentityProvider.java
+++ b/broker/kerberos/src/main/java/org/keycloak/broker/kerberos/KerberosIdentityProvider.java
@@ -103,15 +103,23 @@ public class KerberosIdentityProvider extends AbstractIdentityProvider<KerberosI
logger.trace("Sending back " + HttpHeaders.WWW_AUTHENTICATE + ": " + negotiateHeader);
}
- // Error page is rendered just if browser is unable to send Authorization header with SPNEGO token
- Response response = request.getSession().getProvider(LoginFormsProvider.class)
+ Response response;
+ LoginFormsProvider loginFormsProvider = request.getSession().getProvider(LoginFormsProvider.class)
.setRealm(request.getRealm())
.setUriInfo(request.getUriInfo())
- .setClient(request.getClientSession().getClient())
- .setClientSessionCode(getRelayState(request))
- .setWarning("errorKerberosLogin")
- .setStatus(Response.Status.UNAUTHORIZED)
- .createLogin();
+ .setStatus(Response.Status.UNAUTHORIZED);
+
+ if (request.getClientSession().getUserSession() == null) {
+ // User not logged. Display HTML with login form as fallback if SPNEGO token not found
+ response = loginFormsProvider.setClient(request.getClientSession().getClient())
+ .setClientSessionCode(getRelayState(request))
+ .setWarning("errorKerberosLogin")
+ .createLogin();
+ } else {
+ // User logged and linking account. Display HTML with error if SPNEGO token not found
+ response = loginFormsProvider.setError("errorKerberosLinkAccount")
+ .createErrorPage();
+ }
response.getMetadata().putSingle(HttpHeaders.WWW_AUTHENTICATE, negotiateHeader);
return AuthenticationResponse.fromResponse(response);
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
index 14bc767..34075e9 100644
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
@@ -25,6 +25,7 @@ import org.keycloak.broker.provider.AbstractIdentityProvider;
import org.keycloak.broker.provider.AuthenticationRequest;
import org.keycloak.broker.provider.AuthenticationResponse;
import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.models.FederatedIdentityModel;
import javax.ws.rs.core.Response;
@@ -68,7 +69,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
return AuthenticationResponse.temporaryRedirect(authorizationUrl);
} catch (Exception e) {
- throw new RuntimeException("Could not create authentication request.", e);
+ throw new IdentityBrokerException("Could not create authentication request.", e);
}
}
@@ -85,9 +86,9 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
if (error != null) {
if (error.equals("access_denied")) {
- throw new RuntimeException("Access denied.");
+ throw new IdentityBrokerException("Access denied.");
} else {
- throw new RuntimeException(error);
+ throw new IdentityBrokerException(error);
}
}
@@ -111,9 +112,9 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
return AuthenticationResponse.end(federatedIdentity);
}
- throw new RuntimeException("No authorization code from identity provider.");
+ throw new IdentityBrokerException("No authorization code from identity provider.");
} catch (Exception e) {
- throw new RuntimeException("Could not process response from identity provider.", e);
+ throw new IdentityBrokerException("Could not process response from identity provider.", e);
}
}
@@ -132,7 +133,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
try {
return mapper.readTree(response).get(tokenName).getTextValue();
} catch (IOException e) {
- throw new RuntimeException("Could not extract token [" + tokenName + "] from response [" + response + "].", e);
+ throw new IdentityBrokerException("Could not extract token [" + tokenName + "] from response [" + response + "].", e);
}
} else {
Matcher matcher = Pattern.compile(tokenName + "=([^&]+)").matcher(response);
@@ -149,7 +150,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
String accessToken = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);
if (accessToken == null) {
- throw new RuntimeException("No access token from server.");
+ throw new IdentityBrokerException("No access token from server.");
}
return doGetFederatedIdentity(accessToken);
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
index 50a5f03..2c43d61 100644
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
@@ -21,6 +21,7 @@ import org.codehaus.jackson.JsonNode;
import org.keycloak.broker.oidc.util.SimpleHttp;
import org.keycloak.broker.provider.AuthenticationRequest;
import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.jose.jws.JWSInput;
import javax.ws.rs.core.UriBuilder;
@@ -62,7 +63,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
String accessToken = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);
if (accessToken == null) {
- throw new RuntimeException("No access_token from server.");
+ throw new IdentityBrokerException("No access_token from server.");
}
String idToken = extractTokenFromResponse(response, OIDC_PARAMETER_ID_TOKEN);
@@ -101,13 +102,13 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
return identity;
} catch (Exception e) {
- throw new RuntimeException("Could not fetch attributes from userinfo endpoint.", e);
+ throw new IdentityBrokerException("Could not fetch attributes from userinfo endpoint.", e);
}
}
private void validateIdToken(String idToken) {
if (idToken == null) {
- throw new RuntimeException("No id_token from server.");
+ throw new IdentityBrokerException("No id_token from server.");
}
try {
@@ -131,10 +132,10 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
}
}
- throw new RuntimeException("Wrong issuer from id_token..");
+ throw new IdentityBrokerException("Wrong issuer from id_token..");
}
} catch (IOException e) {
- throw new RuntimeException("Could not decode id token.", e);
+ throw new IdentityBrokerException("Could not decode id token.", e);
}
}
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
index 8e458bc..1ad696c 100644
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
@@ -22,6 +22,7 @@ import org.keycloak.broker.provider.AbstractIdentityProvider;
import org.keycloak.broker.provider.AuthenticationRequest;
import org.keycloak.broker.provider.AuthenticationResponse;
import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder;
@@ -112,11 +113,11 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
PublicKey publicKey = request.getRealm().getPublicKey();
if (privateKey == null) {
- throw new RuntimeException("Identity Provider [" + getConfig().getName() + "] wants a signed authentication request. But the Realm [" + request.getRealm().getName() + "] does not have a private key.");
+ throw new IdentityBrokerException("Identity Provider [" + getConfig().getName() + "] wants a signed authentication request. But the Realm [" + request.getRealm().getName() + "] does not have a private key.");
}
if (publicKey == null) {
- throw new RuntimeException("Identity Provider [" + getConfig().getName() + "] wants a signed authentication request. But the Realm [" + request.getRealm().getName() + "] does not have a public key.");
+ throw new IdentityBrokerException("Identity Provider [" + getConfig().getName() + "] wants a signed authentication request. But the Realm [" + request.getRealm().getName() + "] does not have a public key.");
}
KeyPair keypair = new KeyPair(publicKey, privateKey);
@@ -131,7 +132,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
return AuthenticationResponse.fromResponse(authnRequestBuilder.redirectBinding().request());
}
} catch (Exception e) {
- throw new RuntimeException("Could not create authentication request.", e);
+ throw new IdentityBrokerException("Could not create authentication request.", e);
}
}
@@ -145,7 +146,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
String samlResponse = getRequestParameter(request, SAML_RESPONSE_PARAMETER);
if (samlResponse == null) {
- throw new RuntimeException("No response from SAML identity provider.");
+ throw new IdentityBrokerException("No response from SAML identity provider.");
}
try {
@@ -167,7 +168,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
return AuthenticationResponse.end(identity);
} catch (Exception e) {
- throw new RuntimeException("Could not process response from SAML identity provider.", e);
+ throw new IdentityBrokerException("Could not process response from SAML identity provider.", e);
}
}
@@ -194,7 +195,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
List<RTChoiceType> assertions = responseType.getAssertions();
if (assertions.isEmpty()) {
- throw new RuntimeException("No assertion from response.");
+ throw new IdentityBrokerException("No assertion from response.");
}
RTChoiceType rtChoiceType = assertions.get(0);
@@ -234,7 +235,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
detailMessage.append("none");
}
- throw new RuntimeException("Authentication failed with code [" + statusCode.getValue() + " and detail [" + detailMessage.toString() + ".");
+ throw new IdentityBrokerException("Authentication failed with code [" + statusCode.getValue() + " and detail [" + detailMessage.toString() + ".");
}
}
@@ -246,7 +247,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
Element enc = DocumentUtil.getElement(doc, new QName(JBossSAMLConstants.ENCRYPTED_ASSERTION.get()));
if (enc == null) {
- throw new RuntimeException("No encrypted assertion found.");
+ throw new IdentityBrokerException("No encrypted assertion found.");
}
String oldID = enc.getAttribute(JBossSAMLConstants.ID.get());
@@ -265,7 +266,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
return responseType;
} catch (Exception e) {
- throw new RuntimeException("Could not decrypt assertion.", e);
+ throw new IdentityBrokerException("Could not decrypt assertion.", e);
}
}
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
index a62fb92..f48615b 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
@@ -43,6 +43,7 @@
<column name="PROVIDER_ID" type="VARCHAR(255)"/>
<column name="UPDATE_PROFILE_FIRST_LOGIN" type="BOOLEAN(1)"/>
<column name="STORE_TOKEN" type="BOOLEAN(1)"/>
+ <column name="AUTHENTICATE_BY_DEFAULT" type="BOOLEAN(1)"/>
<column name="REALM_ID" type="VARCHAR(36)"/>
</createTable>
<createTable tableName="IDENTITY_PROVIDER_CONFIG">
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_2_0_Beta1.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_2_0_Beta1.java
index a24253c..83d8cc6 100644
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_2_0_Beta1.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_2_0_Beta1.java
@@ -8,6 +8,7 @@ import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
+import org.keycloak.models.utils.KeycloakModelUtils;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -52,11 +53,14 @@ public class Update1_2_0_Beta1 extends Update {
.add("clientSecret", clientSecret).get();
DBObject identityProvider = new BasicDBObjectBuilder()
+ .add("internalId", KeycloakModelUtils.generateId())
.add("providerId", socialProviderId)
.add("name", socialProviderId)
.add("id", socialProviderId)
.add("updateProfileFirstLogin", updateProfileOnInitialSocialLogin)
.add("enabled", true)
+ .add("storeToken", false)
+ .add("authenticateByDefault", false)
.add("config", identityProviderConfig).get();
identityProviders.add(identityProvider);
diff --git a/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java
index b9f09c3..c0b0852 100644
--- a/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java
@@ -32,6 +32,7 @@ public class IdentityProviderRepresentation {
protected boolean enabled = true;
protected boolean updateProfileFirstLogin = true;
protected boolean storeToken;
+ protected boolean authenticateByDefault;
protected String groupName;
protected Map<String, String> config = new HashMap<String, String>();
@@ -91,6 +92,14 @@ public class IdentityProviderRepresentation {
this.updateProfileFirstLogin = updateProfileFirstLogin;
}
+ public boolean isAuthenticateByDefault() {
+ return authenticateByDefault;
+ }
+
+ public void setAuthenticateByDefault(boolean authenticateByDefault) {
+ this.authenticateByDefault = authenticateByDefault;
+ }
+
public boolean isStoreToken() {
return this.storeToken;
}
diff --git a/events/api/src/main/java/org/keycloak/events/Details.java b/events/api/src/main/java/org/keycloak/events/Details.java
index 1ca58ec..fa1e57a 100755
--- a/events/api/src/main/java/org/keycloak/events/Details.java
+++ b/events/api/src/main/java/org/keycloak/events/Details.java
@@ -12,6 +12,8 @@ public interface Details {
String REDIRECT_URI = "redirect_uri";
String RESPONSE_TYPE = "response_type";
String AUTH_METHOD = "auth_method";
+ String IDENTITY_PROVIDER = "identity_provider";
+ String IDENTITY_PROVIDER_IDENTITY = "identity_provider_identity";
String REGISTER_METHOD = "register_method";
String USERNAME = "username";
String REMEMBER_ME = "remember_me";
diff --git a/events/api/src/main/java/org/keycloak/events/Errors.java b/events/api/src/main/java/org/keycloak/events/Errors.java
index 2fa691e..94e5a7e 100755
--- a/events/api/src/main/java/org/keycloak/events/Errors.java
+++ b/events/api/src/main/java/org/keycloak/events/Errors.java
@@ -35,7 +35,6 @@ public interface Errors {
String NOT_ALLOWED = "not_allowed";
- String IDENTITY_PROVIDER_NOT_FOUND = "identity_provider_not_found";
String FEDERATED_IDENTITY_EMAIL_EXISTS = "federated_identity_email_exists";
String FEDERATED_IDENTITY_USERNAME_EXISTS = "federated_identity_username_exists";
String FEDERATED_IDENTITY_DISABLED_REGISTRATION = "federated_identity_disabled_registration";
diff --git a/events/api/src/main/java/org/keycloak/events/EventType.java b/events/api/src/main/java/org/keycloak/events/EventType.java
index d292c4c..d66660d 100755
--- a/events/api/src/main/java/org/keycloak/events/EventType.java
+++ b/events/api/src/main/java/org/keycloak/events/EventType.java
@@ -48,5 +48,14 @@ public enum EventType {
UNREGISTER_NODE,
USER_INFO_REQUEST,
- USER_INFO_REQUEST_ERROR
+ USER_INFO_REQUEST_ERROR,
+
+ IDENTITY_PROVIDER_LOGIN,
+ IDENTITY_PROVIDER_LOGIN_ERROR,
+ IDENTITY_PROVIDER_RESPONSE,
+ IDENTITY_PROVIDER_RESPONSE_ERROR,
+ IDENTITY_PROVIDER_RETRIEVE_TOKEN,
+ IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR,
+ IDENTITY_PROVIDER_ACCCOUNT_LINKING,
+ IDENTITY_PROVIDER_ACCCOUNT_LINKING_ERROR,
}
diff --git a/examples/broker/facebook-authentication/src/main/webapp/js/app.js b/examples/broker/facebook-authentication/src/main/webapp/js/app.js
index 14999a7..07ba177 100755
--- a/examples/broker/facebook-authentication/src/main/webapp/js/app.js
+++ b/examples/broker/facebook-authentication/src/main/webapp/js/app.js
@@ -83,7 +83,7 @@ module.controller('GlobalCtrl', function($scope, $http, $location, Auth) {
$scope.identity = Auth.getIdentity();
$scope.loadSocialProfile = function() {
- $http.get('http://localhost:8081/auth/broker/facebook-identity-provider-realm/facebook/token').success(function(data) {
+ $http.get('http://localhost:8081/auth/realms/facebook-identity-provider-realm/broker/facebook/token').success(function(data) {
var accessTokenParameter = 'access_token=';
var accessToken = data.substring(data.indexOf(accessTokenParameter) + accessTokenParameter.length, data.indexOf('&'));
diff --git a/examples/broker/google-authentication/src/main/webapp/js/app.js b/examples/broker/google-authentication/src/main/webapp/js/app.js
index 1662f35..a7e363c 100755
--- a/examples/broker/google-authentication/src/main/webapp/js/app.js
+++ b/examples/broker/google-authentication/src/main/webapp/js/app.js
@@ -83,7 +83,7 @@ module.controller('GlobalCtrl', function($scope, $http, $location, Auth) {
$scope.identity = Auth.getIdentity();
$scope.loadSocialProfile = function() {
- $http.get('http://localhost:8081/auth/broker/google-identity-provider-realm/google/token').success(function(data) {
+ $http.get('http://localhost:8081/auth/realms/google-identity-provider-realm/broker/google/token').success(function(data) {
var accessToken = data.access_token;
var req = {
diff --git a/examples/broker/saml-broker-authentication/saml-broker-realm.json b/examples/broker/saml-broker-authentication/saml-broker-realm.json
index 8c28ba1..016b843 100644
--- a/examples/broker/saml-broker-authentication/saml-broker-realm.json
+++ b/examples/broker/saml-broker-authentication/saml-broker-realm.json
@@ -31,7 +31,7 @@
"name": "http://localhost:8080/auth/",
"enabled": true,
"redirectUris": [
- "http://localhost:8080/auth/broker/saml-broker-authentication-realm/saml-identity-provider"
+ "http://localhost:8080/auth/realms/saml-broker-authentication-realm/broker/saml-identity-provider"
],
"attributes": {
"saml.assertion.signature": "true",
diff --git a/examples/broker/twitter-authentication/src/main/java/org/keycloak/examples/broker/twitter/TwitterShowUserServlet.java b/examples/broker/twitter-authentication/src/main/java/org/keycloak/examples/broker/twitter/TwitterShowUserServlet.java
index 625284b..7fdcb4e 100644
--- a/examples/broker/twitter-authentication/src/main/java/org/keycloak/examples/broker/twitter/TwitterShowUserServlet.java
+++ b/examples/broker/twitter-authentication/src/main/java/org/keycloak/examples/broker/twitter/TwitterShowUserServlet.java
@@ -114,7 +114,7 @@ public class TwitterShowUserServlet extends HttpServlet {
}
private String getIdentityProviderTokenUrl() {
- return this.authServer + "/broker/" + this.realmName + "/" + this.identityProvider.getId() + "/token";
+ return this.authServer + "/realms/" + this.realmName + "/broker/" + this.identityProvider.getId() + "/token";
}
private void initKeyCloakClient(ServletConfig config) {
examples/saml/pom.xml 2(+1 -1)
diff --git a/examples/saml/pom.xml b/examples/saml/pom.xml
index e8be627..bd10f77 100755
--- a/examples/saml/pom.xml
+++ b/examples/saml/pom.xml
@@ -3,7 +3,7 @@
<parent>
<artifactId>examples-pom</artifactId>
<groupId>org.keycloak</groupId>
- <version>1.1.0-Alpha1-SNAPSHOT</version>
+ <version>1.2.0.Beta1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<name>Provider Examples</name>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
index 4971ee1..5c25e1b 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
@@ -653,6 +653,8 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
$scope.identityProvider.name = providerFactory.name;
$scope.identityProvider.enabled = true;
$scope.identityProvider.updateProfileFirstLogin = true;
+ // Kerberos is suggested as default provider, others not
+ $scope.identityProvider.authenticateByDefault = (providerFactory.id === "kerberos");
$scope.newIdentityProvider = true;
}
@@ -719,7 +721,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
}
}, true);
- $scope.callbackUrl = $location.absUrl().replace(/\/admin.*/, "/broker/") + realm.realm + "/" ;
+ $scope.callbackUrl = $location.absUrl().replace(/\/admin.*/, "/realms/") + realm.realm + "/broker/" ;
$scope.addProvider = function(provider) {
$location.url("/create/identity-provider/" + realm.realm + "/" + provider.id);
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-kerberos.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-kerberos.html
index 489628a..b061234 100644
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-kerberos.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-kerberos.html
@@ -60,6 +60,13 @@
</div>
<span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span>
</div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label" for="authenticateByDefault">Authenticate By Default</label>
+ <div class="col-sm-4">
+ <input ng-model="identityProvider.authenticateByDefault" name="identityProvider.authenticateByDefault" id="authenticateByDefault" onoffswitch />
+ </div>
+ <span tooltip-placement="right" tooltip="Indicates if this provider should be tried by default for authentication even before displaying login screen" class="fa fa-info-circle"></span>
+ </div>
</fieldset>
<div class="pull-right form-actions">
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-oidc.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-oidc.html
index 1e4de59..8680ac1 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-oidc.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-oidc.html
@@ -113,6 +113,13 @@
</div>
<span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span>
</div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label" for="authenticateByDefault">Authenticate By Default</label>
+ <div class="col-sm-4">
+ <input ng-model="identityProvider.authenticateByDefault" name="identityProvider.authenticateByDefault" id="authenticateByDefault" onoffswitch />
+ </div>
+ <span tooltip-placement="right" tooltip="Indicates if this provider should be tried by default for authentication even before displaying login screen" class="fa fa-info-circle"></span>
+ </div>
</fieldset>
<div class="pull-right form-actions">
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html
index 91cf006..2f08e48 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html
@@ -114,6 +114,13 @@
</div>
<span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span>
</div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label" for="authenticateByDefault">Authenticate By Default</label>
+ <div class="col-sm-4">
+ <input ng-model="identityProvider.authenticateByDefault" name="identityProvider.authenticateByDefault" id="authenticateByDefault" onoffswitch />
+ </div>
+ <span tooltip-placement="right" tooltip="Indicates if this provider should be tried by default for authentication even before displaying login screen" class="fa fa-info-circle"></span>
+ </div>
</fieldset>
<div class="pull-right form-actions">
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-social.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-social.html
index e5a28e1..3e33cc6 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-social.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-social.html
@@ -70,6 +70,13 @@
</div>
<span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span>
</div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label" for="authenticateByDefault">Authenticate By Default</label>
+ <div class="col-sm-4">
+ <input ng-model="identityProvider.authenticateByDefault" name="identityProvider.authenticateByDefault" id="authenticateByDefault" onoffswitch />
+ </div>
+ <span tooltip-placement="right" tooltip="Indicates if this provider should be tried by default for authentication even before displaying login screen" class="fa fa-info-circle"></span>
+ </div>
</fieldset>
<div class="pull-right form-actions">
diff --git a/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties b/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
index 3819d67..c7e1fff 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
+++ b/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
@@ -34,7 +34,7 @@ invalidPassword=Invalid username or password.
invalidEmail=Invalid email address
accountDisabled=Account is disabled, contact admin
accountTemporarilyDisabled=Account is temporarily disabled, contact admin or try again later
-expiredCode=Login timeout or unknown action. Please login again
+expiredCode=Login timeout. Please login again
missingFirstName=Please specify first name
missingLastName=Please specify last name
@@ -98,7 +98,8 @@ actionPasswordWarning=You need to change your password to activate your account.
actionEmailWarning=You need to verify your email address to activate your account.
actionFollow=Please fill in the fields below.
-errorKerberosLogin=Unable to login with Kerberos. Request Kerberos ticket or use different login mechanism
+errorKerberosLogin=Kerberos ticket not available. Use different login mechanism
+errorKerberosLinkAccount=Kerberos ticket not available.
successHeader=Success!
errorHeader=Error!
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/IdentityProviderBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/IdentityProviderBean.java
index 5a77a1a..dfa2f71 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/IdentityProviderBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/IdentityProviderBean.java
@@ -61,7 +61,7 @@ public class IdentityProviderBean {
}
}
- String loginUrl = Urls.identityProviderAuthnRequest(baseURI, identityProvider, realm).toString();
+ String loginUrl = Urls.identityProviderAuthnRequest(baseURI, identityProvider.getId(), realm.getName()).toString();
providers.add(new IdentityProvider(identityProvider.getId(), identityProvider.getName(), loginUrl));
}
}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/IdentityProviderEntity.java b/model/api/src/main/java/org/keycloak/models/entities/IdentityProviderEntity.java
index 4fb33f2..3343b90 100644
--- a/model/api/src/main/java/org/keycloak/models/entities/IdentityProviderEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/IdentityProviderEntity.java
@@ -32,6 +32,7 @@ public class IdentityProviderEntity {
private boolean enabled;
private boolean updateProfileFirstLogin;
private boolean storeToken;
+ private boolean authenticateByDefault;
private Map<String, String> config = new HashMap<String, String>();
@@ -67,6 +68,14 @@ public class IdentityProviderEntity {
this.updateProfileFirstLogin = updateProfileFirstLogin;
}
+ public boolean isAuthenticateByDefault() {
+ return authenticateByDefault;
+ }
+
+ public void setAuthenticateByDefault(boolean authenticateByDefault) {
+ this.authenticateByDefault = authenticateByDefault;
+ }
+
public boolean isStoreToken() {
return this.storeToken;
}
diff --git a/model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java b/model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java
index 76ca2d8..ca7063f 100644
--- a/model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java
+++ b/model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java
@@ -53,6 +53,11 @@ public class IdentityProviderModel {
private boolean storeToken;
/**
+ * Specifies if particular provider should be used by default for authentication even before displaying login screen
+ */
+ private boolean authenticateByDefault;
+
+ /**
* <p>A map containing the configuration and properties for a specific identity provider instance and implementation. The items
* in the map are understood by the identity provider implementation.</p>
*/
@@ -70,6 +75,7 @@ public class IdentityProviderModel {
this.enabled = model.isEnabled();
this.updateProfileFirstLogin = model.isUpdateProfileFirstLogin();
this.storeToken = model.isStoreToken();
+ this.authenticateByDefault = model.isAuthenticateByDefault();
}
public String getInternalId() {
@@ -128,6 +134,14 @@ public class IdentityProviderModel {
this.storeToken = storeToken;
}
+ public boolean isAuthenticateByDefault() {
+ return authenticateByDefault;
+ }
+
+ public void setAuthenticateByDefault(boolean authenticateByDefault) {
+ this.authenticateByDefault = authenticateByDefault;
+ }
+
public Map<String, String> getConfig() {
return this.config;
}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 444d704..72b62d2 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -314,6 +314,7 @@ public class ModelToRepresentation {
providerRep.setEnabled(identityProviderModel.isEnabled());
providerRep.setStoreToken(identityProviderModel.isStoreToken());
providerRep.setUpdateProfileFirstLogin(identityProviderModel.isUpdateProfileFirstLogin());
+ providerRep.setAuthenticateByDefault(identityProviderModel.isAuthenticateByDefault());
providerRep.setConfig(identityProviderModel.getConfig());
return providerRep;
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 8463168..8a0fc26 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -759,6 +759,7 @@ public class RepresentationToModel {
identityProviderModel.setName(representation.getName());
identityProviderModel.setEnabled(representation.isEnabled());
identityProviderModel.setUpdateProfileFirstLogin(representation.isUpdateProfileFirstLogin());
+ identityProviderModel.setAuthenticateByDefault(representation.isAuthenticateByDefault());
identityProviderModel.setStoreToken(representation.isStoreToken());
identityProviderModel.setConfig(representation.getConfig());
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/IdentityProviderEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/IdentityProviderEntity.java
index 408d706..b1a9f10 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/IdentityProviderEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/IdentityProviderEntity.java
@@ -50,6 +50,9 @@ public class IdentityProviderEntity {
@Column(name="STORE_TOKEN")
private boolean storeToken;
+ @Column(name="AUTHENTICATE_BY_DEFAULT")
+ private boolean authenticateByDefault;
+
@ElementCollection
@MapKeyColumn(name="name")
@Column(name="value", columnDefinition = "TEXT")
@@ -120,6 +123,14 @@ public class IdentityProviderEntity {
this.storeToken = storeToken;
}
+ public boolean isAuthenticateByDefault() {
+ return authenticateByDefault;
+ }
+
+ public void setAuthenticateByDefault(boolean authenticateByDefault) {
+ this.authenticateByDefault = authenticateByDefault;
+ }
+
public Map<String, String> getConfig() {
return this.config;
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index 97c0a24..bbc885e 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -1124,6 +1124,7 @@ public class RealmAdapter implements RealmModel {
identityProviderModel.setConfig(entity.getConfig());
identityProviderModel.setEnabled(entity.isEnabled());
identityProviderModel.setUpdateProfileFirstLogin(entity.isUpdateProfileFirstLogin());
+ identityProviderModel.setAuthenticateByDefault(entity.isAuthenticateByDefault());
identityProviderModel.setStoreToken(entity.isStoreToken());
identityProviders.add(identityProviderModel);
@@ -1154,6 +1155,7 @@ public class RealmAdapter implements RealmModel {
entity.setEnabled(identityProvider.isEnabled());
entity.setStoreToken(identityProvider.isStoreToken());
entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
+ entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
entity.setConfig(identityProvider.getConfig());
realm.addIdentityProvider(entity);
@@ -1180,6 +1182,7 @@ public class RealmAdapter implements RealmModel {
entity.setName(identityProvider.getName());
entity.setEnabled(identityProvider.isEnabled());
entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
+ entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
entity.setStoreToken(identityProvider.isStoreToken());
entity.setConfig(identityProvider.getConfig());
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
index aa3dcd4..52d4c98 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
@@ -251,7 +251,8 @@ public class MongoUserProvider implements UserProvider {
@Override
public FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm) {
FederatedIdentityEntity federatedIdentityEntity = findSocialLink(user, socialProvider, realm);
- return federatedIdentityEntity != null ? new FederatedIdentityModel(federatedIdentityEntity.getIdentityProvider(), federatedIdentityEntity.getUserId(), federatedIdentityEntity.getUserName()) : null;
+ return federatedIdentityEntity != null ? new FederatedIdentityModel(federatedIdentityEntity.getIdentityProvider(), federatedIdentityEntity.getUserId(),
+ federatedIdentityEntity.getUserName(), federatedIdentityEntity.getToken()) : null;
}
@Override
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index dafeb22..f24cc02 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -940,6 +940,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
identityProviderModel.setConfig(entity.getConfig());
identityProviderModel.setEnabled(entity.isEnabled());
identityProviderModel.setUpdateProfileFirstLogin(entity.isUpdateProfileFirstLogin());
+ identityProviderModel.setAuthenticateByDefault(entity.isAuthenticateByDefault());
identityProviderModel.setStoreToken(entity.isStoreToken());
identityProviders.add(identityProviderModel);
@@ -969,6 +970,8 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
entity.setName(identityProvider.getName());
entity.setEnabled(identityProvider.isEnabled());
entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
+ entity.setStoreToken(identityProvider.isStoreToken());
+ entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
entity.setConfig(identityProvider.getConfig());
realm.getIdentityProviders().add(entity);
@@ -995,6 +998,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
entity.setName(identityProvider.getName());
entity.setEnabled(identityProvider.isEnabled());
entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
+ entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
entity.setStoreToken(identityProvider.isStoreToken());
entity.setConfig(identityProvider.getConfig());
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java
index da7d190..fd20b16 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java
@@ -877,9 +877,7 @@ public class OpenIDConnectService {
.setError("Could not find an identity provider with the identifier [" + idpHint + "].")
.createErrorPage();
}
-
- return Response.temporaryRedirect(
- Urls.identityProviderAuthnRequest(this.uriInfo.getBaseUri(), identityProviderModel, realm, accessCode)).build();
+ return redirectToIdentityProvider(idpHint, accessCode);
}
response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
@@ -890,16 +888,18 @@ public class OpenIDConnectService {
return oauth.cancelLogin(clientSession);
}
- List<RequiredCredentialModel> requiredCredentials = realm.getRequiredCredentials();
+ List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
+ for (IdentityProviderModel identityProvider : identityProviders) {
+ if (identityProvider.isAuthenticateByDefault()) {
+ return redirectToIdentityProvider(identityProvider.getId(), accessCode);
+ }
+ }
+ List<RequiredCredentialModel> requiredCredentials = realm.getRequiredCredentials();
if (requiredCredentials.isEmpty()) {
- List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
-
if (!identityProviders.isEmpty()) {
if (identityProviders.size() == 1) {
- return Response.temporaryRedirect(
- Urls.identityProviderAuthnRequest(this.uriInfo.getBaseUri(), identityProviders.get(0), this.realm, accessCode))
- .build();
+ return redirectToIdentityProvider(identityProviders.get(0).getId(), accessCode);
}
return Flows.forms(session, realm, null, uriInfo).setError("Realm [" + this.realm.getName() + "] supports multiple identity providers. Could not determine which identity provider should be used to authenticate with.").createErrorPage();
@@ -1197,6 +1197,13 @@ public class OpenIDConnectService {
return Response.status(status).entity(e).type("application/json").build();
}
+ private Response redirectToIdentityProvider(String providerId, String accessCode) {
+ logger.debug("Automatically redirect to identity provider: " + providerId);
+ return Response.temporaryRedirect(
+ Urls.identityProviderAuthnRequest(this.uriInfo.getBaseUri(), providerId, this.realm.getName(), accessCode))
+ .build();
+ }
+
TokenManager getTokenManager() {
return this.tokenManager;
}
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 70b3ebb..241b37a 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -46,7 +46,6 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
-import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.protocol.oidc.OpenIDConnect;
@@ -684,14 +683,9 @@ public class AccountService {
clientSession.setRedirectUri(redirectUri);
clientSession.setNote(OpenIDConnect.STATE_PARAM, UUID.randomUUID().toString());
- URI url = UriBuilder.fromUri(this.uriInfo.getBaseUri())
- .path(AuthenticationBrokerResource.class)
- .path(AuthenticationBrokerResource.class, "performLogin")
- .queryParam("provider_id", providerId)
- .queryParam("code", clientSessionCode.getCode())
- .build(this.realm.getName());
-
- return Response.temporaryRedirect(url).build();
+ return Response.temporaryRedirect(
+ Urls.identityProviderAuthnRequest(this.uriInfo.getBaseUri(), providerId, realm.getName(), clientSessionCode.getCode()))
+ .build();
} catch (Exception spe) {
setReferrerOnPage();
return account.setError(Messages.IDENTITY_PROVIDER_REDIRECT_ERROR).createResponse(AccountPages.FEDERATED_IDENTITY);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
index 42c3497..a8ea3a4 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
@@ -106,7 +106,12 @@ public class IdentityProvidersResource {
String providerId = formDataMap.get("providerId").get(0).getBodyAsString();
String enabled = formDataMap.get("enabled").get(0).getBodyAsString();
String updateProfileFirstLogin = formDataMap.get("updateProfileFirstLogin").get(0).getBodyAsString();
- String storeToken = formDataMap.get("storeToken").get(0).getBodyAsString();
+ String storeToken = "false";
+
+ if (formDataMap.containsKey("storeToken")) {
+ storeToken = formDataMap.get("storeToken").get(0).getBodyAsString();
+ }
+
InputPart file = formDataMap.get("file").get(0);
InputStream inputStream = file.getBody(InputStream.class, null);
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
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 5dc22d2..3d7b19c 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
@@ -22,12 +22,10 @@
package org.keycloak.services.resources.flows;
import org.keycloak.OAuth2Constants;
-import org.keycloak.models.IdentityProviderModel;
-import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.OpenIDConnect;
import org.keycloak.protocol.oidc.OpenIDConnectService;
import org.keycloak.services.resources.AccountService;
-import org.keycloak.services.resources.AuthenticationBrokerResource;
+import org.keycloak.services.resources.IdentityBrokerService;
import org.keycloak.services.resources.LoginActionsService;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.resources.ThemeResource;
@@ -68,21 +66,31 @@ public class Urls {
return accountBase(baseUri).path(AccountService.class, "processFederatedIdentityUpdate").build(realmName);
}
- public static URI identityProviderAuthnRequest(URI baseURI, IdentityProviderModel identityProvider, RealmModel realm, String accessCode) {
- UriBuilder uriBuilder = UriBuilder.fromUri(baseURI)
- .path(AuthenticationBrokerResource.class)
- .path(AuthenticationBrokerResource.class, "performLogin")
- .replaceQueryParam("provider_id", identityProvider.getId());
+ public static URI identityProviderAuthnResponse(URI baseUri, String providerId, String realmName) {
+ return realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
+ .path(IdentityBrokerService.class, "handleResponseGet")
+ .build(realmName, providerId);
+ }
+
+ public static URI identityProviderAuthnRequest(URI baseUri, String providerId, String realmName, String accessCode) {
+ UriBuilder uriBuilder = realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
+ .path(IdentityBrokerService.class, "performLogin");
if (accessCode != null) {
uriBuilder.replaceQueryParam(OAuth2Constants.CODE, accessCode);
}
- return uriBuilder.build(realm.getName());
+ return uriBuilder.build(realmName, providerId);
+ }
+
+ public static URI identityProviderRetrieveToken(URI baseUri, String providerId, String realmName) {
+ return realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
+ .path(IdentityBrokerService.class, "retrieveToken")
+ .build(realmName, providerId);
}
- public static URI identityProviderAuthnRequest(URI baseURI, IdentityProviderModel identityProvider, RealmModel realm) {
- return identityProviderAuthnRequest(baseURI, identityProvider, realm, null);
+ public static URI identityProviderAuthnRequest(URI baseURI, String providerId, String realmName) {
+ return identityProviderAuthnRequest(baseURI, providerId, realmName, null);
}
public static URI accountTotpPage(URI baseUri, String realmId) {
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
new file mode 100644
index 0000000..1bbb581
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -0,0 +1,605 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.ClientConnection;
+import org.keycloak.broker.provider.AuthenticationRequest;
+import org.keycloak.broker.provider.AuthenticationResponse;
+import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
+import org.keycloak.broker.provider.IdentityProvider;
+import org.keycloak.broker.provider.IdentityProviderFactory;
+import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.services.managers.AppAuthManager;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.AuthenticationManager.AuthResult;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.managers.EventsManager;
+import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.services.resources.flows.Urls;
+import org.keycloak.social.SocialIdentityProvider;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT;
+import static org.keycloak.models.ClientSessionModel.Action.AUTHENTICATE;
+import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_APP;
+import static org.keycloak.models.UserModel.RequiredAction.UPDATE_PROFILE;
+
+/**
+ * <p></p>
+ *
+ * @author Pedro Igor
+ */
+@Path("/broker")
+public class IdentityBrokerService {
+
+ private static final Logger LOGGER = Logger.getLogger(IdentityBrokerService.class);
+
+ private final RealmModel realmModel;
+
+ @Context
+ private UriInfo uriInfo;
+
+ @Context
+ private KeycloakSession session;
+
+ @Context
+ private ClientConnection clientConnection;
+
+ @Context
+ private HttpRequest request;
+ private EventBuilder event;
+
+ public IdentityBrokerService(RealmModel realmModel) {
+ if (realmModel == null) {
+ throw new IllegalArgumentException("Realm can not be null.");
+ }
+
+ this.realmModel = realmModel;
+ }
+
+ public void init() {
+ this.event = new EventsManager(this.realmModel, this.session, this.clientConnection).createEventBuilder().event(EventType.IDENTITY_PROVIDER_LOGIN);
+ }
+
+ @GET
+ @Path("/{provider_id}/login")
+ public Response performLogin(@PathParam("provider_id") String providerId, @QueryParam("code") String code) {
+ this.event.detail(Details.IDENTITY_PROVIDER, providerId);
+
+ if (isDebugEnabled()) {
+ LOGGER.debugf("Sending authentication request to identity provider [%s].", providerId);
+ }
+
+ try {
+ ClientSessionCode clientSessionCode = parseClientSessionCode(code, providerId);
+ IdentityProvider identityProvider = getIdentityProvider(providerId);
+ AuthenticationResponse authenticationResponse = identityProvider.handleRequest(createAuthenticationRequest(providerId, clientSessionCode));
+
+ Response response = authenticationResponse.getResponse();
+
+ if (response != null) {
+ this.event.success();
+ if (isDebugEnabled()) {
+ LOGGER.debugf("Identity provider [%s] is going to send a request [%s].", identityProvider, response);
+ }
+ return response;
+ }
+ } catch (IdentityBrokerException e) {
+ return redirectToErrorPage("Could not send authentication request to identity provider [" + providerId + "].", e);
+ } catch (Exception e) {
+ return redirectToErrorPage("Unexpected error when handling authentication request to identity provider [" + providerId + "].", e);
+ }
+
+ return redirectToErrorPage("Could not proceed with authentication request to identity provider.");
+ }
+
+ @GET
+ @Path("{provider_id}")
+ public Response handleResponseGet(@PathParam("provider_id") String providerId) {
+ return handleResponse(providerId);
+ }
+
+ @POST
+ @Path("{provider_id}")
+ public Response handleResponsePost(@PathParam("provider_id") String providerId) {
+ return handleResponse(providerId);
+ }
+
+ @Path("{provider_id}/token")
+ @OPTIONS
+ public Response retrieveTokenPreflight() {
+ return Cors.add(this.request, Response.ok()).auth().preflight().build();
+ }
+
+ @GET
+ @Path("{provider_id}/token")
+ public Response retrieveToken(@PathParam("provider_id") String providerId) {
+ return getToken(providerId, false);
+ }
+
+ private Response getToken(String providerId, boolean forceRetrieval) {
+ this.event.event(EventType.IDENTITY_PROVIDER_RETRIEVE_TOKEN);
+
+ try {
+ AppAuthManager authManager = new AppAuthManager();
+ AuthResult authResult = authManager.authenticateBearerToken(this.session, this.realmModel, this.uriInfo, this.clientConnection, this.request.getHttpHeaders());
+
+ if (authResult != null) {
+ String audience = authResult.getToken().getAudience();
+ ClientModel clientModel = this.realmModel.findClient(audience);
+
+ if (clientModel == null) {
+ return badRequest("Invalid client.");
+ }
+
+ if (!clientModel.hasIdentityProvider(providerId)) {
+ return corsResponse(badRequest("Client [" + audience + "] not authorized."), clientModel);
+ }
+
+ if (OAuthClientModel.class.isInstance(clientModel) && !forceRetrieval) {
+ return corsResponse(Flows.forms(this.session, this.realmModel, clientModel, this.uriInfo)
+ .setClientSessionCode(authManager.extractAuthorizationHeaderToken(this.request.getHttpHeaders()))
+ .setAccessRequest("Your information from " + providerId + " identity provider.")
+ .setClient(clientModel)
+ .setUriInfo(this.uriInfo)
+ .setActionUri(this.uriInfo.getRequestUri())
+ .createOAuthGrant(), clientModel);
+ }
+
+ IdentityProvider identityProvider = getIdentityProvider(providerId);
+ IdentityProviderModel identityProviderConfig = getIdentityProviderConfig(providerId);
+
+ if (identityProviderConfig.isStoreToken()) {
+ FederatedIdentityModel identity = this.session.users().getFederatedIdentity(authResult.getUser(), providerId, this.realmModel);
+
+ if (identity == null) {
+ return corsResponse(badRequest("User [" + authResult.getUser().getId() + "] is not associated with identity provider [" + providerId + "]."), clientModel);
+ }
+
+ this.event.success();
+
+ return corsResponse(identityProvider.retrieveToken(identity), clientModel);
+ }
+
+ return corsResponse(badRequest("Identity Provider [" + providerId + "] does not support this operation."), clientModel);
+ }
+
+ return badRequest("Invalid token.");
+ } catch (IdentityBrokerException e) {
+ return redirectToErrorPage("Could not obtain token fron identity provider [" + providerId + "].", e);
+ } catch (Exception e) {
+ return redirectToErrorPage("Unexpected error when retrieving token from identity provider [" + providerId + "].", e);
+ }
+ }
+
+ @POST
+ @Path("{provider_id}/token")
+ @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+ public Response consentTokenRetrieval(@PathParam("provider_id") String providerId,
+ MultivaluedMap<String, String> formData) {
+ if (formData.containsKey("cancel")) {
+ return redirectToErrorPage("Permission not approved.");
+ }
+
+ return getToken(providerId, true);
+ }
+
+ private Response handleResponse(String providerId) {
+ if (isDebugEnabled()) {
+ LOGGER.debugf("Handling authentication response from identity provider [%s].", providerId);
+ }
+ this.event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
+ this.event.detail(Details.IDENTITY_PROVIDER, providerId);
+ IdentityProviderModel identityProviderConfig = getIdentityProviderConfig(providerId);
+
+ try {
+ IdentityProvider identityProvider = getIdentityProvider(providerId);
+ String relayState = identityProvider.getRelayState(createAuthenticationRequest(providerId, null));
+
+ if (relayState == null) {
+ return redirectToErrorPage("No relay state in response from identity identity [" + providerId + ".");
+ }
+
+ if (isDebugEnabled()) {
+ LOGGER.debugf("Relay state is valid: [%s].", relayState);
+ }
+
+ ClientSessionCode clientSessionCode = parseClientSessionCode(relayState, providerId);
+ AuthenticationResponse authenticationResponse = identityProvider.handleResponse(createAuthenticationRequest(providerId, clientSessionCode));
+ Response response = authenticationResponse.getResponse();
+
+ if (response != null) {
+ if (isDebugEnabled()) {
+ LOGGER.debugf("Identity provider [%s] is going to send a response [%s].", identityProvider, response);
+ }
+ return response;
+ }
+
+ FederatedIdentity identity = authenticationResponse.getUser();
+
+ if (isDebugEnabled()) {
+ LOGGER.debugf("Identity provider [%s] returned with identity [%s].", providerId, identity);
+ }
+
+ if (!identityProviderConfig.isStoreToken()) {
+ if (isDebugEnabled()) {
+ LOGGER.debugf("Token will not be stored for identity provider [%s].", providerId);
+ }
+ identity.setToken(null);
+ }
+
+ identity.setIdentityProviderId(providerId);
+
+ return performLocalAuthentication(identity, clientSessionCode);
+ } catch (IdentityBrokerException e) {
+ rollback();
+ return redirectToErrorPage("Authentication failed. Could not authenticate with identity provider [" + providerId + "].", e);
+ } catch (Exception e) {
+ rollback();
+ return redirectToErrorPage("Unexpected error when handling response from identity provider [" + providerId + "].", e);
+ } finally {
+ if (this.session.getTransaction().isActive()) {
+ this.session.getTransaction().commit();
+ }
+ }
+ }
+
+ private Response performLocalAuthentication(FederatedIdentity updatedIdentity, ClientSessionCode clientCode) {
+ ClientSessionModel clientSession = clientCode.getClientSession();
+ IdentityProviderModel identityProviderConfig = getIdentityProviderConfig(updatedIdentity.getIdentityProviderId());
+ String providerId = identityProviderConfig.getId();
+ FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(providerId, updatedIdentity.getId(),
+ updatedIdentity.getUsername(), updatedIdentity.getToken());
+
+ this.event.event(EventType.IDENTITY_PROVIDER_LOGIN)
+ .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
+ .detail(Details.IDENTITY_PROVIDER_IDENTITY, updatedIdentity.getUsername());
+
+ UserModel federatedUser = this.session.users().getUserByFederatedIdentity(federatedIdentityModel, this.realmModel);
+
+ // Check if federatedUser is already authenticated (this means linking social into existing federatedUser account)
+ if (clientSession.getUserSession() != null) {
+ return performAccountLinking(clientSession, providerId, federatedIdentityModel, federatedUser);
+ }
+
+ if (federatedUser == null) {
+ try {
+ federatedUser = createUser(updatedIdentity);
+
+ if (identityProviderConfig.isUpdateProfileFirstLogin()) {
+ if (isDebugEnabled()) {
+ LOGGER.debugf("Identity provider requires update profile action.", federatedUser);
+ }
+ federatedUser.addRequiredAction(UPDATE_PROFILE);
+ }
+ } catch (Exception e) {
+ return redirectToLoginPage(e.getMessage(), clientCode);
+ }
+ }
+
+ updateFederatedIdentity(updatedIdentity, federatedUser);
+
+ UserSessionModel userSession = this.session.sessions()
+ .createUserSession(this.realmModel, federatedUser, federatedUser.getUsername(), this.clientConnection.getRemoteAddr(), "broker", false);
+
+ this.event.user(federatedUser);
+ this.event.session(userSession);
+
+ TokenManager.attachClientSession(userSession, clientSession);
+
+ if (isDebugEnabled()) {
+ LOGGER.debugf("Performing local authentication for user [%s].", federatedUser);
+ }
+
+ return AuthenticationManager.nextActionAfterAuthentication(this.session, userSession, clientSession, this.clientConnection, this.request,
+ this.uriInfo, event);
+ }
+
+ private Response performAccountLinking(ClientSessionModel clientSession, String providerId, FederatedIdentityModel federatedIdentityModel, UserModel federatedUser) {
+ this.event.event(EventType.IDENTITY_PROVIDER_ACCCOUNT_LINKING);
+
+ if (federatedUser != null) {
+ return redirectToErrorPage("The identity returned by the identity provider [" + providerId + "] is already linked to other user.");
+ }
+
+ UserModel authenticatedUser = clientSession.getUserSession().getUser();
+
+ if (isDebugEnabled()) {
+ LOGGER.debugf("Linking account [%s] from identity provider [%s] to user [%s].", federatedIdentityModel, providerId, authenticatedUser);
+ }
+
+ if (!authenticatedUser.isEnabled()) {
+ fireErrorEvent(Errors.USER_DISABLED);
+ return redirectToErrorPage("User is disabled.");
+ }
+
+ if (!authenticatedUser.hasRole(this.realmModel.getApplicationByName(ACCOUNT_MANAGEMENT_APP).getRole(MANAGE_ACCOUNT))) {
+ fireErrorEvent(Errors.NOT_ALLOWED);
+ return redirectToErrorPage("Insufficient permissions to link identities.");
+ }
+
+ this.session.users().addFederatedIdentity(this.realmModel, authenticatedUser, federatedIdentityModel);
+
+ this.event.success();
+
+ return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build();
+ }
+
+ private void updateFederatedIdentity(FederatedIdentity updatedIdentity, UserModel federatedUser) {
+ FederatedIdentityModel federatedIdentityModel = this.session.users().getFederatedIdentity(federatedUser, updatedIdentity.getIdentityProviderId(), this.realmModel);
+
+ federatedIdentityModel.setToken(updatedIdentity.getToken());
+
+ this.session.users().updateFederatedIdentity(this.realmModel, federatedUser, federatedIdentityModel);
+
+ if (isDebugEnabled()) {
+ LOGGER.debugf("Identity [%s] update with response from identity provider [%s].", federatedUser, updatedIdentity.getIdentityProviderId());
+ }
+ }
+
+ private ClientSessionCode parseClientSessionCode(String code, String providerId) {
+ ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel);
+
+ if (clientCode != null && clientCode.isValid(AUTHENTICATE)) {
+ validateClientPermissions(clientCode, providerId);
+ ClientSessionModel clientSession = clientCode.getClientSession();
+
+ if (clientSession != null) {
+ ClientModel client = clientSession.getClient();
+
+ if (client != null) {
+ LOGGER.debugf("Got authorization code from client [%s].", client.getClientId());
+ this.event.client(client);
+ }
+
+ if (clientSession.getUserSession() != null) {
+ this.event.session(clientSession.getUserSession());
+ }
+ }
+
+ if (isDebugEnabled()) {
+ LOGGER.debugf("Authorization code is valid.");
+ }
+
+ return clientCode;
+ }
+
+ throw new IdentityBrokerException("Invalid code, please login again through your application.");
+ }
+
+ private AuthenticationRequest createAuthenticationRequest(String providerId, ClientSessionCode clientSessionCode) {
+ ClientSessionModel clientSession = null;
+ String relayState = null;
+
+ if (clientSessionCode != null) {
+ clientSession = clientSessionCode.getClientSession();
+ relayState = clientSessionCode.getCode();
+ }
+
+ return new AuthenticationRequest(this.session, this.realmModel, clientSession, this.request, this.uriInfo, relayState, getRedirectUri(providerId));
+ }
+
+ private String getRedirectUri(String providerId) {
+ return Urls.identityProviderAuthnResponse(this.uriInfo.getBaseUri(), providerId, this.realmModel.getName()).toString();
+ }
+
+ private Response redirectToErrorPage(String message) {
+ return redirectToErrorPage(message, null);
+ }
+
+ private Response redirectToErrorPage(String message, Throwable throwable) {
+ fireErrorEvent(message, throwable);
+ return Flows.forwardToSecurityFailurePage(this.session, this.realmModel, this.uriInfo, message);
+ }
+
+ private Response badRequest(String message) {
+ fireErrorEvent(message);
+ return Flows.errors().error(message, Status.BAD_REQUEST);
+ }
+
+ private Response redirectToLoginPage(String message, ClientSessionCode clientCode) {
+ fireErrorEvent(message);
+ return Flows.forms(this.session, this.realmModel, clientCode.getClientSession().getClient(), this.uriInfo)
+ .setClientSessionCode(clientCode.getCode())
+ .setError(message)
+ .createLogin();
+ }
+
+ private IdentityProvider getIdentityProvider(String providerId) {
+ IdentityProviderModel identityProviderModel = this.realmModel.getIdentityProviderById(providerId);
+
+ if (identityProviderModel != null) {
+ IdentityProviderFactory providerFactory = getIdentityProviderFactory(identityProviderModel);
+
+ if (providerFactory == null) {
+ throw new IdentityBrokerException("Could not find factory for identity provider [" + providerId + "].");
+ }
+
+ return providerFactory.create(identityProviderModel);
+ }
+
+ throw new IdentityBrokerException("Identity Provider [" + providerId + "] not found.");
+ }
+
+ private IdentityProviderFactory getIdentityProviderFactory(IdentityProviderModel model) {
+ Map<String, IdentityProviderFactory> availableProviders = new HashMap<String, IdentityProviderFactory>();
+ List<ProviderFactory> allProviders = new ArrayList<ProviderFactory>();
+
+ allProviders.addAll(this.session.getKeycloakSessionFactory().getProviderFactories(IdentityProvider.class));
+ allProviders.addAll(this.session.getKeycloakSessionFactory().getProviderFactories(SocialIdentityProvider.class));
+
+ for (ProviderFactory providerFactory : allProviders) {
+ availableProviders.put(providerFactory.getId(), (IdentityProviderFactory) providerFactory);
+ }
+
+ return availableProviders.get(model.getProviderId());
+ }
+
+ private IdentityProviderModel getIdentityProviderConfig(String providerId) {
+ for (IdentityProviderModel model : this.realmModel.getIdentityProviders()) {
+ if (model.getId().equals(providerId)) {
+ return model;
+ }
+ }
+
+ throw new IdentityBrokerException("Configuration for identity provider [" + providerId + "] not found.");
+ }
+
+ private void validateClientPermissions(ClientSessionCode clientSessionCode, String providerId) {
+ ClientSessionModel clientSession = clientSessionCode.getClientSession();
+ ClientModel clientModel = clientSession.getClient();
+
+ if (clientModel == null) {
+ throw new IdentityBrokerException("Invalid client.");
+ }
+
+ if (!clientModel.hasIdentityProvider(providerId)) {
+ throw new IdentityBrokerException("Client [" + clientModel.getClientId() + "] not authorized to authenticate with identity provider [" + providerId + "].");
+ }
+ }
+
+ private UserModel createUser(FederatedIdentity updatedIdentity) {
+ FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(updatedIdentity.getIdentityProviderId(), updatedIdentity.getId(),
+ updatedIdentity.getUsername(), updatedIdentity.getToken());
+ // Check if no user already exists with this username or email
+ UserModel existingUser = this.session.users().getUserByEmail(updatedIdentity.getEmail(), this.realmModel);
+
+ if (existingUser != null) {
+ fireErrorEvent(Errors.FEDERATED_IDENTITY_EMAIL_EXISTS);
+ throw new IdentityBrokerException("federatedIdentityEmailExists");
+ }
+
+ existingUser = this.session.users().getUserByUsername(updatedIdentity.getUsername(), this.realmModel);
+
+ if (existingUser != null) {
+ fireErrorEvent(Errors.FEDERATED_IDENTITY_USERNAME_EXISTS);
+ throw new IdentityBrokerException("federatedIdentityUsernameExists");
+ }
+
+ // Check if realm registration is allowed
+ if (!this.realmModel.isRegistrationAllowed()) {
+ fireErrorEvent(Errors.FEDERATED_IDENTITY_DISABLED_REGISTRATION);
+ throw new IdentityBrokerException("federatedIdentityDisabledRegistration");
+ }
+
+ if (isDebugEnabled()) {
+ LOGGER.debugf("Creating account from identity [%s].", federatedIdentityModel);
+ }
+
+ UserModel federatedUser = this.session.users().addUser(this.realmModel, updatedIdentity.getUsername());
+
+ if (isDebugEnabled()) {
+ LOGGER.debugf("Account [%s] created.", federatedUser);
+ }
+
+ federatedUser.setEnabled(true);
+ federatedUser.setFirstName(updatedIdentity.getFirstName());
+ federatedUser.setLastName(updatedIdentity.getLastName());
+ federatedUser.setEmail(updatedIdentity.getEmail());
+
+ this.session.users().addFederatedIdentity(this.realmModel, federatedUser, federatedIdentityModel);
+
+ this.event.clone().user(federatedUser).event(EventType.REGISTER)
+ .detail(Details.IDENTITY_PROVIDER, federatedIdentityModel.getIdentityProvider())
+ .detail(Details.IDENTITY_PROVIDER_IDENTITY, updatedIdentity.getUsername())
+ .removeDetail("auth_method")
+ .success();
+
+ return federatedUser;
+ }
+
+ private Response corsResponse(Response response, ClientModel clientModel) {
+ return Cors.add(this.request, Response.fromResponse(response)).auth().allowedOrigins(clientModel).build();
+ }
+
+ private void fireErrorEvent(String message, Throwable throwable) {
+ if (!this.event.getEvent().getType().toString().endsWith("_ERROR")) {
+ boolean newTransaction = !this.session.getTransaction().isActive();
+
+ try {
+ if (newTransaction) {
+ this.session.getTransaction().begin();
+ }
+
+ this.event.error(message);
+
+ if (newTransaction) {
+ this.session.getTransaction().commit();
+ }
+ } catch (Exception e) {
+ LOGGER.error("Could not fire event.", e);
+ rollback();
+ }
+ }
+
+ if (throwable != null) {
+ LOGGER.error(message, throwable);
+ } else {
+ LOGGER.error(message);
+ }
+ }
+
+ private void fireErrorEvent(String message) {
+ fireErrorEvent(message, null);
+ }
+
+ private boolean isDebugEnabled() {
+ return LOGGER.isDebugEnabled();
+ }
+
+ private void rollback() {
+ if (this.session.getTransaction().isActive()) {
+ this.session.getTransaction().rollback();
+ }
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index f3044ea..11b59c8 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -72,7 +72,6 @@ public class KeycloakApplication extends Application {
singletons.add(new ServerVersionResource());
singletons.add(new RealmsResource());
- singletons.add(new AuthenticationBrokerResource());
singletons.add(new AdminRoot());
classes.add(SkeletonKeyContextResolver.class);
classes.add(QRCodeResource.class);
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index a7fe6c4..de6620c 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -1,14 +1,11 @@
package org.keycloak.services.resources;
import org.jboss.logging.Logger;
-import org.jboss.resteasy.spi.BadRequestException;
import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.ClientConnection;
-import org.keycloak.Config;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ApplicationModel;
-import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@@ -20,22 +17,18 @@ import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.managers.EventsManager;
import org.keycloak.services.managers.RealmManager;
-import org.keycloak.util.StreamUtil;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
-import java.io.IOException;
-import java.io.InputStream;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -190,7 +183,18 @@ public class RealmsResource {
return realmResource;
}
+ @Path("{realm}/broker")
+ public IdentityBrokerService getBrokerService(final @PathParam("realm") String name) {
+ RealmManager realmManager = new RealmManager(session);
+ RealmModel realm = locateRealm(name, realmManager);
+ IdentityBrokerService brokerService = new IdentityBrokerService(realm);
+ ResteasyProviderFactory.getInstance().injectProperties(brokerService);
+
+ brokerService.init();
+
+ return brokerService;
+ }
}
diff --git a/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java
index 5266c68..8ebea50 100755
--- a/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java
+++ b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java
@@ -5,6 +5,7 @@ import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
import org.keycloak.broker.oidc.util.SimpleHttp;
import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.social.SocialIdentityProvider;
/**
@@ -61,7 +62,7 @@ public class FacebookIdentityProvider extends AbstractOAuth2IdentityProvider imp
return user;
} catch (Exception e) {
- throw new RuntimeException(e);
+ throw new IdentityBrokerException("Could not obtain user profile from facebook.", e);
}
}
diff --git a/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java b/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
index 7be16dc..df6a4d0 100755
--- a/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
+++ b/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
@@ -5,6 +5,7 @@ import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
import org.keycloak.broker.oidc.util.SimpleHttp;
import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.social.SocialIdentityProvider;
/**
@@ -37,7 +38,7 @@ public class GitHubIdentityProvider extends AbstractOAuth2IdentityProvider imple
return user;
} catch (Exception e) {
- throw new RuntimeException(e);
+ throw new IdentityBrokerException("Could not obtain user profile from github.", e);
}
}
diff --git a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java
index d9ee567..c7d3492 100755
--- a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java
+++ b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProvider.java
@@ -26,6 +26,7 @@ import org.keycloak.broker.provider.AbstractIdentityProvider;
import org.keycloak.broker.provider.AuthenticationRequest;
import org.keycloak.broker.provider.AuthenticationResponse;
import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.social.SocialIdentityProvider;
@@ -68,7 +69,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
return AuthenticationResponse.temporaryRedirect(authenticationUrl);
} catch (Exception e) {
- throw new RuntimeException(e);
+ throw new IdentityBrokerException("Could send authentication request to twitter.", e);
}
}
@@ -83,7 +84,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
MultivaluedMap<String, String> queryParameters = request.getUriInfo().getQueryParameters();
if (queryParameters.getFirst("denied") != null) {
- throw new RuntimeException("Access denied.");
+ throw new IdentityBrokerException("Access denied.");
}
try {
@@ -121,7 +122,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
return AuthenticationResponse.end(identity);
} catch (Exception e) {
- throw new RuntimeException(e);
+ throw new IdentityBrokerException("Could get user profile from twitter.", e);
}
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
index 1c912d3..edd0812 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
@@ -30,6 +30,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.IDToken;
+import org.keycloak.services.resources.flows.Urls;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
import org.keycloak.testsuite.broker.util.UserSessionStatusServlet.UserSessionStatus;
@@ -53,8 +54,10 @@ import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
+import java.net.URI;
import java.util.List;
import java.util.Set;
@@ -69,6 +72,8 @@ import static org.junit.Assert.assertTrue;
*/
public abstract class AbstractIdentityProviderTest {
+ private static final URI BASE_URI = UriBuilder.fromUri("http://localhost:8081/auth").build();
+
@ClassRule
public static BrokerKeyCloakRule brokerServerRule = new BrokerKeyCloakRule();
@@ -308,7 +313,7 @@ public abstract class AbstractIdentityProviderTest {
UserSessionStatus userSessionStatus = retrieveSessionStatus();
String accessToken = userSessionStatus.getAccessTokenString();
- String tokenEndpointUrl = "http://localhost:8081/auth/broker/realm-with-broker/" + getProviderId() + "/token";
+ URI tokenEndpointUrl = Urls.identityProviderRetrieveToken(BASE_URI, getProviderId(), realm.getName());
final String authHeader = "Bearer " + accessToken;
ClientRequestFilter authFilter = new ClientRequestFilter() {
@Override
@@ -317,11 +322,10 @@ public abstract class AbstractIdentityProviderTest {
}
};
Client client = ClientBuilder.newBuilder().register(authFilter).build();
- UriBuilder authBase = UriBuilder.fromUri(tokenEndpointUrl);
- WebTarget tokenEndpoint = client.target(authBase);
+ WebTarget tokenEndpoint = client.target(tokenEndpointUrl);
Response response = tokenEndpoint.request().get();
- assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+ assertEquals(Status.OK.getStatusCode(), response.getStatus());
assertNotNull(response.readEntity(String.class));
driver.navigate().to("http://localhost:8081/test-app/logout");
@@ -364,14 +368,15 @@ public abstract class AbstractIdentityProviderTest {
grantPage.accept();
assertTrue(oauth.getCurrentQuery().containsKey(OAuth2Constants.CODE));
+
AccessTokenResponse accessToken = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get(OAuth2Constants.CODE), "password");
- String tokenEndpointUrl = "http://localhost:8081/auth/broker/realm-with-broker/" + getProviderId() + "/token";
+ URI tokenEndpointUrl = Urls.identityProviderRetrieveToken(BASE_URI, getProviderId(), getRealm().getName());
String authHeader = "Bearer " + accessToken.getAccessToken();
HtmlUnitDriver htmlUnitDriver = (WebRule.HtmlUnitDriver) this.driver;
htmlUnitDriver.getWebClient().addRequestHeader(HttpHeaders.AUTHORIZATION, authHeader);
- htmlUnitDriver.navigate().to(tokenEndpointUrl);
+ htmlUnitDriver.navigate().to(tokenEndpointUrl.toString());
grantPage.assertCurrent();
grantPage.accept();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java
index 8ac0ee7..9b9d723 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java
@@ -80,6 +80,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
identityProviderModel.setEnabled(false);
identityProviderModel.setUpdateProfileFirstLogin(false);
identityProviderModel.setStoreToken(true);
+ identityProviderModel.setAuthenticateByDefault(true);
realm.updateIdentityProvider(identityProviderModel);
@@ -94,11 +95,13 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
assertFalse(identityProviderModel.isEnabled());
assertFalse(identityProviderModel.isUpdateProfileFirstLogin());
assertTrue(identityProviderModel.isStoreToken());
+ assertTrue(identityProviderModel.isAuthenticateByDefault());
identityProviderModel.setName("Changed Name Again");
identityProviderModel.getConfig().remove("config-added");
identityProviderModel.setEnabled(true);
identityProviderModel.setUpdateProfileFirstLogin(true);
+ identityProviderModel.setAuthenticateByDefault(false);
realm.updateIdentityProvider(identityProviderModel);
@@ -109,8 +112,9 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
assertEquals("Changed Name Again", identityProviderModel.getName());
assertFalse(identityProviderModel.getConfig().containsKey("config-added"));
- assertEquals(true, identityProviderModel.isEnabled());
- assertEquals(true, identityProviderModel.isUpdateProfileFirstLogin());
+ assertTrue(identityProviderModel.isEnabled());
+ assertTrue(identityProviderModel.isUpdateProfileFirstLogin());
+ assertFalse(identityProviderModel.isAuthenticateByDefault());
}
@Test
@@ -175,6 +179,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
assertEquals("Google", config.getName());
assertEquals(true, config.isEnabled());
assertEquals(true, config.isUpdateProfileFirstLogin());
+ assertEquals(false, config.isAuthenticateByDefault());
+ assertEquals(true, config.isStoreToken());
assertEquals("clientId", config.getClientId());
assertEquals("clientSecret", config.getClientSecret());
assertEquals(GoogleIdentityProvider.AUTH_URL, config.getAuthorizationUrl());
@@ -192,6 +198,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
assertEquals("SAML Signed IdP", config.getName());
assertEquals(true, config.isEnabled());
assertEquals(true, config.isUpdateProfileFirstLogin());
+ assertEquals(false, config.isAuthenticateByDefault());
+ assertEquals(false, config.isStoreToken());
assertEquals("http://localhost:8082/auth/realms/realm-with-saml-identity-provider/protocol/saml", config.getSingleSignOnServiceUrl());
assertEquals("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", config.getNameIDPolicyFormat());
assertEquals("MIIDdzCCAl+gAwIBAgIEbySuqTANBgkqhkiG9w0BAQsFADBsMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3duMB4XDTE1MDEyODIyMTYyMFoXDTE3MTAyNDIyMTYyMFowbDEQMA4GA1UEBhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAII/K9NNvXi9IySl7+l2zY/kKrGTtuR4WdCI0xLW/Jn4dLY7v1/HOnV4CC4ecFOzhdNFPtJkmEhP/q62CpmOYOKApXk3tfmm2rwEz9bWprVxgFGKnbrWlz61Z/cjLAlhD3IUj2ZRBquYgSXQPsYfXo1JmSWF5pZ9uh1FVqu9f4wvRqY20ZhUN+39F+1iaBsoqsrbXypCn1HgZkW1/9D9GZug1c3vB4wg1TwZZWRNGtxwoEhdK6dPrNcZ+6PdanVilWrbQFbBjY4wz8/7IMBzssoQ7Usmo8F1Piv0FGfaVeJqBrcAvbiBMpk8pT+27u6p8VyIX6LhGvnxIwM07NByeSUCAwEAAaMhMB8wHQYDVR0OBBYEFFlcNuTYwI9W0tQ224K1gFJlMam0MA0GCSqGSIb3DQEBCwUAA4IBAQB5snl1KWOJALtAjLqD0mLPg1iElmZP82Lq1htLBt3XagwzU9CaeVeCQ7lTp+DXWzPa9nCLhsC3QyrV3/+oqNli8C6NpeqI8FqN2yQW/QMWN1m5jWDbmrWwtQzRUn/rh5KEb5m3zPB+tOC6e/2bV3QeQebxeW7lVMD0tSCviUg1MQf1l2gzuXQo60411YwqrXwk6GMkDOhFDQKDlMchO3oRbQkGbcP8UeiKAXjMeHfzbiBr+cWz8NYZEtxUEDYDjTpKrYCSMJBXpmgVJCZ00BswbksxJwaGqGMPpUKmCV671pf3m8nq3xyiHMDGuGwtbU+GE8kVx85menmp8+964nin", config.getSigningCertificate());
@@ -211,6 +219,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
assertEquals("OIDC IdP", config.getName());
assertEquals(false, config.isEnabled());
assertEquals(false, config.isUpdateProfileFirstLogin());
+ assertEquals(false, config.isAuthenticateByDefault());
+ assertEquals(false, config.isStoreToken());
assertEquals("clientId", config.getClientId());
assertEquals("clientSecret", config.getClientSecret());
}
@@ -224,6 +234,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
assertEquals("Facebook", config.getName());
assertEquals(true, config.isEnabled());
assertEquals(true, config.isUpdateProfileFirstLogin());
+ assertEquals(false, config.isAuthenticateByDefault());
+ assertEquals(false, config.isStoreToken());
assertEquals("clientId", config.getClientId());
assertEquals("clientSecret", config.getClientSecret());
assertEquals(FacebookIdentityProvider.AUTH_URL, config.getAuthorizationUrl());
@@ -240,6 +252,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
assertEquals("GitHub", config.getName());
assertEquals(true, config.isEnabled());
assertEquals(true, config.isUpdateProfileFirstLogin());
+ assertEquals(false, config.isAuthenticateByDefault());
+ assertEquals(false, config.isStoreToken());
assertEquals("clientId", config.getClientId());
assertEquals("clientSecret", config.getClientSecret());
assertEquals(GitHubIdentityProvider.AUTH_URL, config.getAuthorizationUrl());
@@ -256,6 +270,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
assertEquals("Twitter", config.getName());
assertEquals(true, config.isEnabled());
assertEquals(true, config.isUpdateProfileFirstLogin());
+ assertEquals(false, config.isAuthenticateByDefault());
+ assertEquals(true, config.isStoreToken());
assertEquals("clientId", config.getClientId());
assertEquals("clientSecret", config.getClientSecret());
}
@@ -269,6 +285,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
assertEquals("Kerberos", config.getName());
assertEquals(true, config.isEnabled());
assertEquals(true, config.isUpdateProfileFirstLogin());
+ assertEquals(false, config.isAuthenticateByDefault());
assertEquals("HTTP/server.domain.org@DOMAIN.ORG", config.getServerPrincipal());
assertEquals("/etc/http.keytab", config.getKeyTab());
assertTrue(config.getDebug());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
index eb71e08..a1ef54c 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
@@ -286,7 +286,7 @@ public class LoginTest {
loginPage.login("login@test.com", "password");
loginPage.assertCurrent();
- Assert.assertEquals("Login timeout or unknown action. Please login again", loginPage.getError());
+ Assert.assertEquals("Login timeout. Please login again", loginPage.getError());
events.expectLogin().user((String) null).session((String) null).error("expired_code").clearDetails().assertEvent();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
index d308c3e..580ba2a 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
@@ -164,7 +164,7 @@ public class LoginTotpTest {
loginTotpPage.login(totp.generate("totpSecret"));
loginPage.assertCurrent();
- Assert.assertEquals("Login timeout or unknown action. Please login again", loginPage.getError());
+ Assert.assertEquals("Login timeout. Please login again", loginPage.getError());
AssertEvents.ExpectedEvent expectedEvent = events.expectLogin().error("expired_code")
.user((String)null)
diff --git a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json
index d7831a7..dbed987 100755
--- a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json
+++ b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-kc-oidc.json
@@ -12,7 +12,7 @@
"enabled": true,
"secret": "secret",
"redirectUris": [
- "http://localhost:8081/auth/broker/realm-with-broker/kc-oidc-idp"
+ "http://localhost:8081/auth/realms/realm-with-broker/broker/kc-oidc-idp"
],
"claims": {
"name" : true,
diff --git a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml.json b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml.json
index 4bf96ff..1972224 100755
--- a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml.json
+++ b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml.json
@@ -11,7 +11,7 @@
"name": "http://localhost:8081/auth/",
"enabled": true,
"redirectUris": [
- "http://localhost:8081/auth/broker/realm-with-broker/kc-saml-idp-basic"
+ "http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic"
],
"attributes": {
"saml.authnstatement": "true"
diff --git a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml-with-signature.json b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml-with-signature.json
index 82db4ea..3d22c42 100755
--- a/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml-with-signature.json
+++ b/testsuite/integration/src/test/resources/broker-test/test-broker-realm-with-saml-with-signature.json
@@ -11,7 +11,7 @@
"name": "http://localhost:8081/auth/",
"enabled": true,
"redirectUris": [
- "http://localhost:8081/auth/broker/realm-with-broker/kc-saml-signed-idp"
+ "http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-signed-idp"
],
"attributes": {
"saml.assertion.signature": "true",
diff --git a/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json b/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json
index 182b131..bc62fa8 100755
--- a/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json
+++ b/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json
@@ -15,6 +15,7 @@
"name" : "Google",
"enabled": true,
"updateProfileFirstLogin" : "true",
+ "storeToken": "true",
"config": {
"clientId": "clientId",
"clientSecret": "clientSecret"
@@ -40,6 +41,7 @@
"name" : "GitHub",
"enabled": true,
"updateProfileFirstLogin" : "true",
+ "storeToken": "false",
"config": {
"authorizationUrl": "authorizationUrl",
"tokenUrl": "tokenUrl",
@@ -54,6 +56,7 @@
"name" : "Twitter",
"enabled": true,
"updateProfileFirstLogin" : "true",
+ "storeToken": true,
"config": {
"authorizationUrl": "authorizationUrl",
"tokenUrl": "tokenUrl",
@@ -116,6 +119,7 @@
"name" : "OIDC IdP",
"enabled": false,
"updateProfileFirstLogin" : "false",
+ "authenticateByDefault" : "false",
"config": {
"clientId": "clientId",
"clientSecret": "clientSecret",
@@ -148,6 +152,7 @@
"name" : "Kerberos",
"enabled": true,
"updateProfileFirstLogin" : "true",
+ "authenticateByDefault" : "false",
"config": {
"serverPrincipal": "HTTP/server.domain.org@DOMAIN.ORG",
"keyTab": "/etc/http.keytab",