@GET
@Path("callback")
public Response callback(@QueryParam("state") String encodedState) throws URISyntaxException, IOException {
ClientSessionCode clientCode = null;
ClientSessionModel clientSession = null;
try {
clientCode = ClientSessionCode.parse(encodedState, session);
if (clientCode == null) {
return Flows.forms(session, null, null, uriInfo).setError("Unexpected callback").createErrorPage();
}
clientSession = clientCode.getClientSession();
if (!clientCode.isValid(ClientSessionModel.Action.SOCIAL_CALLBACK)) {
return Flows.forwardToSecurityFailurePage(session, clientSession.getRealm(), uriInfo, "Invalid code, please login again through your application.");
}
} catch (Throwable t) {
logger.error("Invalid social callback", t);
return Flows.forms(session, null, null, uriInfo).setError("Unexpected callback").createErrorPage();
}
String providerId = clientSession.getNote("social_provider");
SocialProvider provider = SocialLoader.load(providerId);
String authMethod = "social@" + provider.getId();
RealmModel realm = clientSession.getRealm();
EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder()
.event(EventType.LOGIN)
.client(clientSession.getClient())
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
.detail(Details.AUTH_METHOD, authMethod);
if (!realm.isEnabled()) {
event.error(Errors.REALM_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.");
}
String key = realm.getSocialConfig().get(provider.getId() + ".key");
String secret = realm.getSocialConfig().get(provider.getId() + ".secret");
String callbackUri = Urls.socialCallback(uriInfo.getBaseUri()).toString();
SocialProviderConfig config = new SocialProviderConfig(key, secret, callbackUri);
Map<String, String[]> queryParams = getQueryParams();
AuthCallback callback = new AuthCallback(queryParams);
SocialUser socialUser;
try {
socialUser = provider.processCallback(clientSession, config, callback);
} catch (SocialAccessDeniedException e) {
event.error(Errors.REJECTED_BY_USER);
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
return Flows.forms(session, realm, clientSession.getClient(), uriInfo).setClientSessionCode(clientCode.getCode()).setWarning("Access denied").createLogin();
} catch (SocialProviderException e) {
logger.error("Failed to process social callback", e);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process social callback");
}
event.detail(Details.USERNAME, socialUser.getId() + "@" + provider.getId());
try {
SocialLinkModel socialLink = new SocialLinkModel(provider.getId(), socialUser.getId(), socialUser.getUsername());
UserModel user = session.users().getUserBySocialLink(socialLink, realm);
// Check if user is already authenticated (this means linking social into existing user account)
if (clientSession.getUserSession() != null) {
UserModel authenticatedUser = clientSession.getUserSession().getUser();
event.event(EventType.SOCIAL_LINK).user(authenticatedUser.getId());
if (user != null) {
event.error(Errors.SOCIAL_ID_IN_USE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "This social account is already linked to other user");
}
if (!authenticatedUser.isEnabled()) {
event.error(Errors.USER_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "User is disabled");
}
if (!authenticatedUser.hasRole(realm.getApplicationByName(Constants.ACCOUNT_MANAGEMENT_APP).getRole(AccountRoles.MANAGE_ACCOUNT))) {
event.error(Errors.NOT_ALLOWED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Insufficient permissions to link social account");
}
session.users().addSocialLink(realm, authenticatedUser, socialLink);
logger.debugv("Social provider {0} linked with user {1}", provider.getId(), authenticatedUser.getUsername());
event.success();
return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build();
}
if (user == null) {
user = session.users().addUser(realm, KeycloakModelUtils.generateId());
user.setEnabled(true);
user.setFirstName(socialUser.getFirstName());
user.setLastName(socialUser.getLastName());
user.setEmail(socialUser.getEmail());
if (realm.isUpdateProfileOnInitialSocialLogin()) {
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PROFILE);
}
session.users().addSocialLink(realm, user, socialLink);
event.clone().user(user).event(EventType.REGISTER)
.detail(Details.REGISTER_METHOD, "social@" + provider.getId())
.detail(Details.EMAIL, socialUser.getEmail())
.removeDetail("auth_method")
.success();
}
event.user(user);
if (!user.isEnabled()) {
event.error(Errors.USER_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Your account is not enabled.");
}
String username = socialLink.getSocialUserId() + "@" + socialLink.getSocialProvider();
UserSessionModel userSession = session.sessions().createUserSession(realm, user, username, clientConnection.getRemoteAddr(), authMethod, false);
event.session(userSession);
TokenManager.attachClientSession(userSession, clientSession);
AuthenticationManager authManager = new AuthenticationManager();
Response response = authManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request, uriInfo, event);
if (session.getTransaction().isActive()) {
session.getTransaction().commit();
}
return response;
} catch (ModelDuplicateException e) {
// Assume email is the duplicate as there's nothing else atm
return Flows.forms(session, realm, clientSession.getClient(), uriInfo)
.setClientSessionCode(clientCode.getCode())
.setError("socialEmailExists")
.createLogin();
}
}