Keycloak - Adding an Additional Step to Login - keycloak

I have a situation where during login, after a user has entered their username/password, they would get transferred to an additional page where they would have to enter an additional field (or select something from a select box)
Today, I use a simple session id over a cookie. When a user enters their credentials I create a session, and after they had entered the field in the additional page, I update the session.
I was wondering what would be the best way to achieve something like that in Keycloak. I would also like to include that additional field in the token.
I guess the obvious way would be to keep my login frontend as it is now and use the direct credentials grant API that Keycloak provides, but I would rather avoid that.
Please advise.
Clarifications: Each user in the system can belong to multiple organizations. That additional field corresponds to the organization that the user logs in to. All applications that interact with the token have to be aware of that organization field

According to your requirements i would suggest following:
Continue with UserStorageProvider implementation. Refer to following docs. Your implementation should also provide list of available companies for every user. You can expose this list as an UserModel attribute.
Implement custom required action (components like that runs after successfully passed credential challenges) that will get list of available companies from UserModel object of authenticated user. Offer this list to user as separate login form, so after user submits his choice it will be persisted in user session note. As example check out implementation for UpdateUserLocaleAction and javadoc for RequiredActionProvider. Here is example of logic for storing selected company:
#Override
public void requiredActionChallenge(RequiredActionContext context) {
List<String> availableCompanies = context.getUser().getAttribute("companies");
... // form rendering
}
#Override
public void processAction(RequiredActionContext context) {
String company = context.getHttpRequest().getFormParameters().getFirst("selected_company");
context.getAuthenticationSession().setUserSessionNote("company", company);
}
Add UserSessionNote oidc mapper to required client (or to shared client scope), so it will extract 'company' note saved by required action implementation from step 2
???
Profit

Related

How to enable 2FA using email using keycloak-admin-client in spring boot

My requirement is enable 2FA using email in Keycloak.
When enabled, if user tries to login through email & password ,after user is successfully authenticated ,time based token will be sent to email .
User will do this action from custom UI i.e in our product we have UI to enable/disable 2FA for user.
We are using Keycloak & we want to achieve this using Keycloak API.
I am using keycloak-admin-client to interact with Keycloak API but I did not find sufficient resources to achieve this using keycloak-admin-client.
I am looking a way using keycloak-admin-client how to enable 2FA for user.
Any help will be highly appreciated.
Thank You
You should add custom REST endpoints to Keycloak to be able to enable 2FA from your custom UI. We have done this before. It's not that much complicated, but it requires you to have a look at Keycloak source to see what it's doing when OTP gets activated. Some important classes to check/use are TotpBean, OTPCredentialModel and OTPPolicy.
In order to enable the 2FA, we needed to show the QR code image in our custom UI. So we added an endpoint to Keycloak that instantiates an instance of TotpBean. It's the one that gives you access to the QR code image and the secret value that are required to generate the equivalent string representation of the image so that it could be scanned/entered in the 2FA app (e.g. Google Authenticator). Here is an example of how such an endpoint would look like:
#GET
#Produces({MediaType.APPLICATION_JSON})
#Path("/o2p-enable-config/{email}")
#NoCache
public Response fetchOtpEnableConfig(#Email #PathParam("email") String email) {
UserModel user = session.users().getUserByEmail(email, realm);
TotpBean totp = new TotpBean(session, realm, user, session.getContext().getUri().getRequestUriBuilder());
return Response
.ok(new YouOTPResponseClass("data:image/png;base64, " + totp.getTotpSecretQrCode(), totp.getTotpSecret(), totp.getTotpSecretEncoded()))
.build();
}
Then on your own backend, you call this endpoint and send the user's email to it and receive the image and the secret value. You can just display the image as is in your UI and keep the secret value on your backend (e.g. in user's session). When user scans the image using the app and enters the totp value provided by the app in your custom UI, you send the totp value and the secret to another endpoint that you should add to the Keycloak. This second endpoint is the one that does that verification of the value and enables 2FA.
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Path("/enable-2fa/{email}")
#NoCache
public Response enable2Fa(#Email #PathParam("email") String email, OtpDetails optDetails) {
OTPPolicy policy = realm.getOTPPolicy();
String totp = optDetails.getTotp();
UserModel user = session.users().getUserByEmail(email, realm);
OTPCredentialModel credential = OTPCredentialModel.createFromPolicy(realm, optDetails.getSecret(), optDetails.getUserLabel());
if (CredentialValidation.validOTP(totp, credential, policy.getLookAheadWindow())) {
CredentialHelper.createOTPCredential(session, realm, user, totp, credential);
return Response.noContent().status(204).build();
} else {
return Response.status(BAD_REQUEST).build();
}
}
Keycloak supports multiple 2FA for each user. That's why it also has a property named label that allows user to name them so that it would be displayed in the 2FA login scenario with given name. You can also allow user to enter the label value in your custom UI and pass it to the second endpoint (or just pass an empty value to Keycloak if you're not going to allow your users to setup multiple 2FA).
I know it seems complicated, but it's actually not that much. The Keycloak domain model is well designed and when you get familiar with it, you can easily find what you need to do and wrap it in custom APIs. But always ensure that exposing a functionality would not compromise the overall security model of the system.
Take a look at keycloak two factor email authenticator provider
https://github.com/mesutpiskin/keycloak-2fa-email-authenticator
I agree that is necessary to write a custom provider for this use case.
Take a look at https://www.n-k.de/2020/12/keycloak-2fa-sms-authentication.html and https://www.youtube.com/watch?v=GQi19817fFk for a look at how to implement that.
That is an example via SMS, but via e-mail would be very similar, changing just the way of sending the code to the user.

