MFA-authenticated users now remain signed in across browser restarts, matching the session lifetime of password-authenticated users. ## Closes - [CW-7898](https://linear.app/chatwoot/issue/CW-7898/mfa-users-are-logged-out-when-the-browser-session-ends) ## How to reproduce 1. Enable MFA for a user. 2. Sign in and complete OTP or backup-code verification. 3. End the browser session, then reopen the browser and return to Chatwoot. 4. Before this change, the user is sent back to the login page. ## Root cause and evidence Before this change, successful MFA verification wrote `cw_d_session_info` directly through `document.cookie` without an `Expires` or `Max-Age` attribute. This makes it a session cookie: - [MFA cookie creation on the base commit](a4eae9710a/app/javascript/dashboard/components/auth/MfaVerification.vue (L70-L87)) - [MDN session-cookie behavior](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie) - [Chromium cookie persistence documentation](https://chromium.googlesource.com/chromium/src/+/master/net/cookies/README.md) Password login already uses `setAuthCredentials`, which derives a persistent cookie expiry from the authentication response: - [Existing credential helper](a4eae9710a/app/javascript/dashboard/store/utils/api.js (L28-L36)) - [Configured two-month token lifetime](a4eae9710a/config/initializers/devise_token_auth.rb (L8-L10)) This change routes successful MFA verification through the same credential helper and existing verified event. The regression test asserts that MFA credentials use this shared persistence path. Session-cookie restoration is browser and profile dependent. Chromium may persist session cookies when restoring a previous browsing session, so the problem can be masked on some restarts. This PR does not rely on a desktop-versus-mobile distinction. ## How to verify 1. On `develop`, complete an MFA login. 2. Inspect `cw_d_session_info` in browser developer tools. Its expiry is shown as `Session`. 3. Repeat on this branch. 4. Confirm that `cw_d_session_info` has a concrete expiry derived from the authentication response instead of `Session`.