Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .changeset/jwks-es256-auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
"@smooai/smooth-operator": minor
---

feat(auth): JWKS-based JWT verification (ES256 + any algorithm, with rotation) for `smoo`/`jwt` modes

The auth verifier could only validate tokens against a **static RS256 PEM**
(`AUTH_JWT_RS256_PUBLIC_KEY`). SmooAI's `auth.smoo.ai` (the `smoo` issuer) signs
dashboard tokens with **ES256** (`/.well-known/jwks.json` → `alg: ES256, kty: EC`),
so every real SmooAI token was rejected — blocking `AUTH_MODE=smoo` for the SmooAI
K8s flavor.

This adds a JWKS-backed verification path (additive, behavior-preserving):

- New optional `AUTH_JWT_JWKS_URL`, and auto-derivation of
`{AUTH_JWT_ISSUER}/.well-known/jwks.json` when an issuer is set and no static
key is given.
- Keys are fetched, **cached** (TTL) and **rotation-aware** (refresh-on-unknown-`kid`),
selected per-token by `kid`, and validated with the key's algorithm via
`DecodingKey::from_jwk` — so **any** advertised JWS algorithm works
(ES256/ES384/RS256/PS256/EdDSA/…), not just RS256.
- Wired into both `SmooIdentityVerifier` (the `smoo` path) and `JwtVerifier`
(BYO), so any OIDC issuer works. `AuthVerifier::verify` stays **synchronous**
(the keyset is read from cache; the network fetch is off the hot path).

Key-source precedence (`jwt`/`smoo`): static `AUTH_JWT_RS256_PUBLIC_KEY` →
static `AUTH_JWT_HS256_SECRET` → JWKS (`AUTH_JWT_JWKS_URL`, else issuer-derived).
The static-RS256/HS256 paths are unchanged. With this, `AUTH_MODE=smoo` needs
only `AUTH_JWT_ISSUER` (+ optional audience) — no static public key.
1 change: 1 addition & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion rust/smooth-operator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ hmac = { workspace = true }
sha2 = { workspace = true }
hex = { workspace = true }
# `fetch_url` built-in tool: HTTP GET + URL parsing for the SSRF guard.
reqwest = { workspace = true }
# `blocking` adds the JWKS fetcher's synchronous HTTPS GET (run on a dedicated
# thread so `AuthVerifier::verify` can stay sync — see `auth::HttpJwksFetcher`).
reqwest = { workspace = true, features = ["blocking"] }
url = { workspace = true }
# `github_search` tool: live GitHub code/issue search via the GitHub API.
octocrab = { workspace = true }
Expand Down
Loading
Loading