Test your login flow before launch
What breaks login flows before launch, ranked — and how to verify the whole loop: you sign in, the session sticks, locked-out users recover.
Login is the flow every returning user hits, every time, forever — and it’s the gateway to everything else your app does. If signup is the front door a stranger walks through once, login is the door your real users open every day. When it breaks, it doesn’t break quietly for one person: it locks out everyone who already has an account. And it tends to break in production specifically, because the things that make login work — session cookies, redirect targets, OAuth callback URLs — are exactly the things that change between localhost and a real domain.
The safe way to test a login flow before launch is to test the whole loop: sign in, confirm you land in the right place, and confirm the session sticks on the next click. A login request that returns a 200 and flashes “welcome back” tells you almost nothing — the failures that actually lock users out live in the session and redirect steps that come after the credential is accepted. This piece is the list of what breaks, ranked, and how to verify each one. In a June 2026 audit of 49 fresh web launches, 38 (78%) shipped with at least one critical verified finding on day one. (That audit walks public pages, so it can’t tell you how many login flows specifically broke — but it tells you launch-day code ships broken far more often than founders expect.)
Why static checkers and a quick self-test miss this
A static audit confirms the login page exists, returns 200, and posts over HTTPS. It doesn’t submit a credential, follow the redirect, or check whether the session it just created survives the next page load. Login is a multi-step flow with server-side state, and flow testing is a different problem from page testing.
Testing it casually misses the same way. You log in from your own browser — the
one that’s already logged in, with a warm session cookie and your password
saved — so you never exercise the cold path a real user hits: a fresh browser
with no cookies, on the production domain, where a misconfigured SameSite or a
missing Secure flag means the session is silently dropped. The good news is the
same as with signup flows,
checkout flows, and
password reset flows: the
surface is small and bounded. A handful of things break logins. You can know all
of them.
The things that break login flows
In rough order of how often they show up in founder reports and in our own audits:
-
Login succeeds, but the session doesn’t persist. You sign in, land on the dashboard, click one link — and you’re back at the login page. Why it happens: the session cookie was never set, or it was set without the attributes production needs — missing
Secureon HTTPS, aSameSitevalue that blocks the cookie on the redirect, or the wrong domain or path — so the browser quietly discards it. Or the login endpoint returns a token the client never stores. Or the server-side session row was never created. Verify it: log in on the live site in a clean browser profile, then navigate to an authenticated page and refresh. If you stay logged in, the session persists. If you bounce to login, it doesn’t. (In devtools → Application → Cookies, confirm your session cookie is actually present,Secure, andHttpOnly.) -
The redirect after login is wrong, or it loops. The credential is accepted, but you land in the wrong place — the generic homepage, a 404, or a ping-pong loop between the login page and the page that required login. Why it happens: the post-login redirect target is hardcoded to dev, or the auth guard and the login page disagree about whether you’re authenticated, so each bounces you to the other. Verify it: open a deep link that requires auth (say,
/settings), get redirected to login, sign in — and confirm you land back on/settings, not on a generic home screen and not in a loop. -
“Remember me” doesn’t, or the session dies mid-task. You check “remember me” and you’re logged out an hour later anyway; or even mid-session, the app logs you out while you’re still using it. Why it happens: the cookie’s
Max-Ageor the token’s TTL is misconfigured, or “remember me” is a checkbox wired to nothing. Verify it: log in with “remember me” checked, fully close the browser, reopen, and return to the site — you should still be in. Then use the app for a few minutes and confirm it doesn’t drop you. -
Legit users get locked out by rate-limiting or account lockout. The brute-force protection you (or your host, or your auth provider) added locks out real users: the launch-day spike of people signing in trips an IP rate limit, a shared corporate network gets one IP throttled for everyone, or a few fat-fingered attempts lock an account with no self-serve way back. Why it happens: aggressive lockout thresholds with no unlock path, or rate limits tuned for an attacker and never tested against a crowd. Verify it: enter a wrong password several times in a row and confirm a real user can still recover — a CAPTCHA or a cooldown is fine; a permanent lock with no reset link is a launch-day support fire.
-
OAuth (“Sign in with Google”) breaks in production. The button works in dev and does nothing — or errors — on the live site. Why it happens: the callback redirect URI is allow-listed for
localhostbut not your production domain, or the OAuth client is still in “testing” mode and only your own account can use it, or the consent screen was never published. Verify it: actually click “Sign in with Google” on the production domain, signed into a Google account that isn’t yours, and confirm it completes — don’t assume the button working in dev means the production OAuth app is configured. -
The diagnostic one: login itself is the wall. If login is broken or sits behind a CAPTCHA, an external automated tester can’t reach anything behind it — and the right behavior is to report “couldn’t sign in,” not to fake a pass. That’s not a bug in the test; it’s the single most important thing a tester can tell you, because if sign-in fails, nothing downstream can be checked at all. (We learned this distinction the hard way on a production app behind reCAPTCHA Enterprise — the full investigation is in why AI agents fail at login.)
How Prufa walks a login flow
Prufa runs the same pattern here it runs everywhere: an LLM does the navigation, plain code does the verification. The LLM reads the page like a user — finds the email and password fields, the “Sign in” button — and plain code, against captured browser evidence, verifies what actually happened: the session was established and an authenticated page rendered, not just that the login request returned 200. The LLM never grades the result, so its blind spots can’t become the verifier’s blind spots. (The full walkthrough is in How Prufa verifies a signup flow; a login is the same machinery pointed at the gateway flow.)
Login is the one flow where the tester’s own design is the interesting part, because to test anything behind auth, the tester has to log in — which means it has to hold a credential. On a domain you’ve authorized, Prufa keeps that credential in an encrypted vault: Fernet-encrypted at rest, never written to a log, masked in the UI, with deliberately no insecure fallback — if the encryption key is missing, the API returns a clear error rather than storing a credential in the clear. And it never lets the credential near the model: the LLM supplies the selector (“type into this field”), but plain code types the actual value at the browser boundary, outside the LLM context. Your password never enters the prompt, the tool output, the progress events, or the logs — the same LLM-navigates / code-verifies split, applied to the one place it matters most.
When sign-in doesn’t complete, Prufa records “The tester could not sign in” as a first-class finding the report leads with, rather than burying it under downstream noise — because a real user with those credentials would be blocked from reaching the product, and that’s the story. And it draws an honest line between two kinds of failure: a genuinely broken app (an assertion that should hold after login and doesn’t) is the one thing that alerts you; a wrong or changed credential is treated as something to ask you to re-save, not a 3am page. A credential problem is your problem to fix quietly, not an outage.
What the free 60-second audit does for login
The free audit on prufa.dev opens your entry URL on a real browser, finds the login page, and runs the deterministic checks on its markup — is the form well-formed, does it post over HTTPS, are the fields labelled. No signup, no card, public pages only — so it audits the login page, but it can’t complete the authenticated loop, because that needs a credential it doesn’t have. You can run the free login audit on your entry URL in about a minute to clear the page-level checks first.
Completing the whole loop automatically — signing in, confirming the session persists, and testing what’s behind it — is the credential-backed path. Starter ($29/mo) re-verifies your core flows — signup, login, checkout — daily and on every deploy, and alerts you the moment one breaks, which matters because a login that works today can stop working the day you rotate a secret, change your cookie domain, or your OAuth provider deprecates an endpoint — exactly the kind of silent rot that hits an app after launch. You can also automate the login check from the CLI and wire it into CI so it runs on every push, not just on a schedule. Pro ($99/mo) adds the encrypted credential vault and deeper logged-in journeys across staging and production.
For the rest of the pre-launch surface — sitemaps, meta tags, broken links, console errors — see Website QA checklist before launch. Login is one of the flows that list points at and static checkers don’t walk.
What the audit doesn’t do (honest limits)
A functional check proves login completes: a valid credential gets in, the session persists, the redirect lands right. It does not replace a security review of the auth system — whether passwords are hashed correctly, whether the session is vulnerable to fixation, whether the login form is CSRF-protected, whether tokens are validated and brute-force attempts are throttled. Those are an OWASP authentication-testing job — pair the functional check with one, the same way you’d pair a flow walk with a secret scanner for exposed keys. The anonymous free audit also can’t log in (no credential) and can’t pass a CAPTCHA. And it can’t test that your OAuth provider is up — that’s Google’s or GitHub’s uptime, not yours — though a broken callback configuration absolutely is yours, and a walk on the production domain catches it.
What to do before you launch
In order of impact:
- Sign in on the live site, then refresh an authenticated page. Use a clean
browser profile with no saved cookies. If you stay logged in, your session
persists. If you bounce to login, your cookie config is wrong — check
Secure,SameSite, and the cookie domain in devtools. - Log in from a deep link. Open a page that requires auth, get sent to login, sign in — and confirm you land back on that page, not a generic home and not a redirect loop.
- Test “remember me” across a browser restart. Check it, close the browser completely, reopen, and return — you should still be in.
- Click “Sign in with Google” on the production domain, on a phone. Use an account that isn’t yours. Test-mode OAuth apps and unlisted callback URIs only fail for other people.
- Fat-finger the password a few times. Confirm a real user who mistypes can still recover — a cooldown or CAPTCHA is fine; a permanent lock with no reset path is a launch-day support fire.
Get those five right and you’ve closed the failures that turn “Sign in” — the door every returning user opens — into a locked door on launch day.
Frequently asked questions
How do I test my login flow before launch?
Test the whole loop, not just the sign-in button. Log in with a real test account, then confirm four things: you land on the right page, a refresh keeps you logged in, the session behaves as you intend after a browser restart, and a few wrong passwords don't lock a real user out for good. Login returning a success response tells you almost nothing — the failures live in the session and redirect steps after it.
Why does my login work locally but not in production?
Almost always the session cookie or a baked-in URL. Locally the cookie is set on localhost over HTTP, so missing Secure or SameSite attributes don't bite; in production over HTTPS on a real domain, a misconfigured cookie silently isn't stored and you bounce back to login. The other classic is OAuth: the callback URI is allow-listed for localhost but not your production domain. Sign in on the deployed site to catch both.
Can an automated tool test a login flow?
Yes. With an authorized test credential, an external tool can walk the whole loop on a real browser — submit the credentials, confirm it reaches an authenticated state, and verify the session persists on the next navigation, not just that the login request returned 200. What it can't replace is a security review of the auth system itself: password hashing, session fixation, CSRF on the login form, and brute-force hardening are a security scanner's job, not a functional walk's.
What should a login flow do when it works correctly?
Five things: accept a valid credential and reject an invalid one clearly, establish a session that survives navigation and a refresh, send the user to the right place afterward (not a generic homepage or a redirect loop), honor 'remember me' if you offer it, and give a locked-out user a way back in. If any is missing — a session that won't stick, a redirect loop, a lockout with no recovery — a correct password still can't get you in.