Define the states
This comes down to a handful of concrete details. Work through the list below and flag the ones you have not covered yet.
- Signed out: show sign-in and create-account actions.
- Submitting: prevent duplicate credentials requests.
- Signed in but unverified: explain the verification step.
- Session loading: avoid redirecting before the session is hydrated.
- Expired session: return the user to a safe sign-in route.
OAuth redirect rules
Send OAuth callbacks to a same-origin callback route, not directly to a protected screen. Store the intended destination separately and navigate there after the session is available. This avoids losing the session or redirecting before the app knows who is signed in.
- Configure the provider and exact redirect origins.
- Start the OAuth flow from a clear user action.
- Handle the callback and wait for session hydration.
- Show a useful error for cancelled or rejected consent.
- Test refresh, sign-out and a second account.
Password reset
Use a dedicated reset route, accept the token only through the auth flow and show a confirmation after the new password is saved. Do not reveal whether an email address exists in a way that enables account enumeration.
Rollout checklist
Use the list below as a working checklist. Each item is something you can verify or action directly on your own site.
- Test sign-up, sign-in, sign-out and refresh in a private browser window with no prior session.
- Confirm the redirect origins registered with the provider match the deployed domain exactly, including protocol and any preview subdomain.
- Confirm an unverified account cannot reach protected content by URL alone.
- Confirm a password reset link expires and cannot be reused after the password changes.
- Confirm signing out clears the session everywhere the app checks for it, not just in one component.
Diagnosing a broken sign-in
Here are the key things to get right. None of them are complicated on their own, but skipping one tends to undermine the rest.
- Blank screen after OAuth redirect: the callback route is missing or the app is redirecting before the session resolves.
- Signed in on refresh but treated as signed out immediately: session hydration is being read before it completes.
- Google sign-in works locally but fails in production: the production redirect origin is not registered with the provider.
- Reset link opens the app signed out with no error: the token is being dropped before the auth flow reads it.