Determine if Keycloak User has TOTP enabled

I have a Single-Page Web-Application authenticating against Keycloak and letting Users enable TOTP in their Keycloak-Account-Page.
However I want the Client-Application to determine after login, if the authenticated User has 2-factor-authentication/TOTP enabled or not.
Is it possible to map this (boolean) information into the Tokens or userinfo Endpoint ... I haven't found any User Property, that contains this information.
The only Place I found it, was in Admin-REST-API /auth/admin/realms/{realm}/users/{uuid}, but the Client/End-user won't and shouldn't have access there:
{
...
totp: true,
}
I don't think this is possible without customization.
You may want to add a custom protocol mapper and check for totp like this:
keyclaokSession.userCredentialManager().isConfiguredFor(realm, user, OTPCredentialModel.TYPE)
Here is a video that explains the first steps.

Breeze EFContextProvider per request and based on parameter?

I have a multi-tenant app in which user can select "current company" after they log in.
There is a DB per company but the model is the same, the workflow is the same, and the controller actions are same....The user can switch companies while being logged in and all actions need to be 'directed' to proper DB.
I know it is possible to customize context creation in EFContextProvider<T> by overriding CreateContext() but how do I pass the extra info (parameter, e.g. CompanyId) that would allow me to create context with correct connection string?
Is this possible?
I find the easiest way is to include the tenant id in a custom HTTP header.
Because the tenant id changes during the session, you probably want to create a custom Breeze ajax adapter (wrap the one you're using now) that sets this header dynamically during its implementation of the ajax method.
On the server you fish the header out of the request.
MAKE SURE YOU ARE VALIDATING USER AND HEADER ON THE SERVER

Different registration forms for different roles. FOSUserBundle

I'm absolutely new of Symfony, and I'm trying to implement a registration form that works only with invitation
but that can redirect two different forms for two different roles.
In practice if I send an invitation for an USER_TYPE1 role the client can only register like USER_TYPE1, if I send an invitation for an USER_TYPE2 the client can only register like USER_TYPE2 (and, of course, assigns the corrispondent role).
Is it possible?
thank you in advance for your help
UPDATE:
I want two different form because one user will be allowed to update file, but will also have to set his position and other important settings. The second user will only allow to download the files uploaded by the first kind of user, and his profile needs completely different information.
I do not have enough reputation to ask for details, but one thing that is not clear in your question is: why do you need 2 different forms? In your question, you mention 2 different roles, but why do you need 2 different forms? If you really need 2 different forms, then you should first:
- create a new form type
- create a new view (twig)
Like Boris suggested, I would keep some kind of token for every invitation sent, and associate an email address, and a role to it. Then modify your registration route so you can pass a token in there, like this:
register:
pattern: /signup/{token}
defaults: { _controller: MyBundle:Registration:signup }
In the registration action of your controller, you created the correct form type and display the appropriate twig, depending on the ROLE associated to the token you just got. And when handling a POST, you check the Token again to see if it matches the email address, and assign the proper ROLE when creating the User.
public function signupAction($token) {
// 1. Get the Token entity matching the $token variable
// 2. Create the correct form type
// 3. Display the correct twig for GET, assign correct ROLE to new User for POST
}
But you can't use FOSUserBundle as-is. You will have to overwrite the registration process. You can read the FOSUserBundle documentation about that.
What's certain is that, for every invitation you send, you should keep a token with a matching email address and ROLE (the role you want to give to that person).

API call to retrieve all public information of Facebook user?

I know I can call each individual field via something like
/---ID----?fields=gender,languages,age_range,bio,birthday,education,email,hometown
Is there a quicker way than listing every single field?
It is ok if the user has most of these fields locked down with their privacy settings, I just want it to pull what is available.
just try:
https://graph.facebook.com/{{ username or uid }}?access_token={{access token goes here}}
Example:
https://graph.facebook.com/zuck?access_token=###
will give all the public information available for that user.