Why this module exists. Modern authentication is not “username + password + check the DB.” It is a stack of OAuth flows, token handling, cookie discipline, MFA orchestration. This module covers what works.
Password handling — when you must store one
- Argon2id is the current default for password hashing. PBKDF2 acceptable; bcrypt acceptable; scrypt OK. MD5/SHA1/SHA256 alone are never acceptable for password storage.
- Salt per password, generated cryptographically random per user.
- Tunable cost — Argon2id parameters tuned so a single hash takes ~100ms on production hardware. Re-tune annually as hardware improves.
- Never log passwords, even in failure messages. Centralised auth-error logging needs sanitisation.
Session cookies — the modern attributes
Set-Cookie: session=abc123;
HttpOnly; # No JavaScript access
Secure; # HTTPS only
SameSite=Lax; # CSRF protection (Strict for highest sensitivity)
Path=/;
Max-Age=86400 # 24 hours; refresh on activity
JWT — the right way
- Pin the algorithm at validation:
jwt.decode(token, key, algorithms=["RS256"]). Never accept what the token claims. - Use asymmetric (RS256, ES256) for federated; symmetric (HS256) only for tokens that never leave one trust boundary.
- Short expiry (minutes-hours for access tokens, days for refresh).
- Don’t put sensitive data in JWT payload — it is base64, not encrypted.
- Have a revocation strategy. Short expiry + refresh token rotation is the pragmatic answer.
OAuth 2.0 / OIDC — pick the right flow
| Use case | Flow |
|---|---|
| Server-side web app | Authorisation Code + PKCE |
| SPA | Authorisation Code + PKCE (NOT implicit) |
| Mobile app | Authorisation Code + PKCE |
| Service-to-service | Client Credentials |
| Device with limited input | Device Authorization Grant |
Implicit flow is deprecated. Password Grant is deprecated. If a library default is one of these, override.
MFA — modern enforcement
- Phishing-resistant MFA (passkeys, FIDO2 security keys) for high-privilege accounts.
- TOTP (Google Authenticator etc.) for general workforce — better than SMS-OTP.
- SMS-OTP only as a fallback; not as a primary factor for any sensitive account.
- Step-up authentication for high-risk actions — re-auth required for password change, payment authorisation, admin operations.
Account lockout vs rate-limiting
- Account lockout (lock after N failed attempts) creates a denial-of-service vector — attacker can lock any user out.
- Rate-limiting per IP + per account, with exponential backoff, is the modern pattern. Combined with CAPTCHA at threshold.
Password reset — the often-vulnerable flow
- Reset tokens: cryptographically random, 30-60 minute expiry, single-use, bound to the user account.
- Reset emails: must go to the email on file, not user-provided in the request.
- Reset confirmation: notify user via separate channel when password was changed.
- “Forgot password” enumeration: respond identically whether the email exists or not (timing also).
Common failure modes
- JWT signature verification disabled (alg=none accepted).
- Session cookies without Secure or HttpOnly attributes.
- Password reset tokens too long-lived or reused.
- SMS-OTP used as primary MFA on high-value accounts.
- OAuth state parameter missing (CSRF on authorization callback).
Key takeaways
- Password storage: Argon2id with per-user salt and tuned cost.
- Session cookies: HttpOnly + Secure + SameSite + bounded lifetime.
- JWT: pin the algorithm, asymmetric for federated, short expiry, revocation strategy.
- OAuth: Authorisation Code + PKCE for almost everything; deprecate Implicit and Password Grant.
- MFA: phishing-resistant for high-privilege; TOTP for general; SMS-OTP only as fallback.
- Rate-limiting + CAPTCHA, not account lockout.
Get a VAPT scoping call
Senior practitioner-led VAPT — not a checklist run by juniors. CVSS-scored findings, free retest, attestation letter. India's SMBs and SaaS teams.