feat(07.3-02): ES256 JWT algorithm upgrade + startup rotation hook
- config.py: add refresh_token_expire_hours=16, jwt_private_key, jwt_public_key fields (D-01, D-09) - services/auth.py: swap all 4 JWT sites to ES256 via base64-decoded PEM keys; remove HS256 (D-02, D-03) - main.py: add _rotate_tokens_on_algorithm_change lifespan hook — bulk-revokes refresh tokens on algorithm change; idempotent on repeat boots (D-04, D-05) - test_auth_es256.py: promote ES256-01..05 + CFG-01 stubs to 6 passing tests; RM-01..03 remain xfail - docker-compose.yml: inject JWT_PRIVATE_KEY + JWT_PUBLIC_KEY into backend + celery-worker (D-07) - README.md: add JWT key env vars + key generation Python one-liner snippet - .env.example: add JWT_PRIVATE_KEY= and JWT_PUBLIC_KEY= lines - Version bump to 0.1.2
This commit is contained in:
@@ -140,7 +140,9 @@ Copy `.env.example` to `.env`. Only the fields marked **Required** must be set b
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `SECRET_KEY` | JWT signing secret — generate with `openssl rand -hex 32` |
|
||||
| `SECRET_KEY` | Legacy HMAC secret (kept for future use) — generate with `openssl rand -hex 32` |
|
||||
| `JWT_PRIVATE_KEY` | base64-encoded PEM PKCS8 private key for ES256 JWT signing (required) |
|
||||
| `JWT_PUBLIC_KEY` | base64-encoded PEM SubjectPublicKeyInfo public key for ES256 JWT verification (required) |
|
||||
| `CLOUD_CREDS_KEY` | Master key for cloud credential encryption — generate with `openssl rand -hex 16` (pad to 32 chars) |
|
||||
| `ADMIN_EMAIL` | Bootstrap admin email |
|
||||
| `ADMIN_PASSWORD` | Bootstrap admin password (must pass strength check) |
|
||||
@@ -159,6 +161,29 @@ Copy `.env.example` to `.env`. Only the fields marked **Required** must be set b
|
||||
| `GOOGLE_CLIENT_ID/SECRET` | *(unset)* | Required only if using Google Drive backend |
|
||||
| `ONEDRIVE_CLIENT_ID/SECRET` | *(unset)* | Required only if using OneDrive backend |
|
||||
|
||||
### JWT Key Generation
|
||||
|
||||
Phase 7.3 uses ES256 (ECDSA P-256) asymmetric signing. The private key signs tokens; the public key verifies them. A leaked public key cannot forge tokens.
|
||||
|
||||
Generate the keypair with a single Python command:
|
||||
|
||||
```bash
|
||||
python3 -c "
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
import base64
|
||||
k = ec.generate_private_key(ec.SECP256R1())
|
||||
priv = base64.b64encode(k.private_bytes(serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8, serialization.NoEncryption())).decode()
|
||||
pub = base64.b64encode(k.public_key().public_bytes(serialization.Encoding.PEM, serialization.PublicFormat.SubjectPublicKeyInfo)).decode()
|
||||
print(f'JWT_PRIVATE_KEY={priv}')
|
||||
print(f'JWT_PUBLIC_KEY={pub}')
|
||||
"
|
||||
```
|
||||
|
||||
Paste the two output lines into your `.env` file at the project root.
|
||||
|
||||
> **Warning:** Rotating these keys invalidates every active session — the startup rotation hook will bulk-revoke all refresh tokens on the next boot.
|
||||
|
||||
---
|
||||
|
||||
## Development
|
||||
|
||||
Reference in New Issue
Block a user