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

5.4k Views Asked by At

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

3

There are 3 best solutions below

3
On

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.

0
On

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.

0
On

Take a look at keycloak two factor email authenticator provider https://github.com/mesutpiskin/keycloak-2fa-email-authenticator