3

I would like to implement this authentication flow in Keycloak:

  1. A user creates an account by typing only his email
  2. The user is logged in and can access my service 2'. At the same time, an email is sent to him, allowing him to "finalize" his account
  3. The user leaves his session -> to reuse my service, he must click in the received email
  4. By clicking in the received email, the user defines his first password
  5. The user is then logged in automatically (without going through a login page).

The objective of this flow is to be the simplest, to hook users who are not used to webapps.

The implementation I would do:

  • Create an account without password request: I customize the Keycloak Registration flow by disabling the Password Validation and Profile Validation rules
  • Programmatically, in my webapp, at the first connection of a user, via the REST Admin API, I trigger the email action UPDATE_PASSWORD

I get something that works, but:

A. The link received by email redirects to an intermediary page confirming the execution of actions ("Perform the following action (s)") - (similar to Keycloak Implement Reset password flow same as forgot password flow)

B. The user is then redirected to a login page, and not directly connected to the application.

When, as a normal user, I trigger a reset password request (through 'forget password' feature), the process is the one I want: by clicking on the email link, I go directly to the page allowing me to enter and confirm a new password, then I'm authenticated.

My question: Do you see a way to implement this 'simplified' flow?

My keycloak version : 11.0.2

Thank you !

John Smith
  • 41
  • 1
  • 4

2 Answers2

3

I could remove the "info.ftl" page display, customizing the "ExecuteActionsActionTokenHandler", as explained here :

action-token-spi

You have to create a file :

src/main/resources/META-INF/services/org.keycloak.authentication.actiontoken.ActionTokenHandlerFactory

containing the name of the class you want to use instead :

com.example.ExecuteActionTokenHandlerFactory

Then you create that class com.example.ExecuteActionTokenHandlerFactory with the following code :

public class ExecuteActionTokenHandlerFactory extends ExecuteActionsActionTokenHandler {


    @Override
    public Response handleToken(ExecuteActionsActionToken token, ActionTokenContext<ExecuteActionsActionToken> tokenContext) {
        AuthenticationSessionModel authSession = tokenContext.getAuthenticationSession();
        String redirectUri = RedirectUtils.verifyRedirectUri(tokenContext.getUriInfo(), token.getRedirectUri(),
                tokenContext.getRealm(), authSession.getClient());

        if (redirectUri != null) {
            authSession.setAuthNote(AuthenticationManager.SET_REDIRECT_URI_AFTER_REQUIRED_ACTIONS, "true");

            authSession.setRedirectUri(redirectUri);
            authSession.setClientNote(OIDCLoginProtocol.REDIRECT_URI_PARAM, redirectUri);
        }

        token.getRequiredActions().stream().forEach(authSession::addRequiredAction);

        UserModel user = tokenContext.getAuthenticationSession().getAuthenticatedUser();
        // verify user email as we know it is valid as this entry point would never have gotten here.
        user.setEmailVerified(true);

        String nextAction = AuthenticationManager.nextRequiredAction(tokenContext.getSession(), authSession, tokenContext.getClientConnection(), tokenContext.getRequest(), tokenContext.getUriInfo(), tokenContext.getEvent());
        return AuthenticationManager.redirectToRequiredActions(tokenContext.getSession(), tokenContext.getRealm(), authSession, tokenContext.getUriInfo(), nextAction);
    }

}

Actually it is the same implementation as the upper class, except we removed the following part :

        if (tokenContext.isAuthenticationSessionFresh()) {
           ...
        }

which means that if the user did not have a session, which happens when the user is reseting his password, he is redirected to that "info.ftl" page.

nico
  • 460
  • 3
  • 7
0

As a workaround for problem A, I customize info.ftl template page. I add an ugly inline script to click on the link, redirecting automatically to the update password page.

<#import "template.ftl" as layout>
(...)
            <#elseif actionUri?has_content>
              <p><a id="yolo" href="${actionUri}">${kcSanitize(msg("proceedWithAction"))?no_esc}</a></p>
              <script>document.getElementById('yolo').click()</script>
(...)

It'll do the job until I found a cleaner solution.

At the moment, B problem remains.

John Smith
  • 41
  • 1
  • 4