Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question - Magic Link Continuation not working when password option available #110

Open
ulevitsky opened this issue Feb 28, 2025 · 6 comments
Assignees

Comments

@ulevitsky
Copy link

ulevitsky commented Feb 28, 2025

Observed with quay.io/phasetwo/phasetwo-keycloak:26.1.2 image and docker compose file from https://github.com/p2-inc/phasetwo-containers/blob/main/examples/postgres/docker-compose.yml, with minimal additional configuration:

  1. Created new realm "test".
  2. In the new realm, created a OIDC client "test" with default settings and * for Valid redirect URIs, Valid post logout redirect URIs, Web origins.
  3. In realm settings, under Email, configured SMTP.
  4. Created a new user with a valid email and a password.

I then initiate authentication flow by opening http://localhost:8080/realms/test/protocol/openid-connect/auth?client_id=test&response_type=code&redirect_uri=https://example.com in browser.

Overview

I'm implementing an authentication flow that offers users two options:

  1. Password login (if they have a password set up)
  2. Magic link continuation.

For reference, this is the flow I came up with.

Image

Observed behavior

When both authentication methods are available:

  • The login page correctly shows password first for users who have one
  • Users can click "Try another way" to see all options (password or magic link)
  • Bug?: if magic link is selected, the form reverts to the password prompt after 5 seconds. Then even after clicking the magic link in the email, nothing happens

However, when magic link is the only available option (user has no password or password form is disabled), everything works correctly:

  • The form duly polls the /login-actions/authenticate endpoint repeatedly
  • The authentication completes when the email link is clicked

Expected behavior

The magic link continuation flow should work regardless of whether the password option is available.

Questions

  • Is there a configuration setting I'm missing?
  • Should the polling behavior be consistent regardless of authentication options?
  • Is this a known limitation or a potential bug?

Any advice would be greatly appreciated!

@xgp
Copy link
Member

xgp commented Feb 28, 2025

@rtufisi can you please take a look?

@rtufisi
Copy link
Collaborator

rtufisi commented Mar 3, 2025

I will investigate it.

@rtufisi
Copy link
Collaborator

rtufisi commented Mar 4, 2025

Hello @ulevitsky . I think this is a limitation of the 'Magic link continuation authenticator' since the pooling is done by refreshing the authenticator tab. If you have a flow configuration like the following:

Image

And select the alternative 'Authenticator Application' and refresh the page, you will be redirected to the 'Password' flow. I will investigate other options for the pooling behaviour

@pnzrr
Copy link
Contributor

pnzrr commented Mar 4, 2025

The page has a built in 5s timeout that refreshes the page, which likely puts them back at the beginning. The beginning page won't then poll to know if the link was clicked in another view.

If you refresh that password page after the link is clicked, do you then log in?

@ulevitsky
Copy link
Author

ulevitsky commented Mar 5, 2025

@pnzrr thank you for your suggestion, and to @rtufisi for your continued investigation!

Unfortunately, no, if I reload the page after the link is clicked, it returns to the beginning of the flow (i.e. the password prompt).

I experimented with a possible quick and dirty solution by attempting to replace this section of code with a fetch request -- something along these lines:

<@layout.registrationLayout displayRequiredFields=false displayMessage=false; section>
 <#if section = "header">
    <div id="kc-username" class="${properties.kcFormGroupClass!}">
      <label id="kc-attempted-username">${auth.attemptedUsername}</label>
      <a id="reset-login" href="${url.loginRestartFlowUrl}" aria-label="${msg("restartLoginTooltip")}">
        <div class="kc-login-tooltip">
          <i class="${properties.kcResetFlowIcon!}"></i>
          <span class="kc-tooltip-text">${msg("restartLoginTooltip")}</span>
        </div>
      </a>
    </div>
  <#elseif section = "form">
    ${msg("magicLinkContinuationConfirmation")}
  </#if>
  <script>
     (function (w, d) {
        function checkAuthStatus() {
            fetch(w.location.href, {
                method: 'GET',
                headers: {
                    'X-Requested-With': 'XMLHttpRequest',
                    'Pragma': 'no-cache',
                    'Cache-Control': 'no-cache, no-store'
                },
                cache: 'no-store',
            })
            .then(response => {                
                console.log('[Magic Link Debug] Didn't get redirected -- meaning authentication not confirmed yet');
                setTimeout(checkAuthStatus, 5000);
            })
            .catch(error => {
                console.error('[Magic Link Debug] Error checking auth status:', error);
                setTimeout(checkAuthStatus, 5000);
            });
        }
        
        // Start polling after a delay
        setTimeout(checkAuthStatus, 5000);
     })(window, document);
  </script>
</@layout.registrationLayout>

If magic link continuation is the only option, this is more or less functionally equivalent to current code in main branch, although weirdly after the link is clicked, the server may return one or two HTTP 500 before eventually returning a 302.

However if password is also enabled, the backend never returns 302 after the link is clicked, instead continuing to return HTTP 200, which suggests there is something happening on the server side too -- but that's way beyond my skill level.

@rtufisi
Copy link
Collaborator

rtufisi commented Mar 5, 2025

Thanks @ulevitsky . I think the 500 you are experiencing is related to this issue #111

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants