A practical reference for building secure authentication with this library.
| 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.
but the secret must never leave the server.
Preferred when multiple services need to verify tokens independently.
# Asymmetric example
jwt = JWTHandler(
secret=PRIVATE_KEY_PEM,
algorithm="RS256",
expiry_minutes=15,
)Always include:
sub — Subject (user ID or username)iat — Issued atexp — Expirationjti — Unique token ID (enables revocation)Avoid storing sensitive data (passwords, PII) in the payload — JWTs are
signed, not encrypted.
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.
For server-to-server communication where no user is involved:
tokens = await oauth_client.client_credentials_grant()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+).
hasher = PasswordHasher(algorithm="argon2")
hashed = hasher.hash("user-password")
ok = hasher.verify("user-password", hashed)Enforce at registration time:
This library uses server-side sessions stored in Redis:
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:
| 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 |
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.
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 onceIf a user loses their authenticator, they can use a recovery code to regain
access and re-enroll MFA.
alg: none AttackRisk: 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"Risk: Tokens in URL query strings appear in server logs and referrer headers.
Mitigation: Transmit tokens in the Authorization header or httpOnly cookies.
Risk: Open redirect allows an attacker to steal the authorization code.
Mitigation: Validate redirect_uri against a strict allow-list server-side.
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.
Risk: User modifies their JWT claims to elevate their role.
Mitigation: Verify the JWT signature server-side; never trust client-supplied roles.
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
pip-audit, safety)*By Datanest Digital — OAuth Auth Library Security Guide*