feat(auth): implement JWT-based principal authentication#46
feat(auth): implement JWT-based principal authentication#46zeroasterisk wants to merge 7 commits into
Conversation
- Add A2A.Plug.JWTVerifier for JWT token verification with HMAC/RSA support - Add A2A.Plug.Auth for credential extraction and authentication middleware - Add AgentmsgElixirWeb.A2AController with JWT verification for Phoenix apps - Derive stable principal IDs from JWT claims instead of defaulting to 'test' - Support configurable JWT verification with JWKS, issuer, audience validation - Include comprehensive documentation and examples for JWT auth configuration
TCK 1.0-dev Compatibility Results (experimental)
|
The create_jwt_token/2 helper had a quadruple-backslash (\\\\) where Elixir default-argument syntax needs a double-backslash (\\), so the file failed to parse. This broke 'mix format --check-formatted' (Quality job) and made all four Test matrix jobs fail to compile. Verified locally: mix format --check-formatted exit 0; mix test = 512 tests + 2 doctests, 0 failures.
Formatting-only (line wrapping to 100 chars, comment placement, trailing whitespace). No logic change. Satisfies the Quality job mix format gate. Verified: mix format --check-formatted exit 0; mix test = 512 tests + 2 doctests, 0 failures.
|
See if https://hex.pm/packages/joken could be used instead of rolling your own JWT verification. |
Replaces hand-rolled HMAC/base64/constant-time-compare crypto in A2A.Plug.JWTVerifier with Joken (per maintainer suggestion on actioncard#46). - Signature verification via Joken.Signer + Joken.verify (HS256) - Header/algorithm + claim validation kept local for descriptive errors - Module now guarded on both :plug and :joken (optional deps) - jose pinned <1.11.11 (1.11.11+ needs OTP 26 dynamic(); CI targets OTP 25) Public API (new/1, verify/2) and all 18 existing tests unchanged.
|
Done — swapped the hand-rolled JWT crypto for Joken in Two notes worth flagging:
|
RFC 7519 NumericDate MAY contain a fractional component. Widen the exp/nbf guards from is_integer to is_number so float timestamps are honored instead of rejected as malformed. Also corrects the moduledoc feature list (dropped the unvalidated 'iat' claim).
Adds alg:none / alg:None rejection, empty-signature, stripped-signature, and wrong-secret cases, plus fractional-exp acceptance, non-numeric-exp rejection, and audience-as-list match/mismatch.
The example claimed JWKS-based verification and carried jwks_url/cache_ttl config keys the verifier never reads, producing a non-functional secret:nil verifier. Switch the example to a working HS256 shared-secret config and guard the module on Joken (with an accurate fallback message).
|
Did a thorough review/hardening pass on top of the Joken refactor (3 follow-up commits, all CI green across the 1.17/1.18 × OTP 25/27 matrix): Security — added explicit tests proving forged tokens are rejected: Correctness — Honesty fixes — the moduledoc feature list claimed an Net: signature verification fully delegated to Joken (no hand-rolled crypto), HS256-only and the docs now say so, +9 tests. Happy to split any of these out if you'd rather they land separately. |
Implements inbound credential verification to derive stable principal/User ID instead of defaulting unknown senders to 'test'.
Changes
Key Features
Security
Resolves the requirement to implement inbound credential verification for stable principal identification.