From 4ef75c7d84c736fc59b5c4ce13e9b0439e6c4dc7 Mon Sep 17 00:00:00 2001 From: Umberto Toniolo Date: Sun, 10 May 2026 14:43:37 +0200 Subject: [PATCH] update(auth-flow): add documentation for new features on authtool bff --- .../auth-flow/auth-flow.mdx | 38 + .../auth-flow/configuration.mdx | 323 ++++++- .../auth-flow/frontend-application.mdx | 9 + .../latest/authtool_bff.schema.json | 794 ++++++++++++++++++ 4 files changed, 1163 insertions(+), 1 deletion(-) create mode 100644 static/schemas/platform/auth/authtool/authtool-bff/latest/authtool_bff.schema.json diff --git a/docs/products/console/project-configuration/auth-flow/auth-flow.mdx b/docs/products/console/project-configuration/auth-flow/auth-flow.mdx index 32ce89a293..55fe1b5a49 100644 --- a/docs/products/console/project-configuration/auth-flow/auth-flow.mdx +++ b/docs/products/console/project-configuration/auth-flow/auth-flow.mdx @@ -43,6 +43,12 @@ This page describes how to secure frontend applications using a **Backend-For-Fr Two session modes are supported: **`bff`** (cookie-only, tokens stay server-side) and **`tokenExchange`** (cookie session + short-lived access token returned to JavaScript). +Additional capabilities include: + +- **Multi-client support**: a single BFF instance can serve multiple OAuth 2.0 clients (e.g., multiple frontends with different redirect URIs) under a shared session cookie, with independent per-client token storage. +- **[Back-Channel Logout](https://openid.net/specs/openid-connect-backchannel-1_0.html)**: the Authorization Server can terminate sessions server-to-server without browser interaction. +- **JWKS endpoint** (`/.well-known/jwks.json`): publishes the BFF's public keys for `private_key_jwt` client authentication. + ## Authentication for Public Applications Exposing frontend applications as OAuth 2.x public clients raises significant security @@ -230,6 +236,38 @@ sequenceDiagram AS-->>BR: 302 FOUND
Location: / ``` +### Back-Channel Logout + +In addition to RP-initiated logout, the BFF supports [OpenID Connect Back-Channel Logout 1.0](https://openid.net/specs/openid-connect-backchannel-1_0.html). This allows the Authorization Server to notify the BFF directly when a session ends — for example, when a user logs out from another application or an administrator revokes a session. + +The AS sends a `POST` request to the BFF's `/oidc/logout/backchannel` endpoint containing a signed `logout_token` JWT. The BFF validates the token, resolves the browser session from the `sid` (or `sub`) claim, and destroys all stored tokens for that session. + +```mermaid +sequenceDiagram + participant AS as Authorization Server + participant BFF as BFF + participant R as Redis + + AS->>BFF: POST /oidc/logout/backchannel
logout_token= + BFF->>BFF: Validate JWT signature & claims + BFF->>R: Look up session by sid/sub + R-->>BFF: session_id + BFF->>R: Delete all tokens for session_id + BFF-->>AS: 200 OK + + Note over BFF: Next browser request with
the old session cookie
will get 401 → re-login +``` + +This is especially useful when the BFF serves [multiple clients](#multi-client-support): a single back-channel logout notification destroys tokens for **all** clients associated with that session. + +### Multi-Client Support + +A single BFF instance can serve multiple OAuth 2.0 clients. This is useful when multiple frontend applications (e.g., running on different ports or domains) need independent client registrations with different redirect URIs or scopes, while sharing the same Authorization Server. + +All clients share the same session cookie. Token storage is keyed by `(session_id, client_id)`, so each client maintains independent tokens under the same browser session. The target client is selected via the `?client_id=` query parameter on `/login`, `/logout`, `/session`, and `/token` endpoints. When the parameter is omitted, the default client is used. + +See [Configuration — Multi-Client](/products/console/project-configuration/auth-flow/configuration.mdx#multi-client-configuration) for setup details. + ### Token Exchange Token exchange is a hybrid variation of cookie-based authorization: diff --git a/docs/products/console/project-configuration/auth-flow/configuration.mdx b/docs/products/console/project-configuration/auth-flow/configuration.mdx index 8bf1993d2f..aa7e391c85 100644 --- a/docs/products/console/project-configuration/auth-flow/configuration.mdx +++ b/docs/products/console/project-configuration/auth-flow/configuration.mdx @@ -4,7 +4,7 @@ title: Configuration sidebar_label: Configuration --- -import Schema from "@site/static/schemas/platform/auth/authtool/authtool-bff/0.2.2/authtool_bff.schema.json" +import Schema from "@site/static/schemas/platform/auth/authtool/authtool-bff/latest/authtool_bff.schema.json" import Example from "@site/static/schemas/platform/auth/authtool/authtool-bff/examples/example1.json" import SchemaViewer from "@site/src/components/SchemaViewer" @@ -448,6 +448,266 @@ The `authtool_bff` acts as an OAuth 2.0 confidential client that manages the Aut - The session lifecycle (creation, refresh, expiration, logout) is managed transparently using OIDC endpoints. - All sensitive operations (token exchange, refresh, logout) happen server-side. +### Multi-Client Configuration + +A single BFF instance can serve multiple OAuth 2.0 clients. This is useful when multiple +frontend applications share the same Authorization Server but require separate client registrations +with different redirect URIs, scopes, or credentials. + +#### Single Client (default) + +When only one client is needed, configure it directly as an object under `$.client`: + +```json +{ + "client": { + "issuer": "https://idp.example.com", + "clientId": "my-app", + "redirect_uri": "https://app.example.com/oauth/callback", + "scope": "openid profile email", + "credentials": { "..." : "..." }, + "postLogoutRedirectUri": "https://app.example.com/oidc/logout/callback" + } +} +``` + +#### Multiple Clients + +Configure multiple clients as an array under `$.client.clients`. Exactly one client +must be marked as `"default": true`: + +```json +{ + "client": { + "issuer": "https://idp.example.com", + "clients": [ + { + "default": true, + "clientId": "main-app", + "redirect_uri": "https://app.example.com/oauth/callback", + "scope": "openid profile email", + "credentials": { "..." : "..." }, + "postLogoutRedirectUri": "https://app.example.com/oidc/logout/callback" + }, + { + "clientId": "admin-app", + "redirect_uri": "https://admin.example.com/oauth/callback", + "scope": "openid profile email", + "credentials": { "..." : "..." }, + "postLogoutRedirectUri": "https://admin.example.com/oidc/logout/callback" + } + ] + } +} +``` + +All clients share the same `issuer` and the same session cookie. Token storage is +keyed by `(session_id, client_id)`, so each client maintains independent tokens. + +The target client is selected via the `?client_id=` query parameter on +`/login`, `/logout`, `/session`, and `/token` endpoints. When the parameter is omitted +the default client is used. + +:::tip +All clients must be registered on the **same Authorization Server** (same `issuer`). +If you need to integrate with different identity providers, deploy separate BFF instances. +::: + +#### Envoy Routing for Multi-Client + +The BFF selects the target client based on the `?client_id=` query parameter. +Since the frontend library is client-ID agnostic, the parameter must be injected at +the API gateway level before requests reach the BFF. + +There are several techniques to achieve this in Envoy, depending on how your +frontend applications are separated. + +##### Option 1: Virtual hosts (domain-based separation) + +When each frontend application is served on a different domain (e.g., +`app.example.com` and `admin.example.com`), you can use Envoy +[virtual hosts](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#config-route-v3-virtualhost) +to match on the `:authority` (Host) header and inject the correct `client_id` +per domain. + +Each virtual host has its own set of routes and per-route Lua configuration: + +```yaml +# route configuration +virtual_hosts: + # default frontend — no client_id injection needed + - name: main-app + domains: ["app.example.com"] + routes: + - match: + prefix: /bff + route: + cluster: "authtool-bff" + timeout: "0s" + typed_per_filter_config: + envoy.filters.http.lua: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute + disabled: true + + # admin frontend — inject client_id=admin-app + - name: admin-app + domains: ["admin.example.com"] + routes: + - match: + prefix: /bff + route: + cluster: "authtool-bff" + timeout: "0s" + typed_per_filter_config: + envoy.filters.http.lua: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute + source_code: + inline_string: | + package.path = '/etc/lua/lib/?.lua;' .. package.path + local inject = require('client-id-inject') + inject.inject(request_handle, "admin-app") +``` + +When the BFF sits behind a load balancer or reverse proxy, the proxy must +forward the original `Host` header (or set `x-forwarded-host`). Envoy +matches virtual hosts on the `:authority` pseudo-header, which is the `Host` +header in HTTP/1.1. + +##### Option 2: Listener-based separation + +If each frontend application is exposed on a different port or network +interface, use separate Envoy listeners. Each listener can carry its own +`on-request-scripts.yaml` entry that injects the right `client_id`: + +```yaml +# on-request-scripts.yaml — admin frontend on a separate listener +- listener_name: admin + body: |- + package.path = '/etc/lua/lib/?.lua;' .. package.path + + local inject = require('client-id-inject') + inject.inject(request_handle, "admin-app") + + local token_exchange = require('token-exchange') + local config = { + token_service_cluster = "authtool-bff", + token_service_endpoint = "/bff/token", + session_cookie_name = "__Host-session_id", + } + token_exchange.on_request(request_handle, config) +``` + +##### Option 3: Path-prefix discrimination + +If all frontends share the same domain and listener, you can +differentiate by path prefix. For example, serve the admin app under +`/admin/` and rewrite BFF paths accordingly: + +```yaml +# endpoints.yaml — admin app routes to BFF with client_id +- listener_name: frontend + match: + prefix: /admin/bff/ + route: + regex_rewrite: + pattern: + regex: "^/admin/bff/(.*)" + substitution: "/bff/\\1" + cluster: "authtool-bff" + timeout: "0s" + typed_per_filter_config: + envoy.filters.http.lua: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute + source_code: + inline_string: | + package.path = '/etc/lua/lib/?.lua;' .. package.path + local inject = require('client-id-inject') + inject.inject(request_handle, "admin-app") +``` + +##### Lua helper + +All options above use the same `client-id-inject.lua` helper, which can be +mounted alongside the token-exchange filter in `/etc/lua/lib`: + +```lua +-- /etc/lua/lib/client-id-inject.lua + +local M = {} + +--- Appends `client_id=` to the current request query string. +--- If the request already contains a `client_id` parameter it is left unchanged. +function M.inject(request_handle, client_id) + local path = request_handle:headers():get(":path") + if not path then return end + + -- skip if client_id is already present + if path:find("[?&]client_id=") then return end + + local sep = path:find("?") and "&" or "?" + request_handle:headers():replace(":path", path .. sep .. "client_id=" .. client_id) +end + +return M +``` + +##### Which endpoints need `client_id` + +The endpoints that require `client_id` injection are: + +| Endpoint | Purpose | +|---|---| +| `/login` | Initiates the authorization code flow for the correct client | +| `/logout` | Removes the correct client's tokens and redirects to AS | +| `/session` | Checks whether a session exists for the correct client | +| `/token` | Exchanges the session cookie for the correct client's access token | + +Endpoints that do **not** need `client_id` (they work across all clients): + +| Endpoint | Purpose | +|---|---| +| `/oauth/callback` | The BFF resolves the client from the stored authorization state | +| `/oidc/logout/callback` | Post-logout redirect, no client selection needed | +| `/oidc/logout/backchannel` | Validates against all configured client IDs | +| `/.well-known/jwks.json` | Shared public keys | + +:::tip +The default client does not need `client_id` injection — when the parameter is absent +the BFF falls back to the client marked `"default": true`. You only need the inject +script for non-default frontends. +::: + +### Back-Channel Logout + +The BFF supports [OpenID Connect Back-Channel Logout 1.0](https://openid.net/specs/openid-connect-backchannel-1_0.html), +allowing the Authorization Server to terminate sessions server-to-server. + +To enable back-channel logout, register the BFF's backchannel logout URL on the +Authorization Server client configuration: + +| AS Setting | Value | +|---|---| +| Backchannel Logout URL | `https:///bff/oidc/logout/backchannel` | +| Backchannel Logout Session Required | `true` (recommended) | + +When using `private_key_jwt` authentication, the AS needs access to the BFF's +public keys. Configure the JWKS URL on the AS client: + +| AS Setting | Value | +|---|---| +| JWKS URL | `https:///bff/.well-known/jwks.json` | + +When a logout token is received, the BFF validates the JWT against all configured +client IDs, resolves the browser session from the `sid` (or `sub`) claim, and +atomically deletes all stored tokens for that session — including tokens for all +clients in a multi-client setup. + +:::caution +The `/oidc/logout/backchannel` endpoint must be reachable by the Authorization Server. +In environments where the BFF is behind a firewall, ensure the AS can reach this +endpoint via the internal network. +::: + ### Redis Integration The BFF uses Redis as a centralized cache for session data, access tokens, and refresh tokens. The `authorizationFlowCache` field in the session configuration references a Redis connection, either by name or inline. @@ -727,6 +987,67 @@ Add the following entries to `endpoints.yaml`: # envoy.filters.http.ext_authz: # "@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute" # disabled: true + +### oidc/logout/backchannel +- listener_name: frontend + match: + prefix: /bff/oidc/logout/backchannel/ + route: + prefix_rewrite: /bff/oidc/logout/backchannel/ + cluster: "authtool-bff" + timeout: "0s" + typed_per_filter_config: + envoy.filters.http.lua: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute + disabled: true + # activate when jwt_authn covers the whole listener + # envoy.filters.http.jwt_authn: + # "@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + # disabled: true + # activate when ext_authz is active + # envoy.filters.http.ext_authz: + # "@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute" + # disabled: true +- listener_name: frontend + match: + path: /bff/oidc/logout/backchannel + route: + prefix_rewrite: /bff/oidc/logout/backchannel + cluster: "authtool-bff" + timeout: "0s" + typed_per_filter_config: + envoy.filters.http.lua: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute + disabled: true + # activate when jwt_authn covers the whole listener + # envoy.filters.http.jwt_authn: + # "@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + # disabled: true + # activate when ext_authz is active + # envoy.filters.http.ext_authz: + # "@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute" + # disabled: true + +### .well-known/jwks.json +- listener_name: frontend + match: + path: /bff/.well-known/jwks.json + route: + prefix_rewrite: /bff/.well-known/jwks.json + cluster: "authtool-bff" + timeout: "0s" + typed_per_filter_config: + envoy.filters.http.lua: + "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute + disabled: true + # activate when jwt_authn covers the whole listener + # envoy.filters.http.jwt_authn: + # "@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig + # disabled: true + # activate when ext_authz is active + # envoy.filters.http.ext_authz: + # "@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute" + # disabled: true ``` :::tip diff --git a/docs/products/console/project-configuration/auth-flow/frontend-application.mdx b/docs/products/console/project-configuration/auth-flow/frontend-application.mdx index 842edd2c6c..c14cd0ea78 100644 --- a/docs/products/console/project-configuration/auth-flow/frontend-application.mdx +++ b/docs/products/console/project-configuration/auth-flow/frontend-application.mdx @@ -31,6 +31,15 @@ yarn add @mia-platform-internal/authtool-client-js React bindings require `react` and `react-dom` as peer dependencies (v18 or v19). +## Multi-Client Note + +The client library is **client-ID agnostic** — it does not append `?client_id=...` to BFF +requests. In a [multi-client setup](/products/console/project-configuration/auth-flow/configuration.mdx#multi-client-configuration), +the `client_id` query parameter must be injected at the infrastructure layer (e.g., an Envoy Lua +filter or an API gateway rewrite rule) before requests reach the BFF. Each frontend application +should be routed through a proxy configuration that appends the correct `client_id` for its +registered OAuth client. + ## Core Usage (Framework-Agnostic) The library exposes an `init` function that creates a `UserManager` — the single object diff --git a/static/schemas/platform/auth/authtool/authtool-bff/latest/authtool_bff.schema.json b/static/schemas/platform/auth/authtool/authtool-bff/latest/authtool_bff.schema.json new file mode 100644 index 0000000000..d9532358c0 --- /dev/null +++ b/static/schemas/platform/auth/authtool/authtool-bff/latest/authtool_bff.schema.json @@ -0,0 +1,794 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Configuration", + "type": "object", + "properties": { + "client": { + "$ref": "#/definitions/Clients" + }, + "connections": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Connection" + } + }, + "server": { + "allOf": [ + { + "$ref": "#/definitions/RestServerSettings" + } + ], + "default": { + "apiPrefix": "/", + "ip": "0.0.0.0", + "port": 3000 + } + }, + "session": { + "$ref": "#/definitions/SessionSettings" + } + }, + "required": [ + "client", + "session" + ], + "definitions": { + "Algorithm": { + "description": "The algorithms supported for signing/verifying JWTs", + "oneOf": [ + { + "description": "HMAC using SHA-256", + "type": "string", + "const": "HS256" + }, + { + "description": "HMAC using SHA-384", + "type": "string", + "const": "HS384" + }, + { + "description": "HMAC using SHA-512", + "type": "string", + "const": "HS512" + }, + { + "description": "ECDSA using SHA-256", + "type": "string", + "const": "ES256" + }, + { + "description": "ECDSA using SHA-384", + "type": "string", + "const": "ES384" + }, + { + "description": "ECDSA using SHA-512", + "type": "string", + "const": "ES512" + }, + { + "description": "RSASSA-PKCS1-v1_5 using SHA-256", + "type": "string", + "const": "RS256" + }, + { + "description": "RSASSA-PKCS1-v1_5 using SHA-384", + "type": "string", + "const": "RS384" + }, + { + "description": "RSASSA-PKCS1-v1_5 using SHA-512", + "type": "string", + "const": "RS512" + }, + { + "description": "RSASSA-PSS using SHA-256", + "type": "string", + "const": "PS256" + }, + { + "description": "RSASSA-PSS using SHA-384", + "type": "string", + "const": "PS384" + }, + { + "description": "RSASSA-PSS using SHA-512", + "type": "string", + "const": "PS512" + }, + { + "description": "Edwards-curve Digital Signature Algorithm (EdDSA)", + "type": "string", + "const": "EdDSA" + } + ] + }, + "ApiPrefix": { + "title": "string", + "type": "string", + "pattern": "^/(?:[a-zA-Z0-9._~-]+|\\{[a-zA-Z0-9._~-]+\\})(?:/(?:[a-zA-Z0-9._~-]+|\\{[a-zA-Z0-9._~-]+\\}))*$" + }, + "AuthorizationClientSettings": { + "type": "object", + "properties": { + "clientId": { + "description": "The client identifier issued to the client during the registration process", + "allOf": [ + { + "$ref": "#/definitions/Secret" + } + ] + }, + "credentials": { + "description": "Client credentials used for token retrieval,\nthey can be absent for public clients if\nthe client application is registered as such\nand the IdP supports it.", + "allOf": [ + { + "$ref": "#/definitions/Credentials" + } + ] + }, + "default": { + "description": "when multiple clients are set, if marks the default client\nnot needing login query param to select", + "type": "boolean", + "default": false + }, + "extra": { + "description": "Extra parameters to add to the authorization request.\nThese parameters will be added to the authorization URL query string.\n\nFor instance `auth0` requires the `audience` parameter to be set\nto obtain a decrypted access token.", + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "string" + } + }, + "idTokenValidationIssuer": { + "description": "ID Token issuer validation strategy.\nIf not set, the default behavior is to validate\nagainst the `issuer` value.\n\nThis is useful for identity providers which\nissue ID Tokens with a different issuer\nthan the one defined in the metadata document.", + "type": [ + "string", + "null" + ] + }, + "postLogoutRedirectUri": { + "description": "URI to which the RP is requesting that the End-User's User Agent\nbe redirected after a logout has been performed.\nThis URI SHOULD use the https scheme and MAY contain port,\npath, and query parameter components;\nhowever, it MAY use the http scheme, provided that the Client Type is confidential,\nas defined in Section 2.1 of OAuth 2.0 [RFC6749], and provided the OP allows the use of http RP URIs.\n\nThe value MUST have been previously registered with the OP,\neither using the post_logout_redirect_uris Registration parameter or via another mechanism.", + "anyOf": [ + { + "$ref": "#/definitions/UrlAsciiChecked" + }, + { + "type": "null" + } + ] + }, + "preferredTokenEndpointAuthMethod": { + "description": "Preferred token endpoint authentication method.\nIf not set, the default method defined by the\nauthorization server will be used.\n\nThis is useful for `okta` identity providers\nwhich require `client_secret_post` method but still\nclaim to support `client_secret_basic` as default.", + "anyOf": [ + { + "$ref": "#/definitions/TokenAuthMethod" + }, + { + "type": "null" + } + ] + }, + "preferredTokenEndpointSigningAlgorithm": { + "description": "Preferred signing algorithm for token endpoint.\nIt is meaningful only when the token auth\nmethod is [`TokenAuthMethod::PrivateKeyJWT`].\nIf not set, the credentials private key algorithm and\nthe authorization server metadata establish the default to be used.", + "anyOf": [ + { + "$ref": "#/definitions/Algorithm" + }, + { + "type": "null" + } + ] + }, + "redirect_uri": { + "description": "The redirection URI registered by the client application", + "allOf": [ + { + "$ref": "#/definitions/UrlAsciiChecked" + } + ] + }, + "scope": { + "description": "The set of scopes requested by the client application", + "allOf": [ + { + "$ref": "#/definitions/Scope" + } + ], + "default": "" + } + }, + "required": [ + "clientId", + "redirect_uri", + "credentials" + ] + }, + "Clients": { + "type": "object", + "properties": { + "issuer": { + "description": "The issuer URL of the authorization server", + "type": "string", + "format": "uri" + }, + "issuerMetadataSegment": { + "description": "The metadata segment to append to the issuer URL\nto retrieve the OpenID Connect metadata or OAuth Server Metadata.\n\nDefaults to `/.well-known/openid-configuration`", + "allOf": [ + { + "$ref": "#/definitions/ApiPrefix" + } + ], + "default": "/.well-known/openid-configuration" + } + }, + "anyOf": [ + { + "$ref": "#/definitions/AuthorizationClientSettings" + }, + { + "type": "object", + "properties": { + "clients": { + "type": "array", + "items": { + "$ref": "#/definitions/AuthorizationClientSettings" + } + } + }, + "required": [ + "clients" + ] + } + ], + "required": [ + "issuer" + ] + }, + "Connection": { + "oneOf": [ + { + "type": "object", + "properties": { + "config": { + "$ref": "#/definitions/RedisConfig" + }, + "type": { + "type": "string", + "const": "redis" + } + }, + "required": [ + "type", + "config" + ] + } + ] + }, + "ConnectionOrRef": { + "anyOf": [ + { + "type": "object", + "properties": { + "connectionName": { + "type": "string" + } + }, + "required": [ + "connectionName" + ] + }, + { + "$ref": "#/definitions/RedisConfig" + } + ] + }, + "CookieHardeningRule": { + "description": "Cookie hardening rules for the session ID cookie.", + "oneOf": [ + { + "description": "Prefix the cookie name with \"__Host-\" to enforce\n[cookie hardening rules](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis)\nwhich entail:\n- served over HTTPS\n- Secure\n- no Domain attribute\n- Path=/", + "type": "string", + "const": "host" + }, + { + "description": "Prefix the cookie name with \"__Secure-\" to enforce\n[cookie hardening rules](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis)\nwhich entail:\n- served over HTTPS\n- Secure", + "type": "string", + "const": "secure" + }, + { + "description": "No prefix", + "type": "string", + "const": "none" + } + ] + }, + "CookieMode": { + "description": "Sets the behavior of the callback endpoint.\n\nThis modes are regulated by\n[browser-based applications security considerations](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps),\nand [Best Current Practice for Securing Browser-Based Applications](https://www.rfc-editor.org/rfc/rfc9207).\n\nWhen attaching cookies to requests, consider that all modes by default\napplies the following security measures:\n\nThe following cookie security guidelines are relevant for this particular BFF architecture:\n\n- The BFF MUST enable the Secure flag for its cookies\n- The BFF MUST enable the HttpOnly flag for its cookies\n- The BFF SHOULD enable the SameSite=Strict flag for its cookies\n- The BFF SHOULD set its cookie path to /\n- The BFF SHOULD NOT set the Domain attribute for cookies\n- The BFF SHOULD start the name of its cookies with the __Host prefix ([I-D.ietf-httpbis-rfc6265bis])", + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "bff" + } + }, + "allOf": [ + { + "$ref": "#/definitions/CookieSettings" + } + ], + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "tokenMediation" + } + }, + "allOf": [ + { + "$ref": "#/definitions/CookieSettings" + } + ], + "required": [ + "type" + ] + } + ] + }, + "CookieSameSite": { + "description": "`SameSite` attribute applied to the session cookie.", + "oneOf": [ + { + "description": "SameSite=Strict", + "type": "string", + "const": "strict" + }, + { + "description": "SameSite=Lax", + "type": "string", + "const": "lax" + }, + { + "description": "SameSite=None and will force cookie to set attribute `Secure`", + "type": "string", + "const": "none" + } + ] + }, + "CookieSettings": { + "description": "Settings for the session cookie.", + "type": "object", + "properties": { + "domain": { + "description": "`Domain` attribute applied to the session cookie. It is overridden\nby setting cookie hardening to `Host`", + "type": [ + "string", + "null" + ], + "default": null + }, + "expirationSecs": { + "description": "Sets the expiration time for cookies in seconds", + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "http_only": { + "description": "`HttpOnly` attribute applied to the session cookie.\nThis is not available in the release build.", + "type": "boolean", + "default": true + }, + "partitioned": { + "description": "`Partitioned` attribute applied to the session cookie.", + "type": "boolean", + "default": false + }, + "path": { + "description": "`Path` attribute applied to the session cookie. It is overridden\nby setting cookie hardening to `Host`", + "type": [ + "string", + "null" + ], + "default": null + }, + "sameSite": { + "description": "`SameSite` attribute applied to the session cookie.", + "allOf": [ + { + "$ref": "#/definitions/CookieSameSite" + } + ] + }, + "secure": { + "description": "`Secure` attribute applied to the session cookie.\nThis property is not used when session hardening is enabled", + "type": "boolean", + "default": true + }, + "sessionIdHardening": { + "description": "Session hardening prefix", + "allOf": [ + { + "$ref": "#/definitions/CookieHardeningRule" + } + ] + } + } + }, + "Credentials": { + "type": "object", + "properties": { + "kid": { + "type": [ + "string", + "null" + ] + } + }, + "oneOf": [ + { + "type": "object", + "properties": { + "secret": { + "$ref": "#/definitions/Secret" + } + }, + "required": [ + "secret" + ] + }, + { + "type": "object", + "properties": { + "privateKey": { + "$ref": "#/definitions/Secret" + } + }, + "required": [ + "privateKey" + ] + } + ] + }, + "ProtocolVersion": { + "description": "Enum representing the communication protocol with the server.\n\nThis enum represents the types of data that the server can send to the client,\nand the capabilities that the client can use.", + "oneOf": [ + { + "description": "", + "type": "string", + "const": "RESP2" + }, + { + "description": "", + "type": "string", + "const": "RESP3" + } + ] + }, + "RedisConfig": { + "type": "object", + "properties": { + "connection": { + "description": "Connection settings", + "allOf": [ + { + "$ref": "#/definitions/RedisConnectionInfo" + } + ] + }, + "host": { + "description": "Hostname", + "type": "string" + }, + "namespace": { + "description": "Base namespace prefix for this client", + "type": [ + "string", + "null" + ] + }, + "port": { + "description": "Port", + "type": "integer", + "format": "uint16", + "default": 6379, + "maximum": 65535, + "minimum": 0 + }, + "tls": { + "description": "TLS settings", + "anyOf": [ + { + "$ref": "#/definitions/RedisConfigTls" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "host" + ] + }, + "RedisConfigTls": { + "type": "object", + "properties": { + "ca": { + "anyOf": [ + { + "$ref": "#/definitions/Secret" + }, + { + "type": "null" + } + ] + } + } + }, + "RedisConnectionInfo": { + "description": "Redis specific/connection independent information used to establish a connection to redis.", + "type": "object", + "properties": { + "db": { + "description": "The database number to use. This is usually `0`.", + "type": "integer", + "format": "int64", + "default": 0 + }, + "password": { + "description": "Optionally a password that should be used for connection.", + "anyOf": [ + { + "$ref": "#/definitions/Secret" + }, + { + "type": "null" + } + ] + }, + "protocol": { + "description": "Version of the protocol to use.", + "allOf": [ + { + "$ref": "#/definitions/ProtocolVersion" + } + ] + }, + "username": { + "description": "Optionally a username that should be used for connection.", + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "protocol" + ] + }, + "RestServerSettings": { + "type": "object", + "properties": { + "apiPrefix": { + "allOf": [ + { + "$ref": "#/definitions/ApiPrefix" + } + ], + "default": "/" + }, + "ip": { + "description": "Server bind IP", + "type": "string", + "format": "ipv4", + "default": "0.0.0.0" + }, + "port": { + "description": "gRPC Server port", + "type": "integer", + "format": "uint16", + "default": 3000, + "maximum": 65535, + "minimum": 0 + } + } + }, + "Scope": { + "description": "a space separated list of scopes: allowed characters are ASCII 0x21 / 0x23-0x5B / 0x5D-0x7E", + "type": "string" + }, + "Secret": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "encoding": { + "description": "Define which type of encoding the library supports when it needs to read the actual secret value.", + "type": "string", + "enum": [ + "base64" + ] + }, + "key": { + "type": "string" + }, + "type": { + "const": "env" + } + }, + "required": [ + "type", + "key" + ] + }, + { + "type": "object", + "properties": { + "encoding": { + "description": "Define which type of encoding the library supports when it needs to read the actual secret value.", + "type": "string", + "enum": [ + "base64" + ] + }, + "key": { + "type": "string" + }, + "path": { + "type": "string" + }, + "type": { + "const": "file" + } + }, + "required": [ + "type", + "path" + ] + } + ], + "examples": [ + "my-secret", + { + "key": "CUSTOM_ENV_VAR", + "type": "env" + }, + { + "encoding": "base64", + "key": "CUSTOM_ENV_VAR", + "type": "env" + }, + { + "path": "/path/to/file", + "type": "file" + } + ] + }, + "SessionSettings": { + "type": "object", + "properties": { + "authorizationFlowCache": { + "description": "Cache layer for authorization requests", + "allOf": [ + { + "$ref": "#/definitions/ConnectionOrRef" + } + ] + }, + "disableLogoutOnGet": { + "description": "Allow disabling the logout on GET.\nThis is useful on SameSite=Lax cookies to\nprevent a CSRF request on logout.", + "type": "boolean", + "default": false + }, + "encryptAccessToken": { + "description": "Should encrypt access_token at rest (default: true).\nIf the token has an expiration, maybe a short one,\nto make the token retrieval faster, encryption can be\ndeactivated. Be careful with this option.", + "type": "boolean", + "default": true + }, + "encryptionKey": { + "description": "Encryption key for refresh tokens and access tokens at rest.", + "allOf": [ + { + "$ref": "#/definitions/Secret" + } + ] + }, + "errorRedirects": { + "description": "Optional URL to redirect users in case of errors", + "anyOf": [ + { + "$ref": "#/definitions/UrlAsciiChecked" + }, + { + "type": "null" + } + ] + }, + "mode": { + "description": "Sets the behavior of the callback endpoint.", + "allOf": [ + { + "$ref": "#/definitions/CookieMode" + } + ] + }, + "refreshTokenExpirationPayloadField": { + "description": "Access Token response key containing expiration\nof the refresh token. This key is not mandated\nby OAuth 2.0 spec.", + "type": [ + "string", + "null" + ] + }, + "refreshTokenExpirationSecs": { + "description": "Sets the expiration time for refresh tokens in seconds\n(default: 7 days). This value cannot be inferred from\naccess token response, so it must be set here.", + "type": "integer", + "format": "int64", + "default": 604800 + }, + "refreshTokenRotation": { + "description": "Enable refresh token rotation (default: false).\n\nThe IdP must support it and the client application\nmust be registered to use it. If enabled, every time\na refresh token is used to obtain new tokens, a new\nrefresh token is also issued and the previous one\nis invalidated.", + "type": "boolean", + "default": false + }, + "sessionJitterSecs": { + "description": "Adds a jitter in seconds to the session expiration time\nto avoid tokens that expire during transit\n(default: 7 seconds).", + "type": "integer", + "format": "int64", + "default": 7 + }, + "signingKey": { + "description": "Key to sign cookies", + "allOf": [ + { + "$ref": "#/definitions/Secret" + } + ] + }, + "userClaims": { + "description": "Id token claims to be included in the user info stored in the session.", + "type": "array", + "default": [ + "email" + ], + "items": { + "type": "string" + }, + "uniqueItems": true + } + }, + "required": [ + "signingKey", + "authorizationFlowCache", + "encryptionKey" + ] + }, + "TokenAuthMethod": { + "type": "string", + "enum": [ + "client_secret_basic", + "client_secret_post", + "client_secret_jwt", + "private_key_jwt", + "none" + ] + }, + "UrlAsciiChecked": { + "type": "string", + "format": "uri" + } + } +} \ No newline at end of file