Authentication & Security Guide
A practical reference for building secure authentication with this library.
JWT Best Practices
Token Lifetime
| Use case | Recommended expiry |
|---|---|
| Access token | 15–30 minutes |
| Refresh token | 7–14 days |
| Password reset | 10–15 minutes |
| Email verify | 24 hours |
Short-lived access tokens limit the blast radius if a token is leaked.
Pair them with a longer-lived refresh token stored in an httpOnly cookie.
Algorithm Selection
- HS256 — Symmetric; shared secret between issuer and verifier. Simple but the secret must never leave the server.
- RS256 — Asymmetric; sign with a private key, verify with a public key. Preferred when multiple services need to verify tokens independently.
# Asymmetric example
jwt = JWTHandler(
secret=PRIVATE_KEY_PEM,
algorithm="RS256",
expiry_minutes=15,
)
Claims Checklist
Always include:
-
sub— Subject (user ID or username) -
iat— Issued at -
exp— Expiration -
jti— Unique token ID (enables revocation)
Avoid storing sensitive data (passwords, PII) in the payload — JWTs are
signed, not encrypted.
OAuth 2.0 Flows
Authorization Code (with PKCE)
Recommended for all web and mobile apps.
┌─────────┐ ┌──────────┐ ┌──────────────┐
│ Client │──(1)──▶│ Auth │──(2)──▶│ User logs │
│ App │◀──(4)──│ Server │◀──(3)──│ in & grants │
└────┬─────┘ └──────────┘ └──────────────┘
│ (5) Exchange code + verifier
▼
┌──────────┐
│ Token │
│ Endpoint│ returns access_token + refresh_token
└──────────┘
- Client generates
code_verifierandcode_challenge(S256). - User is redirected to the authorization URL with the challenge.
- User authenticates and grants consent.
- Authorization server redirects back with a
code. - Client exchanges
code+code_verifierfor tokens.
PKCE prevents authorization code interception — always enable it.
Client Credentials
For server-to-server communication where no user is involved:
tokens = await oauth_client.client_credentials_grant()
Password Security
Hashing Algorithms (in order of preference)
- Argon2id — Memory-hard; best resistance to GPU/ASIC attacks.
- bcrypt — Battle-tested; good default if Argon2 is unavailable.
- PBKDF2-SHA256 — Available everywhere; use high iteration count (600k+).
hasher = PasswordHasher(algorithm="argon2")
hashed = hasher.hash("user-password")
ok = hasher.verify("user-password", hashed)
Password Strength Rules
Enforce at registration time:
- Minimum 10 characters
- At least one uppercase, one lowercase, one digit
- Reject passwords found in common breach lists (haveibeenpwned)
- Never store plaintext passwords — anywhere
Session Security
Redis-Backed Sessions
This library uses server-side sessions stored in Redis:
- Sessions are referenced by an opaque, random ID (not the JWT).
- Set a TTL on every session key (e.g. 30 minutes of inactivity).
- Rotate the session ID after login to prevent session fixation.
session = SessionManager(redis_url="redis://localhost:6379", ttl=1800)
await session.create(user_id="alice", data={"role": "admin"})
Cookie Flags
When sending the session ID as a cookie:
| Flag | Value | Why |
|---|---|---|
| HttpOnly | true |
Prevents JavaScript access (XSS) |
| Secure | true |
Only sent over HTTPS |
| SameSite | Lax |
Mitigates CSRF |
| Path | / |
Scope to entire app |
| Max-Age | 1800 |
Matches server-side TTL |
Multi-Factor Authentication (MFA)
TOTP Setup Flow
- Generate a secret:
mfa.generate_secret() - Create a provisioning URI and display as QR code.
- User scans with an authenticator app (Google Authenticator, Authy).
- User submits a code to verify setup — store the secret only after success.
Recovery Codes
Generate 8–10 single-use recovery codes at MFA enrollment:
codes = mfa.generate_recovery_codes(count=10)
# Store hashed versions; show plaintext to the user once
If a user loses their authenticator, they can use a recovery code to regain
access and re-enroll MFA.
Common Vulnerabilities & Mitigations
1. JWT alg: none Attack
Risk: Attacker removes the signature and sets algorithm to none.
Mitigation: Always enforce the expected algorithm in JWTHandler:
jwt = JWTHandler(secret=KEY, algorithm="HS256") # Rejects "none"
2. Token Leakage
Risk: Tokens in URL query strings appear in server logs and referrer headers.
Mitigation: Transmit tokens in the Authorization header or httpOnly cookies.
3. Insecure Redirect URI
Risk: Open redirect allows an attacker to steal the authorization code.
Mitigation: Validate redirect_uri against a strict allow-list server-side.
4. CSRF on OAuth Callback
Risk: Attacker initiates an OAuth flow and injects their code into the victim's session.
Mitigation: Use the state parameter — a random, per-request value verified on callback.
5. Privilege Escalation
Risk: User modifies their JWT claims to elevate their role.
Mitigation: Verify the JWT signature server-side; never trust client-supplied roles.
Security Headers
Add these headers to every HTTP response:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Content-Security-Policy: default-src 'self'
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Checklist Before Going to Production
- [ ] Rotate JWT signing keys periodically
- [ ] Enable PKCE on all OAuth2 flows
- [ ] Use Argon2id for password hashing
- [ ] Set short access-token lifetimes (15–30 min)
- [ ] Store refresh tokens in httpOnly cookies
- [ ] Enforce MFA for privileged accounts
- [ ] Validate all redirect URIs against an allow-list
- [ ] Log authentication events (login, failure, token refresh)
- [ ] Rate-limit login and token endpoints
- [ ] Run dependency audits (
pip-audit,safety)
By Datanest Digital — OAuth Auth Library Security Guide
This is 1 of 14 resources in the Python Developer Pro toolkit. Get the complete [OAuth & Auth Library] with all files, templates, and documentation for $39.
Or grab the entire Python Developer Pro bundle (14 products) for $159 — save 30%.
Top comments (0)