Add RS256 signing support to common/jwt library
- Dominant language
- Python
- Stars
- 670
- Forks
- 183
- Avg merge
- 17h 7m
- Merged PRs (30d)
- 358
Description
## Summary
Extend the existing `common/jwt` library to support **RS256 (asymmetric) signing** alongside the current HS256, and add **JWKS fetching/caching** utilities — foundational for the OAuth2/OIDC Provider work.
**Parent issue:** #10500 (Phase 1, Step 1)
## Motivation
The current `common/jwt` library only supports HS256 with per-user symmetric secrets. The OAuth2/OIDC Provider (Account Manager) needs RS256 asymmetric signing so that:
- Account Manager signs tokens with a private key
- Manager (and any resource server) validates tokens using the public key from a JWKS endpoint
- No shared secrets between services — only public keys distributed
## Scope
### Must have
- `JWTConfig`: Add RS256 algorithm support, RSA key path configuration
- `JWTSigner`: Sign JWTs with RSA private key (RS256)
- `JWTValidator`: Validate JWTs with RSA public key (RS256)
- `JWTClaims`: Add `kid` (key ID) field for key rotation
- JWKS utilities: Parse JWKS JSON, fetch from URL with caching, key lookup by `kid`
- RSA key pair generation utility (for development/testing)
- All existing HS256 functionality unchanged (backward compatible)
- Full test coverage for RS256 sign/verify, JWKS parsing, key rotation, error cases
### Out of scope
- Account Manager endpoints (separate issue)
- Manager middleware changes (separate issue)
- OAuth2 scope definitions (separate issue)
## Key Files
| File | Change |
|---|---|
| `src/ai/backend/common/jwt/config.py` | Add RS256 algorithm, key path fields |
| `src/ai/backend/common/jwt/signer.py` | RS256 signing with RSA private key |
| `src/ai/backend/common/jwt/validator.py` | RS256 validation with RSA public key |
| `src/ai/backend/common/jwt/types.py` | Add `kid` to JWTClaims |
| New: `src/ai/backend/common/jwt/jwks.py` | JWKS fetching, caching, key lookup |
| New: `src/ai/backend/common/jwt/keys.py` | RSA key pair generation, loading, serialization |
| `src/ai/backend/common/jwt/exceptions.py` | Add JWKS-related exceptions if needed |
| `src/ai/backend/common/jwt/__init__.py` | Export new public API |
| Tests | RS256 round-trip, JWKS parsing, key rotation, HS256 backward compat |
## Acceptance Criteria
- [ ] `JWTSigner.generate_token()` can sign with RS256 using an RSA private key
- [ ] `JWTValidator.validate_token()` can validate RS256 tokens using an RSA public key
- [ ] JWKS endpoint response can be parsed and keys looked up by `kid`
- [ ] JWKS fetcher caches keys with configurable TTL and background refresh
- [ ] RSA key pair can be generated programmatically (for dev/test)
- [ ] `kid` header is included in RS256-signed JWTs
- [ ] All existing HS256 tests continue to pass unchanged
- [ ] `pants lint/check/test` pass with no new warnings
Contributor guide
Assessment
This issue has not been assessed yet.