Authentication Best Practices for Apps in 2026
The authentication best practices that matter most in 2026 are short: offer passkeys or MFA, follow NIST's current password rules (long passwords, no forced rotation, no composition rules, check against breached lists), keep access tokens short-lived with rotating refresh tokens, rate limit every sign-in path, and never let a privileged key reach a client. Everything below is a checklist you can work through in an afternoon, with the exact setting to change if you use a hosted auth service like StitchDB.
Each item has three parts: the rule, why it matters, and how to apply it.
#Passwords
#1. Require long passwords, not complex ones
Rule. NIST SP 800-63B revision 4 says passwords used as the only factor shall be at least 15 characters, and at least 8 when used with MFA. It says services shall not impose other composition rules (such as "one upper case letter and one symbol") and should allow at least 64 characters (NIST SP 800-63B).
Why. Composition rules produce Password1!. Length produces passphrases.
How. On StitchDB, set the minimum length (6 to 72, default 6) and the required characters in Authentication › Settings (passwords). Raise the default: 15 if passwords stand alone, at least 8 if you require MFA. Set required characters to none.
#2. Never force periodic password changes
Rule. NIST says services shall not require periodic changes, but shall force one when there is evidence of compromise.
Why. Forced rotation makes people pick Summer2026, then Autumn2026.
How. Do not build expiry. Build a good reset flow instead, and reset on evidence (a breach report, suspicious sign-ins).
#3. Refuse breached passwords
Rule. Check new passwords against a blocklist of known-compromised values.
How. Turn on Prevent use of leaked passwords under Authentication › Settings › Bot and abuse protection. Passwords found in public breaches are refused at sign-up and password change with 422 weak_password. Only the first characters of the password's SHA-1 hash leave the server (k-anonymity) (bot protection).
#4. Hash with a slow algorithm, compare in constant time
If you build auth yourself, store passwords with a slow, salted password hash and use your framework's constant-time comparison. The OWASP Authentication Cheat Sheet also warns against silently truncating long passwords. A hosted auth service does this for you, which is one of the best reasons to use one.
#Stronger factors
#5. Offer MFA, and require it where it counts
Rule. OWASP calls MFA "by far the best defense against the majority of password-related attacks".
How. Let every user enroll an authenticator app, and require it for admin or money features in the database, not just in the UI:
const { data: f } = await stitchdb.auth.mfa.enroll({ factorType: 'totp', friendlyName: 'My phone' })
// show f.totp.qr_code, then verify the first code
const { data: c } = await stitchdb.auth.mfa.challenge({ factorId: f.id })
await stitchdb.auth.mfa.verify({ factorId: f.id, challengeId: c.id, code: '123456' })create policy "MFA required" on public.payouts
as restrictive for all to authenticated
using ((auth.jwt() ->> 'aal') = 'aal2');See MFA. Prefer TOTP over SMS as a second factor; SMS is better than nothing but exposed to SIM swaps.
#6. Add passkeys
Passkeys (WebAuthn) are phishing-resistant: the browser only uses a passkey on the domain it was created for. Turn them on under Authentication › Sign-in methods › Passkeys and set the relying party domain if your app does not run on the Site URL (passkeys). Offer them next to your existing method first; do not force a switch.
#7. Give business customers SSO
Companies want their staff to sign in with their own identity provider so they can remove access in one place. StitchDB supports SAML 2.0 SSO with Okta, Microsoft Entra ID, Google Workspace and others, one provider per customer domain.
#Sessions and tokens
#8. Keep access tokens short, rotate refresh tokens
Why. Signing out revokes refresh tokens, but access tokens already issued stay valid until they expire. The shorter they live, the smaller the window.
How. StitchDB defaults are sensible: access tokens last 3,600 seconds (adjustable from 5 minutes to 7 days), refresh tokens rotate on every use with a 10-second reuse interval. For sensitive apps, also set Maximum session length and Inactivity timeout; both default to never (sessions). If one session per user is a requirement, turn on Single session per user.
#9. Let users sign out everywhere
await stitchdb.auth.signOut() // every session of the user (default)
await stitchdb.auth.signOut({ scope: 'others' }) // every other deviceShow this in account settings. Do it automatically after a password change.
#10. Store tokens where they belong
In browsers, the SDK keeps the session in localStorage by default; keep your Content Security Policy tight, because any script on your page can read it. On iOS the Swift SDK uses the keychain. On React Native, pass a storage such as AsyncStorage (or a secure store). Never log tokens.
#11. Never ship privileged keys
The service_role key bypasses Row Level Security and can manage users. It belongs on servers, in Edge Functions and in jobs, never in a bundle with a NEXT_PUBLIC_ or EXPO_PUBLIC_ prefix. If one leaks, rotate it.
#Abuse and bots
#12. Rate limit every auth path
Rule. Limit sign-ins, sign-ups, code requests and verifications per IP, and failed attempts per account.
How. StitchDB applies per-IP limits to sign-ins and sign-ups (per plan), and defaults of 30 per 5 minutes per IP for verifications and for magic link and code requests (auth limits). Tighter rules for specific endpoints go in API › Rate limits, for example 5 sign-ins a minute per IP on the token path (rate limits). For per-account lockouts, the password verification attempt hook lets you write your own rule. OWASP warns that lockouts can be abused to lock real users out, so pair them with a working reset flow.
#13. Stop fake sign-ups
Bots sign up to abuse free tiers and to use your form to send e-mail. Every StitchDB project refuses throwaway e-mail domains, limits sign-ups per IP (10 an hour) and per network, and asks scripted clients to solve a small proof-of-work check that the SDK solves automatically (fake sign-up protection). Add hCaptcha or Turnstile on top if you are a target.
#14. Return the same error for every failure
"Invalid e-mail or password" for wrong e-mail, wrong password and locked account alike. The same for "if an account exists, we sent a link". Different messages, status codes or response times let attackers list your users.
#Flows people forget
#15. Allowlist redirect URLs
Every e-mail link and OAuth sign-in ends with a redirect. If the target is not checked, an attacker can send users to their own site with a valid session in the URL. StitchDB only redirects to the Site URL and up to 100 allowed Redirect URLs, with wildcards (redirect URLs). Set the Site URL to production before launch; new projects default to http://localhost:3000. Use the PKCE flow for server-rendered apps so codes, not tokens, appear in URLs.
#16. Re-authenticate before sensitive changes
OWASP recommends asking for current credentials before changing account details. Turn on Require reauthentication to change password, and keep Secure e-mail change on so both old and new addresses must confirm.
#17. Make account recovery as strong as sign-in
A strong login with a weak reset is a weak login. Send reset links only to verified addresses, expire them, and sign the user out of other sessions after a reset.
#18. Offer account deletion
If your iOS app lets people create an account, Apple's guideline 5.1.1(v) requires that they can delete it from inside the app. GDPR gives EU users a right to erasure too. Build it once, for every platform. With on delete cascade on your user_id columns, deleting the user removes their rows.
#19. Watch what happens
Send auth events to your systems. StitchDB webhooks can deliver auth.user.created, auth.user.signed_in, auth.user.updated and auth.user.deleted to your endpoint, signed and retried, which is enough to alert on unusual sign-in volume or new admin accounts.
#The one-page version
| Area | Do this |
|---|---|
| Passwords | 15+ characters alone, 8+ with MFA; no composition rules; no rotation; breached-password check |
| Factors | MFA for everyone, required for sensitive data; passkeys offered; SSO for companies |
| Tokens | Short access tokens, rotating refresh tokens, sign out everywhere |
| Keys | Privileged keys only on servers |
| Abuse | Rate limits, fake sign-up protection, CAPTCHA if targeted, generic errors |
| Flows | Redirect allowlist, PKCE, re-auth for changes, strong recovery, in-app deletion |
| Visibility | Auth events to webhooks or logs |
Authentication is only half of security. Once you know who the user is, every request still needs a permission check; see authentication vs authorization and the Postgres security checklist.
#FAQ
#What is the most important authentication best practice?
Offer MFA or passkeys. OWASP calls MFA the best defense against most password-related attacks, such as reused or stolen passwords.
#Should I still require special characters in passwords?
No. Current NIST guidance says not to impose composition rules. Require length and check against breached passwords instead.
#How long should an access token last?
Minutes to an hour. One hour is a common default. Use refresh tokens to keep users signed in.
#Is it safe to build my own auth?
It is possible, but every item on this list becomes your code to write and maintain. Most teams are better served by a library or a hosted service and spend the time on authorization.
Turn these settings on in one place: create a free StitchDB project and open Authentication › Settings.