Single QR Code for Authentication

Hello,

I am trying to implement the following QR code authentication system.

First, I want to show to the user a QR code that contains the link, thus the ActionUrl, to which he must make a POST request. Note that this is a Browser connection flow, so the QR code is shown on the browser.

Authenticate Method Authenticator


    @Override
    public void authenticate(AuthenticationFlowContext context) {
        try {
            context.challenge(context.form().createForm("qr-login.ftl"));
        } catch (Exception e) {
            context.failureChallenge(AuthenticationFlowError.INTERNAL_ERROR,
                    context.form().setError("Error", e.getMessage())
                            .createErrorPage(Response.Status.INTERNAL_SERVER_ERROR));
        }
    }

View

<#import "template.ftl" as layout>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>

<style>
    .center-container {
        display: flex;
        justify-content: center;
        align-items: center;
    }
</style>

<@layout.registrationLayout displayInfo=true; section>
    <#if section = "header">
        ${msg("AuthTitle")}
    <#elseif section = "form">
        <form id="kc-template-code-login-form" class="${properties.kcFormClass!}" action="${url.loginAction}"
              method="post">
            <div class="${properties.kcFormGroupClass!}">
                <div class="center-container">
                    <div id="qrcode"></div>
                </div>
            </div>
        </form>
    <#elseif section = "info" >
        ${msg("AuthInstruction")}
    </#if>
</@layout.registrationLayout>

<script>
    let qrcode = new QRCode("qrcode", "${url.loginAction}");
</script>

When the User will Scan the QR Code with his smartphone, it will send a POST request to the url.loginAction*. This request contains the “username” of the user. Then in the action method, I want to get the “username” parameter of this POST request and log the user that has the given username.

Action Method Authenticator

    @Override
    public void action(AuthenticationFlowContext context) {
        // Get the user.
        String enteredName= context.getHttpRequest().getDecodedFormParameters().getFirst("username");
        context.clearUser();

        try {
            UserModel user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), enteredName);
            if (user == null) {
                throw new ModelDuplicateException("User not found");
            }
            context.setUser(user);
        } catch (ModelDuplicateException e) {
            context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS,
                    context.form()
                            .setError("UserError", e.getMessage())
                            .createForm("qr-login.ftl"));
        }
        context.success();
    }

The problem I face is that I don’t know how to trigger the action method by sending this POST request, thus allowing the browser to authenticate the user from the scanning of the QR code. I’m not sure about the fact that I even need to trigger the Action method, so help would be really appreciated.

Do you have any idea or resources on the steps I need to take to implement this “reaction” from the Authenticator when he receives the POST request ?

I’m not sure you can “trigger” an authenticator as you want. Keycloak does some session setup when you start an authentication flow that makes an assumption about how you enter (using a GET), and then allows data to be POSTed to forms.

As a modification to your idea, why not create a magic link with this extension GitHub - p2-inc/keycloak-magic-link: Magic Link Authentication for Keycloak and then just encode the magic link as a QR code? That will use the action token handler mechanism built into Keycloak, and will work without the need for a custom authenticator.

Thanks for your answer.

Unfortunately, I don’t think this will be enough for the final use case we want to make. The goal would be to be able to send a credential in JSON format, that the Authenticator checks against a database and if it is valide, it authenticates the user. The problem is that this credential is sent through a mobile device that scans the QR code.

This is something that would looks like WhatsApp Web or Discord with QR authentication.

Gotcha. Well, take a look at how Action Tokens work in Keycloak, as they’re just JWTs that encode information as JSON. You may be able to use how the magic link extension works as inspiration.

1 Like

Great :smile: I had seen this, but wasn’t sure if it was a “proper way” to do things for my use case. I’ll look into the Action Tokens, thanks a lot!