Contents

Chapter 1

Authentication & Security Guide

A practical reference for building secure authentication with this library.


JWT Best Practices

Token Lifetime

Use caseRecommended expiry
Access token15–30 minutes
Refresh token7–14 days
Password reset10–15 minutes
Email verify24 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.

python
# 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
 └──────────┘

1. Client generates code_verifier and code_challenge (S256).

2. User is redirected to the authorization URL with the challenge.

3. User authenticates and grants consent.

4. Authorization server redirects back with a code.

5. Client exchanges code + code_verifier for tokens.

PKCE prevents authorization code interception — always enable it.

Client Credentials

For server-to-server communication where no user is involved:

python
tokens = await oauth_client.client_credentials_grant()

Password Security

Hashing Algorithms (in order of preference)

1. Argon2id — Memory-hard; best resistance to GPU/ASIC attacks.

2. bcrypt — Battle-tested; good default if Argon2 is unavailable.

3. PBKDF2-SHA256 — Available everywhere; use high iteration count (600k+).

python
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.
python
session = SessionManager(redis_url="redis://localhost:6379", ttl=1800)
await session.create(user_id="alice", data={"role": "admin"})

When sending the session ID as a cookie:

FlagValueWhy
HttpOnlytruePrevents JavaScript access (XSS)
SecuretrueOnly sent over HTTPS
SameSiteLaxMitigates CSRF
Path/Scope to entire app
Max-Age1800Matches server-side TTL

Multi-Factor Authentication (MFA)

TOTP Setup Flow

1. Generate a secret: mfa.generate_secret()

2. Create a provisioning URI and display as QR code.

3. User scans with an authenticator app (Google Authenticator, Authy).

4. 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:

python
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:

python
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*

OAuth & Auth Library v1.0.0 — Free Preview