From 53aa96e2d41c534c966c655af7ebcd49a1a54973 Mon Sep 17 00:00:00 2001 From: Nicolas Dreno Date: Thu, 1 Oct 2026 15:15:35 +0200 Subject: [PATCH] docs: make the jwt-auth and auth examples valid configs Nine jwt-auth examples used keys the plugin's schema has never had (`required`, `scopes`, `header`, `scheme`, `secret`, `public_key`), so copying one failed compilation with E1023. Seven more set only `issuer`: jwt-auth verifies every token against `public_key_jwk`, so without it they rejected all requests. The jwt-auth guide said signature validation was not implemented, told readers to set `skip_signature_validation: true` in production (the compiled plugin ignores it) and listed HS256 as supported (rejected). It now shows `public_key_jwk`, the algorithms each key type verifies, the alg/use binding, and points to oidc-auth for JWKS. The extensions reference shows a real jwt-auth config. Examples whose point is an authenticated operation use oidc-auth (`issuer_url`, `audience`, `required_scopes` for the scope examples, `allow_query_token` on the WebSocket route), and operation-level ones carry the `security` requirement E1057 asks for; the complete example declares `bearerAuth`. The secrets guide's file-based examples use oauth2-auth's `client_secret` and s3's `secret_access_key`. An oidc-auth example with `issuer`/`required` and two oauth2-auth examples missing required keys are fixed too. Every auth and s3 config in the docs now validates against its schema; the two remaining keyless jwt-auth mentions are name-only chain illustrations. Signed-off-by: Nicolas Dreno --- docs/guide/ai-gateway.md | 7 +++-- docs/guide/dispatchers.md | 13 +++++--- docs/guide/middlewares/authentication.md | 26 +++++++++------- docs/guide/middlewares/authorization.md | 16 +++++----- docs/guide/middlewares/index.md | 13 ++++---- docs/guide/secrets.md | 22 ++++++++------ docs/guide/spec-configuration.md | 38 ++++++++++++++++++------ docs/reference/cli.md | 2 ++ docs/reference/extensions.md | 21 ++++++++----- 9 files changed, 105 insertions(+), 53 deletions(-) diff --git a/docs/guide/ai-gateway.md b/docs/guide/ai-gateway.md index bc02c863..756df7c5 100644 --- a/docs/guide/ai-gateway.md +++ b/docs/guide/ai-gateway.md @@ -53,10 +53,13 @@ The dispatcher owns *provider routing* and *catalog policy*. Layer middlewares o paths: /v1/chat/completions: post: + security: + - bearerAuth: [] x-barbacane-middlewares: - - name: jwt-auth + - name: oidc-auth config: - issuer: "https://auth.example.com" + issuer_url: "https://auth.example.com" + audience: "my-api" # Per-tier model gating using request body + claims (cel body_json) - name: cel diff --git a/docs/guide/dispatchers.md b/docs/guide/dispatchers.md index cbb8346e..0adea78a 100644 --- a/docs/guide/dispatchers.md +++ b/docs/guide/dispatchers.md @@ -632,12 +632,13 @@ paths: parameters: - { name: bucket, in: path, required: true, schema: { type: string } } - { name: key, in: path, required: true, allowReserved: true, schema: { type: string } } + security: + - bearerAuth: [] x-barbacane-middlewares: - name: oidc-auth config: - issuer: https://auth.example.com + issuer_url: https://auth.example.com audience: my-api - required: true x-barbacane-dispatch: name: s3 config: @@ -1197,10 +1198,14 @@ Client: GET /ws/echo?token=abc → Upstream: ws://echo.internal:8080/?token=abc required: true schema: type: string + security: + - bearerAuth: [] x-barbacane-middlewares: - - name: jwt-auth + - name: oidc-auth config: - required: true + issuer_url: "https://auth.example.com" + audience: "chat" + allow_query_token: true # browsers cannot set headers on a WebSocket x-barbacane-dispatch: name: ws-upstream config: diff --git a/docs/guide/middlewares/authentication.md b/docs/guide/middlewares/authentication.md index 2c064e8b..def943bc 100644 --- a/docs/guide/middlewares/authentication.md +++ b/docs/guide/middlewares/authentication.md @@ -2,17 +2,17 @@ All authentication middlewares set the standard [consumer identity headers](index.md#consumer-identity-headers) — `x-auth-consumer` and `x-auth-consumer-groups` — so downstream authorization plugins (notably [`acl`](authorization.md#acl)) don't need to know which auth plugin produced them. -- [`jwt-auth`](#jwt-auth) — JWT Bearer tokens with RS256/HS256 signatures -- [`apikey-auth`](#apikey-auth) — API keys from header or query parameter -- [`oauth2-auth`](#oauth2-auth) — Bearer tokens via RFC 7662 token introspection -- [`oidc-auth`](#oidc-auth) — OpenID Connect discovery + JWKS +- [`jwt-auth`](#jwt-auth): JWT bearer tokens verified against an inline public key (RSA or EC) +- [`apikey-auth`](#apikey-auth): API keys from header or query parameter +- [`oauth2-auth`](#oauth2-auth): Bearer tokens via RFC 7662 token introspection +- [`oidc-auth`](#oidc-auth): OpenID Connect discovery + JWKS - [`basic-auth`](#basic-auth) — HTTP Basic per RFC 7617 --- ## jwt-auth -Validates JWT tokens with RS256/HS256 signatures. +Validates JWT bearer tokens against a public key given inline as a JWK. ```yaml x-barbacane-middlewares: @@ -21,22 +21,28 @@ x-barbacane-middlewares: issuer: "https://auth.example.com" # Optional: validate iss claim audience: "my-api" # Optional: validate aud claim groups_claim: "roles" # Optional: claim name for consumer groups - skip_signature_validation: true # Required until JWKS support is implemented + public_key_jwk: # Key that verifies the signature + kty: RSA + alg: RS256 + n: "0vx7agoebGcQSuu..." # base64url modulus + e: AQAB ``` -Accepted algorithms: RS256, RS384, RS512, ES256, ES384, ES512. HS256/HS512 and `none` are rejected. +Every token must carry a valid signature under `public_key_jwk`: without the key, tokens are rejected with `401` at the signature step. RSA keys verify `RS256`, `RS384` and `RS512`; EC keys (`crv`, `x`, `y`) verify `ES256` and `ES384`. `HS256`/`HS384`/`HS512` and `none` are rejected. When the key sets `alg` or `use`, the token must match them. -**Note:** Cryptographic signature validation is not yet implemented. Set `skip_signature_validation: true` in production until JWKS support lands. Without it, all tokens are rejected with 401 at the signature step. +To verify against keys an identity provider publishes at a JWKS URL, with rotation, use [`oidc-auth`](#oidc-auth). ### Configuration | Property | Type | Default | Description | |----------|------|---------|-------------| +| `public_key_jwk` | object | - | Public key as a JWK (RFC 7517) that verifies the token signature. Required for any token to be accepted | | `issuer` | string | - | Expected `iss` claim. Tokens not matching are rejected | -| `audience` | string | - | Expected `aud` claim. Tokens not matching are rejected | +| `audience` | string | - | Expected `aud` claim. Tokens not matching are rejected. Strongly recommended | | `clock_skew_seconds` | integer | `60` | Tolerance in seconds for `exp`/`nbf` validation | | `groups_claim` | string | - | Claim name to extract consumer groups from (e.g., `"roles"`, `"groups"`). Value is set as `x-auth-consumer-groups` | -| `skip_signature_validation` | boolean | `false` | Skip cryptographic signature check. Required until JWKS support is implemented | +| `skip_signature_validation` | boolean | `false` | Test only: ignored by the compiled plugin, so it cannot disable verification | +| `jwks_url`, `public_key_pem` | string | - | Accepted but not used by `jwt-auth`; use `oidc-auth` for JWKS, or supply `public_key_jwk` | ### Context headers diff --git a/docs/guide/middlewares/authorization.md b/docs/guide/middlewares/authorization.md index ffca95c8..b71c020f 100644 --- a/docs/guide/middlewares/authorization.md +++ b/docs/guide/middlewares/authorization.md @@ -93,10 +93,10 @@ Policy-based access control via [Open Policy Agent](https://www.openpolicyagent. ```yaml x-barbacane-middlewares: - - name: jwt-auth + - name: oidc-auth config: - issuer: "https://auth.example.com" - skip_signature_validation: true + issuer_url: "https://auth.example.com" + audience: "my-api" - name: opa-authz config: opa_url: "http://opa:8181/v1/data/authz/allow" @@ -205,9 +205,10 @@ Two modes: ```yaml x-barbacane-middlewares: - - name: jwt-auth + - name: oidc-auth config: - issuer: "https://auth.example.com" + issuer_url: "https://auth.example.com" + audience: "my-api" - name: cel config: expression: > @@ -330,9 +331,10 @@ The `on_match.deny` action turns `cel` into a fully programmable gate — useful ```yaml x-barbacane-middlewares: - - name: jwt-auth + - name: oidc-auth config: - issuer: "https://auth.example.com" + issuer_url: "https://auth.example.com" + audience: "my-api" - name: cel config: expression: > diff --git a/docs/guide/middlewares/index.md b/docs/guide/middlewares/index.md index 2299ac89..0cefa883 100644 --- a/docs/guide/middlewares/index.md +++ b/docs/guide/middlewares/index.md @@ -63,15 +63,18 @@ x-barbacane-middlewares: paths: /admin/users: get: + security: + - bearerAuth: [] x-barbacane-middlewares: - - name: jwt-auth + - name: oidc-auth config: - issuer: "https://auth.example.com" + issuer_url: "https://auth.example.com" + audience: "my-api" x-barbacane-dispatch: name: http-upstream config: url: "https://api.internal" -# Resolved chain: correlation-id → cors → jwt-auth +# Resolved chain: correlation-id → cors → oidc-auth ``` **Name-based override.** When an operation entry has the same `name` as an entry in the global chain, **all** global entries with that name are dropped and the operation entries are appended in their declared order. @@ -134,8 +137,8 @@ Middlewares can write and read a per-request key-value context. The chain's orde ```yaml x-barbacane-middlewares: - - name: jwt-auth # writes context:auth.sub - config: { issuer: "https://auth.example.com" } + - name: oidc-auth # writes context:auth.sub + config: { issuer_url: "https://auth.example.com", audience: "my-api" } - name: rate-limit # reads context:auth.sub config: quota: 100 diff --git a/docs/guide/secrets.md b/docs/guide/secrets.md index 5a5838f5..996affc7 100644 --- a/docs/guide/secrets.md +++ b/docs/guide/secrets.md @@ -22,6 +22,7 @@ Use `env://` to reference environment variables: x-barbacane-middlewares: - name: oauth2-auth config: + introspection_endpoint: https://auth.example.com/introspect client_id: my-client client_secret: "env://OAUTH2_CLIENT_SECRET" ``` @@ -34,9 +35,11 @@ Use `file://` to read secrets from files: ```yaml x-barbacane-middlewares: - - name: jwt-auth + - name: oauth2-auth config: - secret: "file:///etc/secrets/jwt-signing-key" + introspection_endpoint: https://auth.example.com/introspect + client_id: my-api-client + client_secret: "file:///etc/secrets/oauth2-client-secret" ``` The gateway reads the file content, trims whitespace, and uses the result. This works well with: @@ -75,15 +78,16 @@ x-barbacane-dispatch: Authorization: "Bearer env://UPSTREAM_API_KEY" ``` -### JWT Auth with File-based Key +### S3 with Mounted Credentials ```yaml -x-barbacane-middlewares: - - name: jwt-auth - config: - public_key: "file:///var/run/secrets/jwt-public-key.pem" - issuer: https://auth.example.com - audience: my-api +x-barbacane-dispatch: + name: s3 + config: + region: eu-west-1 + bucket: assets + access_key_id: "env://AWS_ACCESS_KEY_ID" + secret_access_key: "file:///var/run/secrets/aws/secret-access-key" ``` ### Multiple Secrets diff --git a/docs/guide/spec-configuration.md b/docs/guide/spec-configuration.md index 1416eff0..ec7db0a5 100644 --- a/docs/guide/spec-configuration.md +++ b/docs/guide/spec-configuration.md @@ -165,17 +165,22 @@ Apply to a specific operation (runs after global middlewares): paths: /admin/users: get: + security: + - bearerAuth: [] x-barbacane-middlewares: - - name: jwt-auth + - name: oidc-auth config: - required: true - scopes: ["admin:read"] + issuer_url: "https://auth.example.com" + audience: "my-api" + required_scopes: "admin:read" x-barbacane-dispatch: name: http-upstream config: url: "https://api.example.com" ``` +An operation that runs an authentication middleware needs a `security` requirement naming a scheme from `components.securitySchemes`; without one the compiler stops with `E1057`. The [complete example](#complete-example) below declares `bearerAuth`. + ### Middleware Merging Operation middlewares are **merged** with global ones. If an operation middleware has the same name as a global one, the operation config overrides it. Non-overridden globals are preserved. @@ -269,10 +274,13 @@ paths: /orders: post: operationId: createOrder + security: + - bearerAuth: [] x-barbacane-middlewares: - - name: jwt-auth + - name: oidc-auth config: - required: true + issuer_url: "https://auth.shop.example.com" + audience: "shop-api" x-barbacane-dispatch: name: http-upstream config: @@ -285,10 +293,14 @@ paths: /orders/{orderId}/pay: post: operationId: payOrder + security: + - bearerAuth: [] x-barbacane-middlewares: - - name: jwt-auth + - name: oidc-auth config: - required: true + issuer_url: "https://auth.shop.example.com" + audience: "shop-api" + required_scopes: "orders:pay" x-barbacane-dispatch: name: http-upstream config: @@ -298,6 +310,12 @@ paths: responses: "200": description: Payment processed + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer ``` ## AsyncAPI Support @@ -392,10 +410,12 @@ operations: action: send channel: $ref: '#/channels/events' + # Authenticated through the security scheme the server declares. x-barbacane-middlewares: - - name: jwt-auth + - name: oidc-auth config: - required: true + issuer_url: "https://auth.example.com" + audience: "events-api" x-barbacane-dispatch: name: kafka config: diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 87d9f86e..d052d0e6 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -680,6 +680,8 @@ Example config with secrets: x-barbacane-middlewares: - name: oauth2-auth config: + introspection_endpoint: https://auth.example.com/introspect + client_id: my-api-client client_secret: "env://OAUTH2_SECRET" ``` diff --git a/docs/reference/extensions.md b/docs/reference/extensions.md index 134983d1..06563b8f 100644 --- a/docs/reference/extensions.md +++ b/docs/reference/extensions.md @@ -348,11 +348,14 @@ paths: paths: /admin: get: + security: + - bearerAuth: [] x-barbacane-middlewares: - - name: jwt-auth + - name: oidc-auth config: - required: true - scopes: ["admin:read"] + issuer_url: "https://auth.example.com" + audience: "my-api" + required_scopes: "admin:read" x-barbacane-dispatch: name: http-upstream config: @@ -392,14 +395,18 @@ paths: ```yaml - name: jwt-auth config: - required: true - header: Authorization - scheme: Bearer issuer: https://auth.example.com audience: my-api - scopes: ["read"] + groups_claim: roles # Optional: claim to read consumer groups from + public_key_jwk: # Key that verifies the token signature + kty: RSA + alg: RS256 + n: "0vx7agoebGcQSuu..." # base64url modulus + e: AQAB ``` +Reads the bearer token from `Authorization`. For keys published at a JWKS URL, use `oidc-auth`. + ### rate-limit ```yaml