MFA prompt still shown to the user even after selecting "Remember browser"

When a user logs into the system with MFA enabled and toggles the “Remember this device for 30 days” checkbox, I expect the MFA prompt to be skipped the next time the user logs into the app from the same browser. However, the MFA prompt is still being shown.

Setup :

In Security → MFA Auth:

  • One-time Password (Google Authenticator) : enabled
    Other factors are disabled
  • Require Multi-factor Auth: Never
  • Customize MFA Actors using Actions: disabled (toggling this didn’t affect the result)

Post-login Action:

exports.onExecutePostLogin = async (event, api) => {
   const userMeta = event.user.user_metadata || {};

    if (userMeta.use_mfa) {
        api.multifactor.enable("any", { allowRememberBrowser: true });
    }
};

We set { use_mfa: true } to user_metadata for users that need MFA enabled.

Steps to reproduce:

  • Log out from the app
  • Enter username/password
  • Check the “Remember this device for 30 days” option, then enter the OTP code from Google Authenticator
  • Log out from the app
  • Enter username/password again

Expected:

  • The user should be redirected to the web app and should not need to enter MFA again, since the “Remember this device for 30 days” option was selected previously in the Auth0 Guardian form.

Actual:

  • The user needs to enter MFA again, though the “Remember this device for 30 days” option is not present this time.

What’s interesting is that after logging out and logging in a second time, the MFA prompt appears, but now the “Remember this device for 30 days” option is visible again.

Please help me understand why MFA is not being skipped when logging in from the same browser. Let me know if you need any additional information.

Hi @viktor_i

Welcome to the Auth0 Community!

Thank you for posting your question here!

I have tested your setup and everything appears to be working as expected on my end.

