Created
March 8, 2023 17:46
-
-
Save vnkdj5/1548c31e8801baf9365872a92f656540 to your computer and use it in GitHub Desktop.
Keycloak JS Authenticator to Link Social accounts automatically if the user already exists
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Template for JavaScript based authenticator's. | |
* See org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticatorFactory | |
*/ | |
// import enum for error lookup | |
AuthenticationFlowError = Java.type("org.keycloak.authentication.AuthenticationFlowError"); | |
ServicesLogger = Java.type("org.keycloak.services.ServicesLogger"); | |
AbstractIdpAuthenticator = Java.type("org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator"); | |
IdpCreateUserIfUniqueAuthenticator = Java.type("org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticator"); | |
ExistingUserInfo = Java.type("org.keycloak.authentication.authenticators.broker.util.ExistingUserInfo"); | |
/** | |
* An example authenticate function. | |
* | |
* The following variables are available for convenience: | |
* user - current user {@see org.keycloak.models.UserModel} | |
* realm - current realm {@see org.keycloak.models.RealmModel} | |
* session - current KeycloakSession {@see org.keycloak.models.KeycloakSession} | |
* httpRequest - current HttpRequest {@see org.jboss.resteasy.spi.HttpRequest} | |
* script - current script {@see org.keycloak.models.ScriptModel} | |
* authenticationSession - current authentication session {@see org.keycloak.sessions.AuthenticationSessionModel} | |
* LOG - current logger {@see org.jboss.logging.Logger} | |
* | |
* You one can extract current http request headers via: | |
* httpRequest.getHttpHeaders().getHeaderString("Forwarded") | |
* | |
* @param context {@see org.keycloak.authentication.AuthenticationFlowContext} | |
*/ | |
/* | |
Things to note: Assumed User are created with mobileNumber in username field and email in email field | |
*/ | |
var IdpUserMustExists = Java.extend(IdpCreateUserIfUniqueAuthenticator); | |
function authenticate(context) { | |
var auth = new IdpUserMustExists(){ | |
authenticateImpl: function(context, serializedCtx, brokerContext) { | |
var parent = Java.super(auth); | |
var session = context.getSession(); | |
var realm = context.getRealm(); | |
var authSession = context.getAuthenticationSession(); | |
var bc = JSON.parse(authenticationSession.getAuthNote("BROKERED_CONTEXT")), | |
idpID = bc.identityProviderId, | |
//token = bc.contextData.FEDERATED_ACCESS_TOKEN.data | |
email = bc.email; | |
print("IDP_ID",idpID); | |
print(authenticationSession.getAuthNote("BROKERED_CONTEXT")); | |
if (email){ | |
LOG.info("email " + email); | |
// print(token) | |
} | |
if (authSession.getAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO) !== null) { | |
print("Existig user. Attemped connection"); | |
print(authSession.getAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO)); | |
context.attempted(); | |
return; | |
} | |
var username = parent.getUsername(context, serializedCtx, brokerContext); | |
if (username) { | |
LOG.info("username " + username); | |
} | |
if(idpID.contains("github") && email!==null){ | |
username=email; | |
} | |
if (username === null) { | |
ServicesLogger.LOGGER.resetFlow(realm.isRegistrationEmailAsUsername() ? "Email" : "Username"); | |
authSession.setAuthNote(AbstractIdpAuthenticator.ENFORCE_UPDATE_PROFILE, "true"); | |
context.resetFlow(); | |
return; | |
} | |
var userWithEmail = session.users().getUserByEmail(username, realm); | |
var duplication = parent.checkExistingUser(context, username, serializedCtx, brokerContext); | |
if (userWithEmail) { | |
LOG.info("User with email user" + userWithEmail.getEmail()); | |
} | |
if (duplication === null && userWithEmail === null) { | |
print("1st if"); | |
LOG.info("user not found " + username); | |
context.failure(AuthenticationFlowError.INVALID_USER); | |
return; | |
} else if (userWithEmail) { | |
print("Case for user having emailId as sub Usage: oidc-email, google, github"); | |
var emailuser = new ExistingUserInfo( | |
userWithEmail.getId(), "username", userWithEmail.getUsername()); | |
authSession.setAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO, emailuser.serialize()); | |
print("2ndIF::EXISTING_USER_INFO "+authSession.getAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO)); | |
context.attempted(); | |
} else { | |
print("Case for user having phoneNub as sub. Usage oidc- mobile login"); | |
//LOG.info("duplication " + duplication.serialize()); | |
var userWithPhone = session.users().getUserByUsername(username, realm); | |
var phoneUser = new ExistingUserInfo( | |
duplication.getExistingUserId(), "username", userWithPhone.getUsername()); | |
authSession.setAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO, phoneUser.serialize()); | |
print("last else::EXISTING_USER_INFO "+authSession.getAuthNote(AbstractIdpAuthenticator.EXISTING_USER_INFO)); | |
context.attempted(); | |
} | |
} | |
} | |
auth.authenticate(context); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment