diff --git a/core/src/main/java/org/keycloak/representations/idm/SocialLinkRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/SocialLinkRepresentation.java index a6e183890a..8203261fed 100644 --- a/core/src/main/java/org/keycloak/representations/idm/SocialLinkRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/SocialLinkRepresentation.java @@ -6,6 +6,7 @@ package org.keycloak.representations.idm; public class SocialLinkRepresentation { protected String socialProvider; + protected String socialUserId; protected String socialUsername; public String getSocialProvider() { @@ -16,6 +17,14 @@ public class SocialLinkRepresentation { this.socialProvider = socialProvider; } + public String getSocialUserId() { + return socialUserId; + } + + public void setSocialUserId(String socialUserId) { + this.socialUserId = socialUserId; + } + public String getSocialUsername() { return socialUsername; } diff --git a/core/src/main/java/org/keycloak/representations/idm/SocialMappingRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/SocialMappingRepresentation.java index 57dd874a8c..c4d852c00b 100644 --- a/core/src/main/java/org/keycloak/representations/idm/SocialMappingRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/SocialMappingRepresentation.java @@ -32,9 +32,10 @@ public class SocialMappingRepresentation { return socialLinks; } - public SocialLinkRepresentation socialLink(String socialProvider, String socialUsername) { + public SocialLinkRepresentation socialLink(String socialProvider, String socialUserId, String socialUsername) { SocialLinkRepresentation link = new SocialLinkRepresentation(); link.setSocialProvider(socialProvider); + link.setSocialUserId(socialUserId); link.setSocialUsername(socialUsername); if (socialLinks == null) socialLinks = new ArrayList(); socialLinks.add(link); diff --git a/docbook/reference/en/en-US/modules/social-facebook.xml b/docbook/reference/en/en-US/modules/social-facebook.xml index bc87171234..6e5e832db8 100644 --- a/docbook/reference/en/en-US/modules/social-facebook.xml +++ b/docbook/reference/en/en-US/modules/social-facebook.xml @@ -40,10 +40,4 @@ - - - Facebook doesn't allow localhost in the redirect URI. To test on a local server - replace localhost with 127.0.0.1. - - \ No newline at end of file diff --git a/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html b/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html index 36ef79f38f..ee38261cb5 100755 --- a/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html +++ b/examples/demo-template/customer-app-js/src/main/webapp/customers/view.html @@ -1,25 +1,52 @@ Customer View Page - +

Goto: logout

-User made this request. +User made this request. +

User details (from )

+

Username:

+

Email:

+

Full Name:

+

First:

+

Last:

+

Customer Listing

diff --git a/examples/test-cordova.json b/examples/test-cordova.json new file mode 100644 index 0000000000..9de2ee35b8 --- /dev/null +++ b/examples/test-cordova.json @@ -0,0 +1,27 @@ +{ + "id": "test", + "realm": "test", + "enabled": true, + "sslNotRequired": true, + "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", + "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", + "requiredCredentials": [ "password" ], + "users" : [ + { + "username" : "test", + "enabled": true, + "email" : "test-user@localhost", + "credentials" : [ + { "type" : "password", + "value" : "test" } + ] + } + ], + "applications": [ + { + "name": "test", + "enabled": true, + "secret": "password" + } + ] +} \ No newline at end of file diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java index ed94d6980b..2fbe8c6a12 100644 --- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java +++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountSocialBean.java @@ -33,22 +33,22 @@ public class AccountSocialBean { for (SocialProvider provider : SocialLoader.load()) { String socialProviderId = provider.getId(); if (socialConfig.containsKey(socialProviderId + ".key")) { - String socialUsername = getSocialUsername(userSocialLinks, socialProviderId); + SocialLinkModel socialLink = getSocialLink(userSocialLinks, socialProviderId); - String action = socialUsername!=null ? "remove" : "add"; + String action = socialLink != null ? "remove" : "add"; String actionUrl = UriBuilder.fromUri(accountSocialUpdateUri).queryParam("action", action).queryParam("provider_id", socialProviderId).build().toString(); - SocialLinkEntry entry = new SocialLinkEntry(socialProviderId, provider.getName(), socialUsername, actionUrl); + SocialLinkEntry entry = new SocialLinkEntry(socialLink, provider.getName(), actionUrl); this.socialLinks.add(entry); } } } } - private String getSocialUsername(Set userSocialLinks, String socialProviderId) { + private SocialLinkModel getSocialLink(Set userSocialLinks, String socialProviderId) { for (SocialLinkModel link : userSocialLinks) { if (socialProviderId.equals(link.getSocialProvider())) { - return link.getSocialUsername(); + return link; } } return null; @@ -60,32 +60,34 @@ public class AccountSocialBean { public class SocialLinkEntry { - private final String providerId; + private SocialLinkModel link; private final String providerName; - private final String socialUsername; private final String actionUrl; - public SocialLinkEntry(String providerId, String providerName, String socialUsername, String actionUrl) { - this.providerId = providerId; + public SocialLinkEntry(SocialLinkModel link, String providerName, String actionUrl) { + this.link = link; this.providerName = providerName; - this.socialUsername = socialUsername!=null ? socialUsername : ""; this.actionUrl = actionUrl; } public String getProviderId() { - return providerId; + return link != null ? link.getSocialProvider() : null; } public String getProviderName() { return providerName; } + public String getSocialUserId() { + return link != null ? link.getSocialUserId() : null; + } + public String getSocialUsername() { - return socialUsername; + return link != null ? link.getSocialUsername() : null; } public boolean isConnected() { - return !socialUsername.isEmpty(); + return link != null; } public String getActionUrl() { diff --git a/forms/common-themes/src/main/resources/theme/account/base/social.ftl b/forms/common-themes/src/main/resources/theme/account/base/social.ftl index a1941de571..e238248eef 100644 --- a/forms/common-themes/src/main/resources/theme/account/base/social.ftl +++ b/forms/common-themes/src/main/resources/theme/account/base/social.ftl @@ -11,16 +11,16 @@ <#list social.links as socialLink>
- +
- +
diff --git a/forms/login-api/src/main/java/org/keycloak/login/LoginForms.java b/forms/login-api/src/main/java/org/keycloak/login/LoginForms.java index 2eb370418e..a02df450b9 100755 --- a/forms/login-api/src/main/java/org/keycloak/login/LoginForms.java +++ b/forms/login-api/src/main/java/org/keycloak/login/LoginForms.java @@ -43,6 +43,8 @@ public interface LoginForms { public LoginForms setClient(ClientModel client); + public LoginForms setQueryParams(MultivaluedMap queryParams); + public LoginForms setFormData(MultivaluedMap formData); public LoginForms setStatus(Response.Status status); diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java index 69e17fcfd7..a91aaec3b6 100755 --- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java +++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginForms.java @@ -51,6 +51,7 @@ public class FreeMarkerLoginForms implements LoginForms { private Response.Status status = Response.Status.OK; private List realmRolesRequested; private MultivaluedMap resourceRolesRequested; + private MultivaluedMap queryParams; public static enum MessageType {SUCCESS, WARNING, ERROR} @@ -114,7 +115,7 @@ public class FreeMarkerLoginForms implements LoginForms { } private Response createResponse(LoginFormsPages page) { - MultivaluedMap queryParameterMap = uriInfo.getQueryParameters(); + MultivaluedMap queryParameterMap = queryParams != null ? queryParams : uriInfo.getQueryParameters(); String requestURI = uriInfo.getBaseUri().getPath(); UriBuilder uriBuilder = UriBuilder.fromUri(requestURI); @@ -276,4 +277,9 @@ public class FreeMarkerLoginForms implements LoginForms { return this; } + @Override + public LoginForms setQueryParams(MultivaluedMap queryParams) { + this.queryParams = queryParams; + return this; + } } diff --git a/integration/js/src/main/resources/META-INF/resources/js/keycloak.js b/integration/js/src/main/resources/META-INF/resources/js/keycloak.js index 525366372d..b3a6af4871 100755 --- a/integration/js/src/main/resources/META-INF/resources/js/keycloak.js +++ b/integration/js/src/main/resources/META-INF/resources/js/keycloak.js @@ -194,13 +194,22 @@ var Keycloak = function (options) { if (token) { window.oauth.token = token; kc.token = token; - kc.tokenParsed = JSON.parse(atob(token.split('.')[1])); kc.authenticated = true; kc.subject = kc.tokenParsed.sub; kc.realmAccess = kc.tokenParsed.realm_access; kc.resourceAccess = kc.tokenParsed.resource_access; + for (var i = 0; i < idTokenProperties.length; i++) { + var n = idTokenProperties[i]; + if (kc.tokenParsed[n]) { + if (!kc.idToken) { + kc.idToken = {}; + } + kc.idToken[n] = kc.tokenParsed[n]; + } + } + setTimeout(function() { successCallback && successCallback({ authenticated: kc.authenticated, subject: kc.subject }); }, 0); @@ -260,6 +269,35 @@ var Keycloak = function (options) { var uuid = s.join(''); return uuid; } + + var idTokenProperties = [ + "name", + "given_name", + "family_name", + "middle_name", + "nickname", + "preferred_username", + "profile", + "picture", + "website", + "email", + "email_verified", + "gender", + "birthdate", + "zoneinfo", + "locale", + "phone_number", + "phone_number_verified", + "address", + "updated_at", + "formatted", + "street_address", + "locality", + "region", + "postal_code", + "country", + "claims_locales" + ] } window.oauth = (function () { diff --git a/integration/js/src/main/resources/META-INF/resources/js/test.html b/integration/js/src/main/resources/META-INF/resources/js/test.html new file mode 100644 index 0000000000..fb20e03c79 --- /dev/null +++ b/integration/js/src/main/resources/META-INF/resources/js/test.html @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/keycloak.json b/keycloak.json new file mode 100644 index 0000000000..35e49cde4e --- /dev/null +++ b/keycloak.json @@ -0,0 +1,8 @@ +{ + "realm" : "test", + "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDddHi8/MoYNtdafydQ+e4P0qrvClCW0o/x9fAbZ09dET/VBZCU28M54LmgJT7snXfreyYBpaQpHkW52/tyuEcJ5KO28LIcvObDQFDq3Z7esrovHl4NRETJBF9Xqwt+XLTZF6m37fYzaUK6MVJzUHP9qmu90LYyyvQ+hBJD0GSw1QIDAQAB", + "auth-server-url" : "http://localhost:8081/auth", + "ssl-not-required" : true, + "resource" : "test", + "public-client" : true +} \ No newline at end of file diff --git a/model/api/src/main/java/org/keycloak/models/SocialLinkModel.java b/model/api/src/main/java/org/keycloak/models/SocialLinkModel.java index 742da1112e..76e892910a 100755 --- a/model/api/src/main/java/org/keycloak/models/SocialLinkModel.java +++ b/model/api/src/main/java/org/keycloak/models/SocialLinkModel.java @@ -5,11 +5,29 @@ package org.keycloak.models; */ public class SocialLinkModel { - private String socialUsername; + private String socialUserId; private String socialProvider; + private String socialUsername; - public SocialLinkModel(String socialProvider, String socialUsername) { + public SocialLinkModel(String socialProvider, String socialUserId, String socialUsername) { + this.socialUserId = socialUserId; + this.socialProvider = socialProvider; this.socialUsername = socialUsername; + } + + public String getSocialUserId() { + return socialUserId; + } + + public void setSocialUserId(String socialUserId) { + this.socialUserId = socialUserId; + } + + public String getSocialProvider() { + return socialProvider; + } + + public void setSocialProvider(String socialProvider) { this.socialProvider = socialProvider; } @@ -20,12 +38,4 @@ public class SocialLinkModel { public void setSocialUsername(String socialUsername) { this.socialUsername = socialUsername; } - - public String getSocialProvider() { - return socialProvider; - } - - public void setSocialProvider(String socialProvider) { - this.socialProvider = socialProvider; - } } 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 986e5657ce..465f71711f 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 @@ -543,13 +543,13 @@ public class RealmAdapter implements RealmModel { TypedQuery query = em.createNamedQuery("findUserByLinkAndRealm", UserEntity.class); query.setParameter("realm", realm); query.setParameter("socialProvider", socialLink.getSocialProvider()); - query.setParameter("socialUsername", socialLink.getSocialUsername()); + query.setParameter("socialUserId", socialLink.getSocialUserId()); List results = query.getResultList(); if (results.isEmpty()) { return null; } else if (results.size() > 1) { throw new IllegalStateException("More results found for socialProvider=" + socialLink.getSocialProvider() + - ", socialUsername=" + socialLink.getSocialUsername() + ", results=" + results); + ", socialUserId=" + socialLink.getSocialUserId() + ", results=" + results); } else { UserEntity user = results.get(0); return new UserAdapter(user); @@ -563,7 +563,7 @@ public class RealmAdapter implements RealmModel { List results = query.getResultList(); Set set = new HashSet(); for (SocialLinkEntity entity : results) { - set.add(new SocialLinkModel(entity.getSocialProvider(), entity.getSocialUsername())); + set.add(new SocialLinkModel(entity.getSocialProvider(), entity.getSocialUserId(), entity.getSocialUsername())); } return set; } @@ -571,7 +571,7 @@ public class RealmAdapter implements RealmModel { @Override public SocialLinkModel getSocialLink(UserModel user, String socialProvider) { SocialLinkEntity entity = findSocialLink(user, socialProvider); - return (entity != null) ? new SocialLinkModel(entity.getSocialProvider(), entity.getSocialUsername()) : null; + return (entity != null) ? new SocialLinkModel(entity.getSocialProvider(), entity.getSocialUserId(), entity.getSocialUsername()) : null; } @Override @@ -579,6 +579,7 @@ public class RealmAdapter implements RealmModel { SocialLinkEntity entity = new SocialLinkEntity(); entity.setRealm(realm); entity.setSocialProvider(socialLink.getSocialProvider()); + entity.setSocialUserId(socialLink.getSocialUserId()); entity.setSocialUsername(socialLink.getSocialUsername()); entity.setUser(((UserAdapter) user).getUser()); em.persist(entity); diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java index 8d53f14535..84db8f6582 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/SocialLinkEntity.java @@ -16,7 +16,7 @@ import org.hibernate.annotations.GenericGenerator; @NamedQueries({ @NamedQuery(name="findSocialLinkByUser", query="select link from SocialLinkEntity link where link.user = :user"), @NamedQuery(name="findSocialLinkByUserAndProvider", query="select link from SocialLinkEntity link where link.user = :user and link.socialProvider = :socialProvider"), - @NamedQuery(name="findUserByLinkAndRealm", query="select link.user from SocialLinkEntity link where link.realm = :realm and link.socialProvider = :socialProvider and link.socialUsername = :socialUsername") + @NamedQuery(name="findUserByLinkAndRealm", query="select link.user from SocialLinkEntity link where link.realm = :realm and link.socialProvider = :socialProvider and link.socialUserId = :socialUserId") }) @Entity public class SocialLinkEntity { @@ -32,6 +32,7 @@ public class SocialLinkEntity { protected RealmEntity realm; protected String socialProvider; + protected String socialUserId; protected String socialUsername; public String getId() { @@ -58,6 +59,14 @@ public class SocialLinkEntity { this.socialProvider = socialProvider; } + public String getSocialUserId() { + return socialUserId; + } + + public void setSocialUserId(String socialUserId) { + this.socialUserId = socialUserId; + } + public String getSocialUsername() { return socialUsername; } 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 d2b5a33dd4..90910f9748 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 @@ -853,7 +853,7 @@ public class RealmAdapter extends AbstractMongoAdapter implements R public UserModel getUserBySocialLink(SocialLinkModel socialLink) { DBObject query = new QueryBuilder() .and("socialLinks.socialProvider").is(socialLink.getSocialProvider()) - .and("socialLinks.socialUsername").is(socialLink.getSocialUsername()) + .and("socialLinks.socialUserId").is(socialLink.getSocialUserId()) .and("realmId").is(getId()) .get(); UserEntity userEntity = getMongoStore().loadSingleEntity(UserEntity.class, query, invocationContext); @@ -871,7 +871,7 @@ public class RealmAdapter extends AbstractMongoAdapter implements R Set result = new HashSet(); for (SocialLinkEntity socialLinkEntity : linkEntities) { - SocialLinkModel model = new SocialLinkModel(socialLinkEntity.getSocialProvider(), socialLinkEntity.getSocialUsername()); + SocialLinkModel model = new SocialLinkModel(socialLinkEntity.getSocialProvider(), socialLinkEntity.getSocialUserId(), socialLinkEntity.getSocialUsername()); result.add(model); } return result; @@ -880,7 +880,7 @@ public class RealmAdapter extends AbstractMongoAdapter implements R @Override public SocialLinkModel getSocialLink(UserModel user, String socialProvider) { SocialLinkEntity socialLinkEntity = findSocialLink(user, socialProvider); - return socialLinkEntity!=null ? new SocialLinkModel(socialLinkEntity.getSocialProvider(), socialLinkEntity.getSocialUsername()) : null; + return socialLinkEntity!=null ? new SocialLinkModel(socialLinkEntity.getSocialProvider(), socialLinkEntity.getSocialUserId(), socialLinkEntity.getSocialUsername()) : null; } @Override @@ -888,6 +888,7 @@ public class RealmAdapter extends AbstractMongoAdapter implements R UserEntity userEntity = ((UserAdapter)user).getUser(); SocialLinkEntity socialLinkEntity = new SocialLinkEntity(); socialLinkEntity.setSocialProvider(socialLink.getSocialProvider()); + socialLinkEntity.setSocialUserId(socialLink.getSocialUserId()); socialLinkEntity.setSocialUsername(socialLink.getSocialUsername()); getMongoStore().pushItemToList(userEntity, "socialLinks", socialLinkEntity, true, invocationContext); diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/SocialLinkEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/SocialLinkEntity.java index 85ae5c0b7b..85afa1158d 100644 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/SocialLinkEntity.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/SocialLinkEntity.java @@ -8,9 +8,19 @@ import org.keycloak.models.mongo.api.MongoField; */ public class SocialLinkEntity implements MongoEntity { + private String socialUserId; private String socialUsername; private String socialProvider; + @MongoField + public String getSocialUserId() { + return socialUserId; + } + + public void setSocialUserId(String socialUserId) { + this.socialUserId = socialUserId; + } + @MongoField public String getSocialUsername() { return socialUsername; @@ -37,9 +47,9 @@ public class SocialLinkEntity implements MongoEntity { SocialLinkEntity that = (SocialLinkEntity) o; if (socialProvider != null && (that.socialProvider == null || !socialProvider.equals(that.socialProvider))) return false; - if (socialUsername != null && (that.socialUsername == null || !socialUsername.equals(that.socialUsername))) return false; + if (socialUserId != null && (that.socialUserId == null || !socialUserId.equals(that.socialUserId))) return false; if (socialProvider == null && that.socialProvider != null)return false; - if (socialUsername == null && that.socialUsername != null) return false; + if (socialUserId == null && that.socialUserId != null) return false; return true; } @@ -47,7 +57,7 @@ public class SocialLinkEntity implements MongoEntity { @Override public int hashCode() { int code = 1; - if (socialUsername != null) { + if (socialUserId != null) { code = code * 13; } if (socialProvider != null) { diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java index d4c1624e27..36d7098e5a 100755 --- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java +++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java @@ -880,14 +880,14 @@ public class RealmAdapter implements RealmModel { public UserModel getUserBySocialLink(SocialLinkModel socialLink) { RelationshipQuery query = getRelationshipManager().createRelationshipQuery(SocialLinkRelationship.class); query.setParameter(SocialLinkRelationship.SOCIAL_PROVIDER, socialLink.getSocialProvider()); - query.setParameter(SocialLinkRelationship.SOCIAL_USERNAME, socialLink.getSocialUsername()); + query.setParameter(SocialLinkRelationship.SOCIAL_USERNAME, socialLink.getSocialUserId()); query.setParameter(SocialLinkRelationship.REALM, realm.getName()); List results = query.getResultList(); if (results.isEmpty()) { return null; } else if (results.size() > 1) { throw new IllegalStateException("More results found for socialProvider=" + socialLink.getSocialProvider() + - ", socialUsername=" + socialLink.getSocialUsername() + ", results=" + results); + ", socialUserId=" + socialLink.getSocialUserId() + ", results=" + results); } else { User user = results.get(0).getUser(); return new UserAdapter(user, getIdm()); @@ -902,7 +902,7 @@ public class RealmAdapter implements RealmModel { Set results = new HashSet(); for (SocialLinkRelationship relationship : plSocialLinks) { - results.add(new SocialLinkModel(relationship.getSocialProvider(), relationship.getSocialUsername())); + results.add(new SocialLinkModel(relationship.getSocialProvider(), relationship.getSocialUserId())); } return results; } @@ -912,7 +912,7 @@ public class RealmAdapter implements RealmModel { SocialLinkRelationship relationship = new SocialLinkRelationship(); relationship.setUser(((UserAdapter)user).getUser()); relationship.setSocialProvider(socialLink.getSocialProvider()); - relationship.setSocialUsername(socialLink.getSocialUsername()); + relationship.setSocialUserId(socialLink.getSocialUserId()); relationship.setRealm(realm.getName()); getRelationshipManager().add(relationship); @@ -923,7 +923,7 @@ public class RealmAdapter implements RealmModel { SocialLinkRelationship relationship = new SocialLinkRelationship(); relationship.setUser(((UserAdapter)user).getUser()); relationship.setSocialProvider(socialLink.getSocialProvider()); - relationship.setSocialUsername(socialLink.getSocialUsername()); + relationship.setSocialUserId(socialLink.getSocialUserId()); relationship.setRealm(realm.getName()); getRelationshipManager().remove(relationship); diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/relationships/SocialLinkRelationship.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/relationships/SocialLinkRelationship.java index 62ec7e343e..12f8d03251 100755 --- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/relationships/SocialLinkRelationship.java +++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/relationships/SocialLinkRelationship.java @@ -20,9 +20,9 @@ public class SocialLinkRelationship extends AbstractAttributedType implements Re private static final long serialVersionUID = 154879L; public static final AttributeParameter SOCIAL_PROVIDER = new AttributeParameter("socialProvider"); - public static final AttributeParameter SOCIAL_USERNAME = new AttributeParameter("socialUsername"); + public static final AttributeParameter SOCIAL_USERID = new AttributeParameter("socialUserId"); - // realm is needed to allow searching as combination socialUsername+socialProvider may not be unique + // realm is needed to allow searching as combination socialUserId+socialProvider may not be unique // (Same user could have mapped same facebook account to username "foo" in "realm1" and to username "bar" in "realm2") public static final AttributeParameter REALM = new AttributeParameter("realm"); @@ -54,12 +54,12 @@ public class SocialLinkRelationship extends AbstractAttributedType implements Re } @AttributeProperty - public String getSocialUsername() { - return (String)getAttribute("socialUsername").getValue(); + public String getSocialUserId() { + return (String)getAttribute("socialUserId").getValue(); } - public void setSocialUsername(String socialProviderUserId) { - setAttribute(new Attribute("socialUsername", socialProviderUserId)); + public void setSocialUserId(String socialUserId) { + setAttribute(new Attribute("socialUserId", socialUserId)); } @AttributeProperty diff --git a/model/tests/src/test/java/org/keycloak/model/test/AdapterTest.java b/model/tests/src/test/java/org/keycloak/model/test/AdapterTest.java index 7a2994543b..9ea7b1e1f6 100755 --- a/model/tests/src/test/java/org/keycloak/model/test/AdapterTest.java +++ b/model/tests/src/test/java/org/keycloak/model/test/AdapterTest.java @@ -156,7 +156,7 @@ public class AdapterTest extends AbstractModelTest { RoleModel appRole = app.addRole("test"); realmModel.grantRole(user, appRole); - SocialLinkModel socialLink = new SocialLinkModel("google", user.getLoginName()); + SocialLinkModel socialLink = new SocialLinkModel("google", "google1", user.getLoginName()); realmModel.addSocialLink(user, socialLink); UserCredentialModel cred = new UserCredentialModel(); diff --git a/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java b/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java index 3c41a5d35e..ae83661dfb 100755 --- a/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java +++ b/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java @@ -133,22 +133,26 @@ public class ImportTest extends AbstractModelTest { for (SocialLinkModel socialLinkModel : socialLinks) { if ("facebook".equals(socialLinkModel.getSocialProvider())) { facebookFound = true; + Assert.assertEquals(socialLinkModel.getSocialUserId(), "facebook1"); Assert.assertEquals(socialLinkModel.getSocialUsername(), "fbuser1"); } else if ("google".equals(socialLinkModel.getSocialProvider())) { googleFound = true; + Assert.assertEquals(socialLinkModel.getSocialUserId(), "google1"); Assert.assertEquals(socialLinkModel.getSocialUsername(), "mySocialUser@gmail.com"); } else if ("twitter".equals(socialLinkModel.getSocialProvider())) { twitterFound = true; + Assert.assertEquals(socialLinkModel.getSocialUserId(), "twitter1"); Assert.assertEquals(socialLinkModel.getSocialUsername(), "twuser1"); } } Assert.assertTrue(facebookFound && twitterFound && googleFound); - UserModel foundSocialUser = realm.getUserBySocialLink(new SocialLinkModel("facebook", "fbuser1")); + UserModel foundSocialUser = realm.getUserBySocialLink(new SocialLinkModel("facebook", "facebook1", "fbuser1")); Assert.assertEquals(foundSocialUser.getLoginName(), socialUser.getLoginName()); - Assert.assertNull(realm.getUserBySocialLink(new SocialLinkModel("facebook", "not-existing"))); + Assert.assertNull(realm.getUserBySocialLink(new SocialLinkModel("facebook", "not-existing", "not-existing"))); SocialLinkModel foundSocialLink = realm.getSocialLink(socialUser, "facebook"); + Assert.assertEquals("facebook1", foundSocialLink.getSocialUserId()); Assert.assertEquals("fbuser1", foundSocialLink.getSocialUsername()); Assert.assertEquals("facebook", foundSocialLink.getSocialProvider()); diff --git a/model/tests/src/test/resources/testrealm.json b/model/tests/src/test/resources/testrealm.json index 6f573d465a..e0d6cfb97a 100755 --- a/model/tests/src/test/resources/testrealm.json +++ b/model/tests/src/test/resources/testrealm.json @@ -52,14 +52,17 @@ "socialLinks": [ { "socialProvider": "facebook", + "socialUserId": "facebook1", "socialUsername": "fbuser1" }, { "socialProvider": "twitter", + "socialUserId": "twitter1", "socialUsername": "twuser1" }, { "socialProvider": "google", + "socialUserId": "google1", "socialUsername": "mySocialUser@gmail.com" } ] diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java index dae9ae3946..face0343ec 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -366,7 +366,7 @@ public class RealmManager { for (SocialMappingRepresentation socialMapping : rep.getSocialMappings()) { UserModel user = userMap.get(socialMapping.getUsername()); for (SocialLinkRepresentation link : socialMapping.getSocialLinks()) { - SocialLinkModel mappingModel = new SocialLinkModel(link.getSocialProvider(), link.getSocialUsername()); + SocialLinkModel mappingModel = new SocialLinkModel(link.getSocialProvider(), link.getSocialUserId(), link.getSocialUsername()); newRealm.addSocialLink(user, mappingModel); } } diff --git a/services/src/main/java/org/keycloak/services/resources/SocialResource.java b/services/src/main/java/org/keycloak/services/resources/SocialResource.java index 55cb9a3ddb..d8ff9b1cc4 100755 --- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java +++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java @@ -23,7 +23,6 @@ package org.keycloak.services.resources; import org.jboss.resteasy.logging.Logger; import org.jboss.resteasy.spi.HttpRequest; -import org.jboss.resteasy.spi.HttpResponse; import org.keycloak.models.AccountRoles; import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; @@ -32,22 +31,20 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.SocialLinkModel; import org.keycloak.models.UserModel; import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.services.managers.AppAuthManager; -import org.keycloak.services.managers.Auth; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.RealmManager; +import org.keycloak.services.managers.SocialRequestManager; import org.keycloak.services.managers.TokenManager; import org.keycloak.services.resources.flows.Flows; import org.keycloak.services.resources.flows.OAuthFlows; import org.keycloak.services.resources.flows.Urls; import org.keycloak.social.AuthCallback; -import org.keycloak.social.AuthRequest; import org.keycloak.social.RequestDetails; +import org.keycloak.social.SocialAccessDeniedException; import org.keycloak.social.SocialLoader; import org.keycloak.social.SocialProvider; import org.keycloak.social.SocialProviderConfig; import org.keycloak.social.SocialProviderException; -import org.keycloak.services.managers.SocialRequestManager; import org.keycloak.social.SocialUser; import javax.ws.rs.GET; @@ -57,6 +54,7 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.container.ResourceContext; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriBuilder; @@ -141,12 +139,20 @@ public class SocialResource { SocialUser socialUser; try { socialUser = provider.processCallback(config, callback); + } catch (SocialAccessDeniedException e) { + MultivaluedHashMap queryParms = new MultivaluedHashMap(); + queryParms.putSingle("client_id", requestData.getClientAttribute("clientId")); + queryParms.putSingle("state", requestData.getClientAttribute("state")); + queryParms.putSingle("scope", requestData.getClientAttribute("scope")); + queryParms.putSingle("redirect_uri", requestData.getClientAttribute("redirectUri")); + queryParms.putSingle("response_type", requestData.getClientAttribute("responseType")); + return Flows.forms(realm, request, uriInfo).setQueryParams(queryParms).setWarning("Access denied").createLogin(); } catch (SocialProviderException e) { logger.warn("Failed to process social callback", e); return oauth.forwardToSecurityFailure("Failed to process social callback"); } - SocialLinkModel socialLink = new SocialLinkModel(provider.getId(), socialUser.getId()); + SocialLinkModel socialLink = new SocialLinkModel(provider.getId(), socialUser.getId(), socialUser.getUsername()); UserModel user = realm.getUserBySocialLink(socialLink); // Check if user is already authenticated (this means linking social into existing user account) @@ -210,7 +216,7 @@ public class SocialResource { public Response redirectToProviderAuth(@PathParam("realm") final String realmName, @QueryParam("provider_id") final String providerId, @QueryParam("client_id") final String clientId, @QueryParam("scope") final String scope, @QueryParam("state") final String state, - @QueryParam("redirect_uri") String redirectUri) { + @QueryParam("redirect_uri") String redirectUri, @QueryParam("response_type") String responseType) { RealmManager realmManager = new RealmManager(session); RealmModel realm = realmManager.getRealmByName(realmName); @@ -239,20 +245,24 @@ public class SocialResource { .putClientAttribute("realm", realmName) .putClientAttribute("clientId", clientId).putClientAttribute("scope", scope) .putClientAttribute("state", state).putClientAttribute("redirectUri", redirectUri) - .redirectToSocialProvider(); + .putClientAttribute("responseType", responseType).redirectToSocialProvider(); } catch (Throwable t) { return Flows.forms(realm, request, uriInfo).setError("Failed to redirect to social auth").createErrorPage(); } } private RequestDetails getRequestDetails(Map queryParams) { - for (SocialProvider provider : SocialLoader.load()) { - if (queryParams.containsKey(provider.getRequestIdParamName())) { - String requestId = queryParams.get(provider.getRequestIdParamName())[0]; - if (socialRequestManager.isRequestId(requestId)) { - return socialRequestManager.retrieveData(requestId); - } - } + String requestId = null; + if (queryParams.containsKey("state")) { + requestId = queryParams.get("state")[0]; + } else if (queryParams.containsKey("oauth_token")) { + requestId = queryParams.get("oauth_token")[0]; + } else if (queryParams.containsKey("denied")) { + requestId = queryParams.get("denied")[0]; + } + + if (requestId != null && socialRequestManager.isRequestId(requestId)) { + return socialRequestManager.retrieveData(requestId); } return null; diff --git a/social/core/src/main/java/org/keycloak/social/AbstractOAuth2Provider.java b/social/core/src/main/java/org/keycloak/social/AbstractOAuth2Provider.java index ae8a0a5d49..7c6d2d706a 100644 --- a/social/core/src/main/java/org/keycloak/social/AbstractOAuth2Provider.java +++ b/social/core/src/main/java/org/keycloak/social/AbstractOAuth2Provider.java @@ -51,6 +51,15 @@ public abstract class AbstractOAuth2Provider implements SocialProvider { @Override public SocialUser processCallback(SocialProviderConfig config, AuthCallback callback) throws SocialProviderException { + String error = callback.getQueryParam("error"); + if (error != null) { + if (error.equals("access_denied")) { + throw new SocialAccessDeniedException(); + } else { + throw new SocialProviderException(error); + } + } + try { String code = callback.getQueryParam(CODE); @@ -82,9 +91,4 @@ public abstract class AbstractOAuth2Provider implements SocialProvider { } } - @Override - public String getRequestIdParamName() { - return STATE; - } - } diff --git a/social/core/src/main/java/org/keycloak/social/AuthCallback.java b/social/core/src/main/java/org/keycloak/social/AuthCallback.java index d9c579a211..3ad0774f1a 100644 --- a/social/core/src/main/java/org/keycloak/social/AuthCallback.java +++ b/social/core/src/main/java/org/keycloak/social/AuthCallback.java @@ -43,7 +43,7 @@ public class AuthCallback { public String getQueryParam(String name) { String[] value = queryParams.get(name); - if (value.length > 0) { + if (value != null && value.length > 0) { return value[0]; } return null; diff --git a/social/core/src/main/java/org/keycloak/social/SocialAccessDeniedException.java b/social/core/src/main/java/org/keycloak/social/SocialAccessDeniedException.java new file mode 100644 index 0000000000..6f9d0c7bd1 --- /dev/null +++ b/social/core/src/main/java/org/keycloak/social/SocialAccessDeniedException.java @@ -0,0 +1,7 @@ +package org.keycloak.social; + +/** + * @author Stian Thorgersen + */ +public class SocialAccessDeniedException extends SocialProviderException { +} diff --git a/social/core/src/main/java/org/keycloak/social/SocialProvider.java b/social/core/src/main/java/org/keycloak/social/SocialProvider.java index 70da76de24..73a8f21d93 100644 --- a/social/core/src/main/java/org/keycloak/social/SocialProvider.java +++ b/social/core/src/main/java/org/keycloak/social/SocialProvider.java @@ -31,8 +31,6 @@ public interface SocialProvider { AuthRequest getAuthUrl(SocialProviderConfig config) throws SocialProviderException; - String getRequestIdParamName(); - String getName(); SocialUser processCallback(SocialProviderConfig config, AuthCallback callback) throws SocialProviderException; diff --git a/social/core/src/main/java/org/keycloak/social/SocialProviderException.java b/social/core/src/main/java/org/keycloak/social/SocialProviderException.java index 98cd2c35c9..5697046f7e 100644 --- a/social/core/src/main/java/org/keycloak/social/SocialProviderException.java +++ b/social/core/src/main/java/org/keycloak/social/SocialProviderException.java @@ -28,6 +28,9 @@ public class SocialProviderException extends Exception { private static final long serialVersionUID = 1L; + protected SocialProviderException() { + } + public SocialProviderException(String message) { super(message); } diff --git a/social/core/src/main/java/org/keycloak/social/SocialUser.java b/social/core/src/main/java/org/keycloak/social/SocialUser.java index 2c6945675c..9a1faed3e6 100644 --- a/social/core/src/main/java/org/keycloak/social/SocialUser.java +++ b/social/core/src/main/java/org/keycloak/social/SocialUser.java @@ -3,12 +3,14 @@ package org.keycloak.social; public class SocialUser { private String id; + private String username; private String firstName; private String lastName; private String email; - public SocialUser(String id) { + public SocialUser(String id, String username) { this.id = id; + this.username = username; } public String getId() { @@ -19,6 +21,14 @@ public class SocialUser { this.id = id; } + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + public String getFirstName() { return firstName; } diff --git a/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookProvider.java b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookProvider.java index cc62c52a6f..9f6403608a 100755 --- a/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookProvider.java +++ b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookProvider.java @@ -52,7 +52,7 @@ public class FacebookProvider extends AbstractOAuth2Provider { try { JSONObject profile = SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken).asJson(); - SocialUser user = new SocialUser(profile.getString("id")); + SocialUser user = new SocialUser(profile.getString("id"), profile.getString("username")); user.setName(profile.optString("first_name"), profile.optString("last_name")); user.setEmail(profile.optString("email")); @@ -64,10 +64,6 @@ public class FacebookProvider extends AbstractOAuth2Provider { @Override public AuthRequest getAuthUrl(SocialProviderConfig config) throws SocialProviderException { - if (config.getCallbackUrl().contains("//localhost")) { - String callbackUrl = config.getCallbackUrl().replace("//localhost", "//127.0.0.1"); - config = new SocialProviderConfig(config.getKey(), config.getSecret(), callbackUrl); - } return super.getAuthUrl(config); } diff --git a/social/github/src/main/java/org/keycloak/social/github/GitHubProvider.java b/social/github/src/main/java/org/keycloak/social/github/GitHubProvider.java index ad38dcd439..95c1067e91 100755 --- a/social/github/src/main/java/org/keycloak/social/github/GitHubProvider.java +++ b/social/github/src/main/java/org/keycloak/social/github/GitHubProvider.java @@ -52,7 +52,7 @@ public class GitHubProvider extends AbstractOAuth2Provider { try { JSONObject profile = SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken).asJson(); - SocialUser user = new SocialUser(profile.get("id").toString()); + SocialUser user = new SocialUser(profile.get("id").toString(), profile.getString("login")); user.setName(profile.optString("name")); user.setEmail(profile.optString("email")); diff --git a/social/google/src/main/java/org/keycloak/social/google/GoogleProvider.java b/social/google/src/main/java/org/keycloak/social/google/GoogleProvider.java index 08b1104e5d..ab3d8f47ea 100755 --- a/social/google/src/main/java/org/keycloak/social/google/GoogleProvider.java +++ b/social/google/src/main/java/org/keycloak/social/google/GoogleProvider.java @@ -71,7 +71,7 @@ public class GoogleProvider extends AbstractOAuth2Provider { try { JSONObject profile = SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken).asJson(); - SocialUser user = new SocialUser(profile.getString("sub")); + SocialUser user = new SocialUser(profile.getString("sub"), profile.getString("email")); user.setName(profile.optString("given_name"), profile.optString("family_name")); user.setEmail(profile.optString("email")); diff --git a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java index fda4c6af99..10df5cda46 100755 --- a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java +++ b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterProvider.java @@ -23,6 +23,7 @@ package org.keycloak.social.twitter; import org.keycloak.social.AuthCallback; import org.keycloak.social.AuthRequest; +import org.keycloak.social.SocialAccessDeniedException; import org.keycloak.social.SocialProvider; import org.keycloak.social.SocialProviderConfig; import org.keycloak.social.SocialProviderException; @@ -67,6 +68,10 @@ public class TwitterProvider implements SocialProvider { @Override public SocialUser processCallback(SocialProviderConfig config, AuthCallback callback) throws SocialProviderException { + if (callback.getQueryParam("denied") != null) { + throw new SocialAccessDeniedException(); + } + try { Twitter twitter = new TwitterFactory().getInstance(); twitter.setOAuthConsumer(config.getKey(), config.getSecret()); @@ -77,7 +82,7 @@ public class TwitterProvider implements SocialProvider { twitter.getOAuthAccessToken(requestToken, verifier); twitter4j.User twitterUser = twitter.verifyCredentials(); - SocialUser user = new SocialUser(Long.toString(twitterUser.getId())); + SocialUser user = new SocialUser(Long.toString(twitterUser.getId()), twitterUser.getScreenName()); user.setName(twitterUser.getName()); return user; @@ -86,9 +91,4 @@ public class TwitterProvider implements SocialProvider { } } - @Override - public String getRequestIdParamName() { - return "oauth_token"; - } - } diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java index 68d6c5ef7b..0a0e463066 100755 --- a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java +++ b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java @@ -202,11 +202,12 @@ public class KeycloakServer { try { RealmManager manager = new RealmManager(session); - if (rep.getId() == null) { - throw new RuntimeException("Realm id not specified"); + if (rep.getId() != null && manager.getRealm(rep.getId()) != null) { + info("Not importing realm " + rep.getRealm() + " realm already exists"); + return; } - if (manager.getRealm(rep.getId()) != null) { + if (manager.getRealmByName(rep.getRealm()) != null) { info("Not importing realm " + rep.getRealm() + " realm already exists"); return; } @@ -268,7 +269,7 @@ public class KeycloakServer { server.deploy(di); - factory = ((KeycloakApplication)deployment.getApplication()).getFactory(); + factory = ((KeycloakApplication) deployment.getApplication()).getFactory(); setupDevConfig(); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java index ee4607e70f..5ad7cf6c54 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocial.java @@ -2,6 +2,7 @@ package org.keycloak.testsuite; import org.keycloak.social.AuthCallback; import org.keycloak.social.AuthRequest; +import org.keycloak.social.SocialAccessDeniedException; import org.keycloak.social.SocialProvider; import org.keycloak.social.SocialProviderConfig; import org.keycloak.social.SocialProviderException; @@ -26,11 +27,6 @@ public class DummySocial implements SocialProvider { .setQueryParam("redirect_uri", config.getCallbackUrl()).setQueryParam("state", state).setAttribute("state", state).build(); } - @Override - public String getRequestIdParamName() { - return "state"; - } - @Override public String getName() { return "Dummy Provider"; @@ -38,12 +34,18 @@ public class DummySocial implements SocialProvider { @Override public SocialUser processCallback(SocialProviderConfig config, AuthCallback callback) throws SocialProviderException { + String error = callback.getQueryParam("error"); + if (error != null) { + throw new SocialAccessDeniedException(); + } + if (!callback.getQueryParam("state").equals(callback.getAttribute("state"))) { throw new SocialProviderException("Invalid state"); } + String id = callback.getQueryParam("id"); String username = callback.getQueryParam("username"); - SocialUser user = new SocialUser(username); + SocialUser user = new SocialUser(id, username); user.setName(callback.getQueryParam("firstname"), callback.getQueryParam("lastname")); user.setEmail(callback.getQueryParam("email")); return user; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java index 9278d36945..e87ce1d47c 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummySocialServlet.java @@ -22,11 +22,13 @@ public class DummySocialServlet extends HttpServlet { pw.print(""); pw.print(""); pw.print("
"); + pw.print(""); pw.print(""); pw.print(""); pw.print(""); pw.print(""); - pw.print(""); + pw.print(""); + pw.print(""); pw.print("
"); pw.print(""); pw.print(""); @@ -53,15 +55,20 @@ public class DummySocialServlet extends HttpServlet { } } - String redirect = redirectUri + "?username=" + req.getParameter("username") + "&state=" + state + "&code=" + UUID.randomUUID().toString(); - if (req.getParameter("firstname") != null) { - redirect += "&firstname=" + req.getParameter("firstname"); - } - if (req.getParameter("lastname") != null) { - redirect += "&lastname=" + req.getParameter("lastname"); - } - if (req.getParameter("email") != null) { - redirect += "&email=" + req.getParameter("email"); + String redirect; + if (req.getParameter("login") != null) { + redirect = redirectUri + "?id=" + req.getParameter("id") + "&username=" + req.getParameter("username") + "&state=" + state + "&code=" + UUID.randomUUID().toString(); + if (req.getParameter("firstname") != null) { + redirect += "&firstname=" + req.getParameter("firstname"); + } + if (req.getParameter("lastname") != null) { + redirect += "&lastname=" + req.getParameter("lastname"); + } + if (req.getParameter("email") != null) { + redirect += "&email=" + req.getParameter("email"); + } + } else { + redirect = redirectUri + "?error=access_denied&state=" + state; } resp.sendRedirect(redirect); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java index 3671ae0b63..a266b8a0c2 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java @@ -62,6 +62,9 @@ public class LoginPage extends AbstractPage { @FindBy(className = "feedback-error") private WebElement loginErrorMessage; + @FindBy(className = "feedback-warning") + private WebElement loginWarningMessage; + public void login(String username, String password) { usernameInput.clear(); usernameInput.sendKeys(username); @@ -80,6 +83,11 @@ public class LoginPage extends AbstractPage { return loginErrorMessage != null ? loginErrorMessage.getText() : null; } + public String getWarning() { + return loginWarningMessage != null ? loginWarningMessage.getText() : null; + } + + public boolean isCurrent() { return driver.getTitle().equals("Log in to test"); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java index 27e7a871c6..18c4281588 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java @@ -97,11 +97,12 @@ public class SocialLoginTest { loginPage.clickSocial("dummy"); + driver.findElement(By.id("id")).sendKeys("1"); driver.findElement(By.id("username")).sendKeys("dummy-user1"); driver.findElement(By.id("firstname")).sendKeys("Bob"); driver.findElement(By.id("lastname")).sendKeys("Builder"); driver.findElement(By.id("email")).sendKeys("bob@builder.com"); - driver.findElement(By.id("submit")).click(); + driver.findElement(By.id("login")).click(); Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); @@ -118,6 +119,23 @@ public class SocialLoginTest { Assert.assertEquals("bob@builder.com", profile.getEmail()); } + + @Test + public void loginCancelled() throws Exception { + loginPage.open(); + + loginPage.clickSocial("dummy"); + + driver.findElement(By.id("cancel")).click(); + + Assert.assertTrue(loginPage.isCurrent()); + Assert.assertEquals("Access denied", loginPage.getWarning()); + + loginPage.login("test-user@localhost", "password"); + + Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); + } + @Test public void profileUpdateRequired() { keycloakRule.configure(new KeycloakSetup() { @@ -132,11 +150,12 @@ public class SocialLoginTest { loginPage.clickSocial("dummy"); + driver.findElement(By.id("id")).sendKeys("2"); driver.findElement(By.id("username")).sendKeys("dummy-user2"); driver.findElement(By.id("firstname")).sendKeys("Bob"); driver.findElement(By.id("lastname")).sendKeys("Builder"); driver.findElement(By.id("email")).sendKeys("bob@builder.com"); - driver.findElement(By.id("submit")).click(); + driver.findElement(By.id("login")).click(); profilePage.isCurrent(); diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java index 12fced6788..62d22af157 100755 --- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java +++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java @@ -97,7 +97,7 @@ public class CreateUsersWorker implements Worker { + " which is too big."); } - SocialLinkModel socialLink = new SocialLinkModel(socialProvider, username); + SocialLinkModel socialLink = new SocialLinkModel(socialProvider, username, username); realm.addSocialLink(user, socialLink); } diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java index 80c8b57985..c125ac56ff 100755 --- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java +++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java @@ -112,7 +112,7 @@ public class ReadUsersWorker implements Worker { // Try to search by social links if (searchBySocialLinks) { - SocialLinkModel socialLink = new SocialLinkModel("facebook", username); + SocialLinkModel socialLink = new SocialLinkModel("facebook", username, username); realm.getUserBySocialLink(socialLink); } }