WhollySoftware
Back to blogWeb

Authentication Patterns for Modern Web Apps: Sessions, JWTs, and Passkeys

Wholly Software TeamDecember 1, 20257 min read
Authentication Patterns for Modern Web Apps: Sessions, JWTs, and Passkeys

For a long stretch, our default for new web app builds was JWT-based auth, mostly because it fit cleanly with stateless API servers and didn't require a shared session store across multiple backend instances. That default got questioned on a project where a client needed to force-logout a compromised user account immediately, and discovered that a signed JWT with a 24-hour expiry can't actually be revoked without a denylist — which reintroduces the server-side state you adopted JWTs to avoid.

For that client we moved to server-side sessions backed by Redis, with a session ID in an httpOnly, secure, sameSite cookie. Revocation became a single Redis delete instead of maintaining a growing denylist. The trade-off is a database round trip on every authenticated request instead of a self-contained token, which we mitigated by keeping session lookups fast with Redis and accepting the added latency — sub-5ms in practice — as worth the security property.

JWTs still make sense in specific cases: service-to-service auth where there's no user session to revoke mid-flight, or a mobile app talking to multiple independently-scaled API services where a shared session store adds a coordination problem. We use short-lived access tokens (15 minutes) paired with a refresh token rotation scheme there, which limits the blast radius of a leaked token without needing a denylist.

Passkeys have become our default recommendation for new consumer-facing signups since WebAuthn support solidified across browsers. We implemented passkey login on a client's account portal using SimpleWebAuthn on the backend, and support tickets related to forgotten passwords — previously one of the top three ticket categories — dropped by more than half within two months of rollout, since most returning users authenticate with Face ID or Windows Hello instead of typing a password.

We still keep a password-plus-email fallback for users on older devices or browsers without WebAuthn support, and account recovery remains the hardest part of any passkey implementation — losing your only enrolled device with no backup method is a real support burden we've had to design around, not an edge case we could ignore.

AuthenticationSecurityPasskeysJWT
Authentication Patterns for Modern Web Apps: Sessions, JWTs, and Passkeys — Wholly Software