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 34c5979c7cc..b0cbc6a3e1d 100755 --- a/events/api/src/main/java/org/keycloak/events/Errors.java +++ b/events/api/src/main/java/org/keycloak/events/Errors.java @@ -44,8 +44,7 @@ public interface Errors { String NOT_ALLOWED = "not_allowed"; - String FEDERATED_IDENTITY_EMAIL_EXISTS = "federated_identity_email_exists"; - String FEDERATED_IDENTITY_USERNAME_EXISTS = "federated_identity_username_exists"; + String FEDERATED_IDENTITY_EXISTS = "federated_identity_account_exists"; String SSL_REQUIRED = "ssl_required"; String USER_SESSION_NOT_FOUND = "user_session_not_found"; 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 5cffe7845ca..b75728bd15a 100755 --- a/events/api/src/main/java/org/keycloak/events/EventType.java +++ b/events/api/src/main/java/org/keycloak/events/EventType.java @@ -48,6 +48,8 @@ public enum EventType { SEND_VERIFY_EMAIL_ERROR(true), SEND_RESET_PASSWORD(true), SEND_RESET_PASSWORD_ERROR(true), + SEND_IDENTITY_PROVIDER_LINK(true), + SEND_IDENTITY_PROVIDER_LINK_ERROR(true), RESET_PASSWORD(true), RESET_PASSWORD_ERROR(true), @@ -66,8 +68,6 @@ public enum EventType { IDENTITY_PROVIDER_RESPONSE_ERROR(false), IDENTITY_PROVIDER_RETRIEVE_TOKEN(false), IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR(false), - IDENTITY_PROVIDER_ACCCOUNT_LINKING(false), - IDENTITY_PROVIDER_ACCCOUNT_LINKING_ERROR(false), IMPERSONATE(true), CUSTOM_REQUIRED_ACTION(true), CUSTOM_REQUIRED_ACTION_ERROR(true), diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpCreateUserIfUniqueAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpCreateUserIfUniqueAuthenticator.java index ffb23004a39..b4ee957e43f 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpCreateUserIfUniqueAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpCreateUserIfUniqueAuthenticator.java @@ -10,11 +10,12 @@ import org.keycloak.authentication.AuthenticationFlowContext; import org.keycloak.authentication.authenticators.broker.util.ExistingUserInfo; import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext; import org.keycloak.broker.provider.BrokeredIdentityContext; +import org.keycloak.events.Details; +import org.keycloak.events.Errors; import org.keycloak.models.AuthenticatorConfigModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; -import org.keycloak.models.utils.FormMessage; import org.keycloak.services.messages.Messages; /** @@ -78,6 +79,15 @@ public class IdpCreateUserIfUniqueAuthenticator extends AbstractIdpAuthenticator .setError(Messages.FEDERATED_IDENTITY_EXISTS, duplication.getDuplicateAttributeName(), duplication.getDuplicateAttributeValue()) .createErrorPage(); context.challenge(challengeResponse); + + if (context.getExecution().isRequired()) { + context.getEvent() + .user(duplication.getExistingUserId()) + .detail("existing_" + duplication.getDuplicateAttributeName(), duplication.getDuplicateAttributeValue()) + .removeDetail(Details.AUTH_METHOD) + .removeDetail(Details.AUTH_TYPE) + .error(Errors.FEDERATED_IDENTITY_EXISTS); + } } } diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java index d6bf10fad36..ae28d3edc91 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java @@ -14,6 +14,10 @@ import org.keycloak.authentication.authenticators.broker.util.SerializedBrokered import org.keycloak.broker.provider.BrokeredIdentityContext; import org.keycloak.email.EmailException; import org.keycloak.email.EmailProvider; +import org.keycloak.events.Details; +import org.keycloak.events.Errors; +import org.keycloak.events.EventBuilder; +import org.keycloak.events.EventType; import org.keycloak.login.LoginFormsProvider; import org.keycloak.models.ClientSessionModel; import org.keycloak.models.Constants; @@ -52,6 +56,15 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator String link = UriBuilder.fromUri(context.getActionUrl()) .queryParam(Constants.KEY, clientSession.getNote(Constants.VERIFY_EMAIL_KEY)) .build().toString(); + + EventBuilder event = context.getEvent().clone().event(EventType.SEND_IDENTITY_PROVIDER_LINK) + .user(existingUser) + .detail(Details.USERNAME, existingUser.getUsername()) + .detail(Details.EMAIL, existingUser.getEmail()) + .detail(Details.CODE_ID, clientSession.getId()) + .removeDetail(Details.AUTH_METHOD) + .removeDetail(Details.AUTH_TYPE); + long expiration = TimeUnit.SECONDS.toMinutes(context.getRealm().getAccessCodeLifespanUserAction()); try { @@ -60,15 +73,11 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator .setUser(existingUser) .setAttribute(EmailProvider.IDENTITY_PROVIDER_BROKER_CONTEXT, brokerContext) .sendConfirmIdentityBrokerLink(link, expiration); -// event.clone().event(EventType.SEND_RESET_PASSWORD) -// .user(user) -// .detail(Details.USERNAME, username) -// .detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, context.getClientSession().getId()).success(); + + event.success(); } catch (EmailException e) { -// event.clone().event(EventType.SEND_RESET_PASSWORD) -// .detail(Details.USERNAME, username) -// .user(user) -// .error(Errors.EMAIL_SEND_FAILED); + event.error(Errors.EMAIL_SEND_FAILED); + logger.error("Failed to send email to confirm identity broker linking", e); Response challenge = context.form() .setError(Messages.EMAIL_SENT_ERROR) 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 71f849b6d9f..2bebec3bfba 100755 --- a/services/src/main/java/org/keycloak/services/resources/AccountService.java +++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java @@ -724,7 +724,9 @@ public class AccountService extends AbstractSecuredLocalService { logger.debugv("Social provider {0} removed successfully from user {1}", providerId, user.getUsername()); event.event(EventType.REMOVE_FEDERATED_IDENTITY).client(auth.getClient()).user(auth.getUser()) - .detail(Details.USERNAME, link.getUserId() + "@" + link.getIdentityProvider()) + .detail(Details.USERNAME, auth.getUser().getUsername()) + .detail(Details.IDENTITY_PROVIDER, link.getIdentityProvider()) + .detail(Details.IDENTITY_PROVIDER_USERNAME, link.getUserName()) .success(); setReferrerOnPage(); diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java index fda37efc009..c8784bda2f4 100755 --- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java +++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java @@ -368,6 +368,13 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal context.getUsername(), context.getToken()); session.users().addFederatedIdentity(realmModel, federatedUser, federatedIdentityModel); + EventBuilder event = this.event.clone().user(federatedUser) + .detail(Details.CODE_ID, clientSession.getId()) + .detail(Details.USERNAME, federatedUser.getUsername()) + .detail(Details.IDENTITY_PROVIDER, providerId) + .detail(Details.IDENTITY_PROVIDER_USERNAME, context.getUsername()) + .removeDetail("auth_method"); + String isRegisteredNewUser = clientSession.getNote(AbstractIdpAuthenticator.BROKER_REGISTERED_NEW_USER); if (Boolean.parseBoolean(isRegisteredNewUser)) { @@ -388,15 +395,17 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal federatedUser.setEmailVerified(true); } - this.event.clone().user(federatedUser).event(EventType.REGISTER) - .detail(Details.IDENTITY_PROVIDER, providerId) - .detail(Details.IDENTITY_PROVIDER_USERNAME, context.getUsername()) - .removeDetail("auth_method") + event.event(EventType.REGISTER) + .detail(Details.REGISTER_METHOD, "broker") + .detail(Details.EMAIL, federatedUser.getEmail()) .success(); } else { LOGGER.debugf("Linked existing keycloak user '%s' with identity provider '%s' . Identity provider username is '%s' .", federatedUser.getUsername(), providerId, context.getUsername()); + event.event(EventType.FEDERATED_IDENTITY_LINK) + .success(); + updateFederatedIdentity(context, federatedUser); } @@ -453,7 +462,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal } private Response performAccountLinking(ClientSessionModel clientSession, BrokeredIdentityContext context, FederatedIdentityModel federatedIdentityModel, UserModel federatedUser) { - this.event.event(EventType.IDENTITY_PROVIDER_ACCCOUNT_LINKING); + this.event.event(EventType.FEDERATED_IDENTITY_LINK); if (federatedUser != null) { return redirectToErrorPage(Messages.IDENTITY_PROVIDER_ALREADY_LINKED, context.getIdpConfig().getAlias()); @@ -478,7 +487,11 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal this.session.users().addFederatedIdentity(this.realmModel, authenticatedUser, federatedIdentityModel); context.getIdp().attachUserSession(clientSession.getUserSession(), clientSession, context); - this.event.success(); + this.event.user(authenticatedUser) + .detail(Details.USERNAME, authenticatedUser.getUsername()) + .detail(Details.IDENTITY_PROVIDER, federatedIdentityModel.getIdentityProvider()) + .detail(Details.IDENTITY_PROVIDER_USERNAME, federatedIdentityModel.getUserName()) + .success(); return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build(); } diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java index 8f845c051ff..7f15d2d025b 100755 --- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java +++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java @@ -509,6 +509,10 @@ public class LoginActionsService { BrokeredIdentityContext brokerContext = serializedCtx.deserialize(session, clientSession); AuthenticationFlowModel firstBrokerLoginFlow = realm.getAuthenticationFlowById(brokerContext.getIdpConfig().getFirstBrokerLoginFlowId()); + event.detail(Details.IDENTITY_PROVIDER, brokerContext.getIdpConfig().getAlias()) + .detail(Details.IDENTITY_PROVIDER_USERNAME, brokerContext.getUsername()); + + AuthenticationProcessor processor = new AuthenticationProcessor() { @Override