feat(common-oauth): RelyingParty skeleton with discover_as and do_par
Implement Task 1 from Phase 7 Subcomponent A:
- Add rand_chacha 0.3 and rand_core 0.6 dependencies (compatible with p256)
- Add serde_urlencoded 0.7 for form-encoded request bodies
- Create src/common/oauth/relying_party.rs with:
- RelyingParty struct with deterministic ES256 key generation from seeded RNG
- ClientKind enum (Confidential, Public)
- ParRequest, ParResponse, AsDescriptor, TokenResponse, AuthorizeOutcome types
- RpError enum with Diagnostic derives and stable error codes
- RelyingParty::new() with deterministic PKCS8 DER key generation
- discover_as() to fetch OAuth metadata from .well-known endpoint
- do_par() to perform Pushed Authorization Request with DPoP proofs
- Internal helpers: new_pkce(), new_jti(), sign_dpop(), sign_private_key_jwt()
- Unit tests for PKCE determinism, JTI uniqueness, and DPoP signature verification
Key implementation details:
- Deterministic ES256 keys from ChaCha20Rng seeded with explicit 32-byte seed
- PKCE S256: SHA-256 hash of 32 random bytes, base64url-nopad encoded
- DPoP proofs manually constructed with custom header including JWK
- Private_key_jwt client assertions for confidential clients
- JTI is 16 bytes base64url-nopad; KID is first 8 bytes of seed base64url-nopad
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>