lablup / lablup/backend.ai

Add RS256 signing support to common/jwt library

Open
#10,898 0 comments 0 reactions 0 assignees View on GitHub
type:feature
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.