For an user which does not have MFA setup, they are asked to enroll with OTP ( please keep in mind that on the enrollment screen, the prompt to Remember the browser for 30 days is not available.

When the user logs in for the 2nd time, they are prompted for MFA where they have the option to remember the browser. After ticking that and logging in for the 3rd time, the user is logged in directly and not prompted for MFA.

The issue might be tied to some configuration on your tenant since I was not able to reproduce it on my end.

If you have any other questions, feel free to leave a reply!

Kind Regards,
Nik

Thanks for checking on your end.

However, I do not see anything unusual in the Tenant and Application configurations. Could you please mention some specific settings that needs to be checked? Is it possible that the frontend React app logic somehow influences this?

Also, is there any way to debug this (check Network/Cookies etc.)? Based on docs everything should work correctly as is, however after checking “Remember browser” the MFA just re-appear again every time.

Hi again,

In general, your setup should work as expected. Perhaps your application is clearing the cookies set for MFA.

Could you please answer the following questions:

  • Could you let me know if you are enforcing mfa by any chance in your application?

  • Does your application use refresh tokens/refresh token rotation?

  • What is the expiration of your tokens set to?

  • Where do you store the session of the user? Do you clear it or the cache set?

  • Do any of your functions like isAuthenticated() or checkSession() return false and prompt the user to log in?

Kind Regards,
Nik

Hi, let me try to provide more insights here:

Could you let me know if you are enforcing mfa by any chance in your application?

I do not see any code / configurations that does this

Does your application use refresh tokens/refresh token rotation?

yes

What is the expiration of your tokens set to?

ID Token Expiration: 36000s
Set Idle Refresh Token Lifetime - enabled
Idle Refresh Token Lifetime - 1296000s
Set Maximum Refresh Token Lifetime - 2592000s
Allow Refresh Token Rotation - disabled

Where do you store the session of the user? Do you clear it or the cache set?

the JWT token is stored in local storage with key ‘CLIENT_JWT’

Do any of your functions like isAuthenticated() or checkSession() return false and prompt the user to log in?

not sure how to answer this

Here should be all of the specific configuration parts:

<Auth0Provider
	domain={process.env.REACT_APP_AUTH_DOMAIN}
	clientId={process.env.REACT_APP_AUTH_CLIENT_ID}
	redirectUri={`${window.location.origin}/home`}
	audience={audience}
	scope={requestedScopes.join(" ")}
	useRefreshTokens
	cacheLocation="localstorage"
	onRedirectCallback={redirectCallBack}
>

const requestedScopes = [
  "read:users",
  "read:user",
  "edit:user",
  "read:current_user",
  "openid",
  "offline_access",
  "enroll",
];

import { useAuth0 } from "@auth0/auth0-react";
useAuth0() 

localStorage.getItem(CLIENT_JWT) //  token stored here

// In Axios interceptor:

const token = isAuthenticated ? await getAccessTokenSilently() : "";
const oldToken = localStorage.getItem(CLIENT_JWT);
if (token && oldToken !== token) {
  const tokenType = oldToken == null ? "ACCESS" : "REFRESH";
  localStorage.setItem(CLIENT_JWT, token);
  const event = new StorageEvent("storage", {
    key: CLIENT_JWT,
    newValue: token,
    oldValue: oldToken,
    storageArea: localStorage,
    url: window.location.href,
  });
  window.dispatchEvent(event);
}


// Logout:
logout({ returnTo: returnTo });
localStorage.removeItem(CLIENT_JWT);

Thank you for looking into this case, hope this will help to understand the scenario

Hi @viktor_i

Thank you for providing that info.

I believe that the refresh tokens might be causing the issue at hand.

Since a new refresh token might be provided to the user whenever they log in, it is triggering the mfa.

You could try adding the following code to your PostLogin action to bypass it for the users:

exports.onExecutePostLogin = async (event, api) => {
  // This action will allow you to bypass the MFA logic for the refresh token exchange flow.

  if (event.transaction.protocol === "oauth2-refresh-token") {
    return;
  }

  //  Add your MFA logic
  //  For example: api.multifactor.enable("any");
};

Let me know if that helps!

Kind Regards,
Nik

I’ve tried the code you’ve suggested but nothing changed, unfortunately:

exports.onExecutePostLogin = async (event, api) => {
    if (event.transaction.protocol === "oauth2-refresh-token") {
        return;
    }

    const userMeta = event.user.user_metadata || {};

    if (userMeta.use_mfa) {
        api.multifactor.enable("any", { allowRememberBrowser: true });
    }
};

My observations:

  • If I comment out this action altogether // api.multifactor.enable("any", { allowRememberBrowser: true }); the user with already configured MFA is prompted MFA each time and does not see “Remember browser” checkmark
  • if this api.multifactor.enable("any", { allowRememberBrowser: true }); is present the user still prompted MFA each time but the checkmark “Remember browser” is visible every other time

Hi again!

Sorry for the delayed response.

Could you try the following and let me know if there are any changes in the behaviour?

    const userMeta = event.user.user_metadata || {};

 if (event.transaction.protocol === "oauth2-refresh-token") {
    return;
  }
  else if (event.connection.strategy === "auth0") {
    if (userMeta.use_mfa) {
        api.multifactor.enable("any", { allowRememberBrowser: true });
    }
}

Kind Regards,
Nik

Hi, the behaviour does not change, the checkbox is shown every second time. I’ve added some logging and see the following output:

console.log("Protocol: " + event.transaction.protocol);
console.log("Strategy: " + event.connection.strategy);
console.log("Meta use_mfa: " + userMeta.use_mfa);


Action Logs:

...
18:44:06: Protocol: oidc-basic-profile
18:44:06: Strategy: auth0
Meta use_mfa: true
18:44:36: Protocol: oidc-basic-profile
18:44:36: Strategy: auth0
Meta use_mfa: true
...

Hi,

Could you check if your tenant’s session policy is set to Non-Persistent?
The mentioned setting can be found at Auth0 Dashboard → Settings → Advanced → scroll down → Session Expiration → Session Policy.

Kind Regards,
Nik

It’s set to Persistent

Thanks for checking that.

Could you please send me a DM with the tenant name that you are experiencing this on so I can take a look?

Kind Regards,
Nik

1 Like

Hi @viktor_i

I have taken a look at your tenant and everything seems to be fine regarding the settings, actions and applications. Would you kindly create a new application and attempt to log in using the /authorize call specific to it to see if the users are prompted for MFA after ticking the remember box in order to see if the issue persists there as well? You can also try using a sample application to test that out to.

I believe that your application or browser might be clearing the cookies set by the MFA. What browser are you using and are you using incognito by any chance?
Does this happens on a different browser?

Kind Regards,
Nik

Ok, I see, thank you for checking. By creating new application you mean that I should create new SPA app with new client id / secret and use it with the same frontend implementation? Could you please clarify what is the sample application?

I’ve checked both Chrome and Safari in the normal mode.

I’ll try to reproduce it with some minimal setup when I can.

Hi, a sample application is an github repository available for download for any application type you have on Auth0 depending on the technology/languages used. I would like you to test a sample application to see if the same behaviour happens there (which might mean the issue is tenant/auth0 app settings related or not)

I would advise to try either of the following, if you can try all it would be perfect:

  • Create a new SPA, replace the client ID and secret with your implementation and attempt to log in to see if the same issue persists
  • Create a new SPA, open a browser window and make an authorize call to your application using the credentials:
EXAMPLE OF AN /authorize CALL

https://{{your_auth0_domain}}/authorize?
  response_type=code
  client_id={{app_client_ID}}&
  redirect_uri=https://{{your_auth0_domain}}/callback
  • Create a SPA application, go to Quickstarts, select any available quickstart ( Preferably JS or React), run the application, attempt to log in

You do not need to create multiple applications, just one should be enough and use the credentials to test all of the above.

Kind Regards,
Nik

1 Like