diff --git a/301redirects.json b/301redirects.json index 97ed6c70e3..8a30f820ba 100644 --- a/301redirects.json +++ b/301redirects.json @@ -428,9 +428,13 @@ "addedOn": "2023-08-29" }, "/docs/development_suite/set-up-infrastructure/authorization-flow": { - "destination": "/docs/products/console/project-configuration/authorization-flow", + "destination": "/docs/products/console/project-configuration/auth-flow/authorization-flow", "addedOn": "2023-08-29" }, + "/docs/products/console/project-configuration/authorization-flow": { + "destination": "/docs/products/console/project-configuration/auth-flow/authorization-flow", + "addedOn": "2026-01-29" + }, "/docs/development_suite/api-portal/api-documentations": { "destination": "/docs/products/console/project-configuration/documentation-portal", "addedOn": "2023-08-29" diff --git a/docs/getting-started/faqs/mia-platform-console-faqs.md b/docs/getting-started/faqs/mia-platform-console-faqs.md index 3243b73f83..f509254099 100644 --- a/docs/getting-started/faqs/mia-platform-console-faqs.md +++ b/docs/getting-started/faqs/mia-platform-console-faqs.md @@ -189,7 +189,7 @@ You can secure an endpoint from its configuration page in the **Design -> Endpoi 3. The Authorization Service calls an **Authentication Manager** (like the `auth0-client`) to validate the credentials and get user information (ID, groups, etc.). 4. The Authorization Service evaluates the endpoint's **User Group Permission** expression against the user's info. 5. If authorized, it returns success to the API Gateway, which then forwards the request to the target microservice, adding headers like `miauserid` and `miausergroups`. - [Discover more](/products/console/project-configuration/authorization-flow.md) + [Discover more](/products/console/project-configuration/auth-flow/authorization-flow.md) #### How can I implement more advanced authorization with policies? For advanced authorization, you can enable **Rönd**, an open-source sidecar based on Open Policy Agent (OPA). With Rönd, you can write declarative policies in the **Rego** language to enforce complex rules, such as: diff --git a/docs/getting-started/faqs/mia-platform-runtime-components-faqs.md b/docs/getting-started/faqs/mia-platform-runtime-components-faqs.md index 447965a5f0..f85c26e834 100644 --- a/docs/getting-started/faqs/mia-platform-runtime-components-faqs.md +++ b/docs/getting-started/faqs/mia-platform-runtime-components-faqs.md @@ -23,7 +23,7 @@ Decorators are microservices that intercept requests to add custom logic before #### What is the Authorization Service? The Authorization Service is a central component that works with the API Gateway to secure your endpoints. When a request for a protected endpoint arrives, the API Gateway asks the Authorization Service to verify the user's credentials (e.g., session cookie, API key) and evaluate their permissions against the endpoint's security policy. -[Discover more](/products/console/project-configuration/authorization-flow.md) +[Discover more](/products/console/project-configuration/auth-flow/authorization-flow.md) #### What is the API Portal? The API Portal is a runtime component that automatically generates interactive API documentation for your project. It consumes the OpenAPI 3.0 specifications exposed by your services (including the [CRUD Service](/runtime-components/plugins/crud-service/10_overview_and_usage.md)) and presents them in a user-friendly web interface. It's a key tool for improving the **developer experience (DevEx)** and acts as a **developer portal** for your APIs. @@ -81,7 +81,7 @@ The PDF Service is a utility plugin that can generate PDF documents from HTML te ### Authentication & Security #### What is the Auth0 Client? -The Auth0 Client is a plugin that integrates with the Auth0 identity platform. It's used by the [Authorization Service](/products/console/project-configuration/authorization-flow.md) to handle user authentication. It can validate tokens, fetch user profiles, and manage user sessions, acting as a bridge between your platform and Auth0. +The Auth0 Client is a plugin that integrates with the Auth0 identity platform. It's used by the [Authorization Service](/products/console/project-configuration/auth-flow/authorization-flow.md) to handle user authentication. It can validate tokens, fetch user profiles, and manage user sessions, acting as a bridge between your platform and Auth0. [Discover more](/runtime-components/plugins/auth0-client/10_overview.md) #### Are there clients for other identity providers? diff --git a/docs/infrastructure/paas/security-measures.md b/docs/infrastructure/paas/security-measures.md index 33774debdc..5f3a4ddf65 100644 --- a/docs/infrastructure/paas/security-measures.md +++ b/docs/infrastructure/paas/security-measures.md @@ -37,7 +37,7 @@ Mia-Platform uses RBAC in its authorization flow, providing its employees with v Thus, for instance, some users may be assigned to a role where they can write and edit specific files, whereas other users may be in a role restricted to reading but not editing files. This method is effective because the policies don’t need to be changed every time a person leaves or joins Mia-Platform, so it reduces administrative work and improves compliance. -For more details, visit our page about [Mia-Platform authorization flow](/products/console/project-configuration/authorization-flow.md). +For more details, visit our page about [Mia-Platform authorization flow](/products/console/project-configuration/auth-flow/authorization-flow.md). ## TLS Encryption diff --git a/docs/products/console/api-console/advanced-section/api-gateway-envoy/jwt-authn.md b/docs/products/console/api-console/advanced-section/api-gateway-envoy/jwt-authn.md index c373e8b1b5..3852789f45 100644 --- a/docs/products/console/api-console/advanced-section/api-gateway-envoy/jwt-authn.md +++ b/docs/products/console/api-console/advanced-section/api-gateway-envoy/jwt-authn.md @@ -7,7 +7,7 @@ sidebar_label: 'Use Case: JWT Authentication' Among the wide variety of features provided by the Envoy API Gateway, you can enable JWT authentication for your project. :::info -This use case is tailored to our [Authorization Flow](/products/console/project-configuration/authorization-flow.md). However, you can customize this extensions based on your application's needs. +This use case is tailored to our [Authorization Flow](/products/console/project-configuration/auth-flow/authorization-flow.md). However, you can customize this extensions based on your application's needs. ::: To do so, you need to combine different extensions in advanced mode: diff --git a/docs/products/console/api-console/advanced-section/api-gateway/how-to.md b/docs/products/console/api-console/advanced-section/api-gateway/how-to.md index c0bd96dcae..8d0f72ae79 100644 --- a/docs/products/console/api-console/advanced-section/api-gateway/how-to.md +++ b/docs/products/console/api-console/advanced-section/api-gateway/how-to.md @@ -248,7 +248,7 @@ In these cases it may be useful to have a single "gateway project" that handles Another advantage of such a configuration is that the authorization process can be centralized at the gateway level, freeing all the sub projects from the burden of managing client authentication and authorization. -In [this section](/products/console/project-configuration/authorization-flow.md) we described how the authorization flow works for a single project. In few words, in order to activate the authorization flow for a project you need to install the **Authorization Service**. +In [this section](/products/console/project-configuration/auth-flow/authorization-flow.md) we described how the authorization flow works for a single project. In few words, in order to activate the authorization flow for a project you need to install the **Authorization Service**. The Authorization Service is the service that resolves if the caller is authorized to invoke a certain endpoint and defines a set of _platform headers_ to inform the rest of the architecture about the authorization of the client. For instance, the Authorization Service sets these headers in the response that it returns to the API Gateway: diff --git a/docs/products/console/project-configuration/auth-flow/auth-flow.mdx b/docs/products/console/project-configuration/auth-flow/auth-flow.mdx new file mode 100644 index 0000000000..32ce89a293 --- /dev/null +++ b/docs/products/console/project-configuration/auth-flow/auth-flow.mdx @@ -0,0 +1,250 @@ +--- +id: auth-flow +title: Auth Flow BYO Provider (Preview) +sidebar_label: Overview +--- + +:::info +This authorization flow is in **preview** and requires the **Advanced** or **Customization** section of the Console Project configuration. +::: + +## How This Differs from the Standard Authorization Flow + +The [Authorization Flow](/products/console/project-configuration/auth-flow/authorization-flow.md) shipped with the Console relies on a chain of +platform services — **API Gateway → Authorization Service → Authentication Manager** (e.g. Auth0 +Client) — where the browser-side client already holds a token and every request is validated +through those services before reaching your microservices. + +This **BYO Provider** flow takes a fundamentally different approach: + +| | Standard Authorization Flow | Auth Flow BYO Provider | +|---|---|---| +| **Identity provider** | Pre-integrated (Auth0 Client, Client Credentials, …) | Any OIDC / OAuth 2.x provider you control | +| **Token handling** | Token lives in the browser; client sends it on every request | Tokens **never reach the browser**; a server-side BFF stores them in Redis | +| **Session mechanism** | Bearer token in `Authorization` header | `HttpOnly` / `Secure` / `SameSite=Strict` session cookie | +| **API Gateway** | Mia-Platform API Gateway (nginx-based) | Envoy API Gateway with Lua + JWT filters | +| **Authorization layer** | Authorization Service + optional Rönd sidecars | Envoy `jwt_authn` filter validates the AT; optional `ext_authz` for policy checks | +| **Client library** | Any HTTP client (token already available) | `@mia-platform-internal/authtool-client-js` manages login/logout redirects and (optionally) short-lived access tokens | +| **Best for** | Services that already have tokens (M2M, mobile apps, existing SPAs) | New SPAs where you want the highest browser-security posture and full control over the identity provider | + +:::tip +The two flows are **not mutually exclusive**. You can run the standard Authorization Flow for +machine-to-machine traffic and the BYO Provider flow for browser-facing applications within the +same project. +::: + +## TL;DR + +This page describes how to secure frontend applications using a **Backend-For-Frontend (BFF)** pattern instead of handling OAuth tokens directly in the browser. + +- The **`authtool_bff`** service acts as an OAuth 2.0 confidential client, managing Authorization Code flow, token storage (in Redis), and session cookies — so tokens never reach the browser. +- The **Envoy API Gateway** exchanges the session cookie for an access token via a Lua filter, then validates the token with a JWT filter before forwarding requests upstream. +- The **`@mia-platform-internal/authtool-client-js`** library provides a framework-agnostic client and React bindings to handle login/logout redirects, session state, and (optionally) short-lived access tokens for direct API calls. + +Two session modes are supported: **`bff`** (cookie-only, tokens stay server-side) and **`tokenExchange`** (cookie session + short-lived access token returned to JavaScript). + +## Authentication for Public Applications + +Exposing frontend applications as OAuth 2.x public clients raises significant security +concerns. + +[Current best-practice guidance](https://www.ietf.org/archive/id/draft-ietf-oauth-browser-based-apps-26.html) considers storing OAuth tokens — access token (AT) +and/or refresh token (RT) — directly in a public client the least secure way to maintain an +authorized session in the browser. + +### SPA Authorization + +To authorize your single page application (SPA) directly in the browser you [need](https://www.ietf.org/archive/id/draft-ietf-oauth-browser-based-apps-26.html#name-browser-based-oauth-20-clie) to: + +- Implement the Authorization Code flow in the frontend — libraries such as [oidc-client-ts](https://www.npmjs.com/package/oidc-client-ts) or [openid-client](https://www.npmjs.com/package/openid-client) can help +- Configure your API Gateway or authorization layer to validate tokens from your issuer of choice (Okta, Auth0, Keycloak, social login providers, etc.) +- Register a **public** application on your AS with: + - Short-lived AT + - Rotated RT + +In this scenario the frontend application running in the browser must: + +- Know the Authorization Server (AS) and/or OpenID Connect Provider (OP) endpoints +- Or discover them via [OAuth 2.0 Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414) or [OpenID Connect 1.0 Server Metadata](https://openid.net/specs/openid-connect-discovery-1_0.html) +- Store the AT (and optional RT) in memory +- Ensure the website is hardened against XSS attacks + +While this approach reduces operational costs — the SPA can call the resource server (RS) API +directly — it places a significant security burden on the frontend and requires careful maintenance. + +For completeness, such an application is not vulnerable to traditional CSRF attacks since cross-site +requests do not carry any cookie. + +### Cookie-Based Authorization + +The alternative approach relies on cookies. Instead of handling tokens in the browser, the user +delegates authorization to a **confidential client** that stores tokens safely behind an API Gateway. + +This confidential client is commonly known as a Backend-For-Frontend (BFF). + +The BFF maintains a long-lived session with the JavaScript application running in the browser +through an opaque session cookie. + +The high-level architecture looks like this: + +```mermaid +flowchart LR + JS["JS SPA"] + BR["Browser"] + AG["AGTW"] + BFF["BFF"] + AS["Authorization Server"] + RS["Resource Server"] + + JS --> BR + BR -->|"HTTPS + session cookie"| AG + AG --> BFF + + BFF -->|"OAuth flow"| AS + AS --> BFF + + BFF -->|"Access token"| RS + RS --> BFF + + BFF --> AG + AG --> BR + BR --> JS +``` + +For now, treat the backend as a black box that handles token storage +and browser redirects for the OAuth 2.0 Authorization Code Flow (with PKCE). + +```mermaid +sequenceDiagram + actor U as User + participant FE as Frontend
(JavaScript application) + participant BR as Browser
(User-Agent) + participant BE as Backend + Authorization Server
(Black Box) + + %% -------------------- Initial navigation -------------------- + U->>BR: Navigate to https://mydomain.com + BR->>BE: GET / + BE-->>BR: 200 HTML/JS + BR-->>FE: Load & run JS app + + Note over FE: Probes the BE to verify if
a session is already available + %% -------------------- Login (Auth Code + PKCE) -------------------- + FE->>BR: Trigger login (navigate /login) + BR->>BE: GET /login + BE-->>BR: 302 FOUND
Location: /
Set-Cookie: __Host-session-id=...#59; HttpOnly#59; Secure#59; SameSite=Strict + + Note over FE,BR: FE JS never sees tokens
Browser only holds a session cookie + +BR->>BE: GET / + BE-->>BR: 200 HTML/JS + BR-->>FE: Load & run JS app + + %% -------------------- Using the session -------------------- + FE->>BE: Fetch /api/data (Browser auto-sends session cookie) + BE-->>FE: 200 OK (resource data) +``` + +The flow goes as follows: + +1. The user navigates to the website. +2. The browser loads and executes the JavaScript application. +3. The JS app checks with the backend whether a session already exists. +4. If no session is found, the app redirects the browser to the backend login endpoint (`/login`), which is treated here as a black box. +5. Once login completes, the backend redirects back to the frontend and sets a session cookie. +6. The browser reloads the JS app, which can now fetch resources from REST APIs. + +From the frontend perspective, the JavaScript application needs to know three BFF endpoints: + +- **`/session`** — check whether a session exists (may return userinfo) +- **`/login`** — initiate the login flow +- **`/logout`** — terminate the session + +:::caution +The BFF is not an OpenID Provider and is not bound to expose a +`/userinfo` endpoint +::: + +Now, let's open the black box. The BFF acts as a confidential client that carries out the +server-side half of the [Authorization Code Grant](https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1) +with the Authorization Server. + +```mermaid +sequenceDiagram + actor U as User + participant BR as Browser
(User-Agent) + participant BE as Backend
(API Gateway + BFF) + participant AS as Authorization Server + + %% -------------------- Login (Auth Code + PKCE) -------------------- + BR->>BE: GET /login + BE-->>BR: 302 FOUND
Location: AS /authorize?client_id=...&code_challenge=... + BR->>AS: GET /authorize + AS-->>BR: Ask for User consent + Note over BR,U: User delegates authorization to
Backend (confidential client) by:
- username/password,
- OTP/2FA,
- passkey + BR->>AS: Sends proof of identity + AS-->>BR: 302 FOUND
Location: /callback?code=...&state=... + BR->>BE: GET /callback?code=...&state=... + + %% -------------------- Backend black box -------------------- + Note over BE: Exchanges code for tokens,
stores tokens server-side,
manages refresh + BE-->>BR: 302 FOUND
Set-Cookie: __Host-session-id=...#59; HttpOnly#59; Secure#59; SameSite=Strict +``` + +This flow relies on HTTP redirects (`302 Found` or `303 See Other`) and on the browser security +model, which prevents JavaScript from inspecting the content of redirected responses. + +The OAuth 2.x specification requires the backend to: + +- Know the AS authorization endpoint (`/authorize`) +- Implement a callback route to complete authorization (`/oauth/callback`) +- Exchange the authorization code for tokens at the AS token endpoint via a back-channel call (no browser redirect) +- Manage the cookie session + +Optionally, in OpenID Connect 1.0 the backend may implement [RP-Initiated Logout](https://openid.net/specs/openid-connect-rpinitiated-1_0.html): + +- A logout endpoint (`/logout`) that redirects to the AS end-session endpoint +- A logout callback route to complete the session termination after the AS acknowledges logout + +```mermaid +sequenceDiagram + actor U as User + participant BR as Browser
(User-Agent) + participant BE as Backend
(API Gateway + BFF) + participant AS as Authorization Server + + %% -------------------- Logout initiated -------------------- + U->>BR: Click "Logout" + BR->>BE: GET /logout (with session cookie) + + %% -------------------- Backend clears local session -------------------- + Note over BE: Destroy server session
remove stored tokens + BE-->>BR: 302 FOUND
Set-Cookie: __Host-session-id=deleted#59; Max-Age=0#59; HttpOnly#59; Secure#59; SameSite=Strict
Location: AS /logout?id_token_hint=... + + %% -------------------- RP-Initiated Logout at AS -------------------- + BR->>AS: GET /logout + AS-->>BR: Ask logout confirmation + Note over BR,U: User confirms logout + BR->>AS: Sends logout confirmation + + %% -------------------- Return to app -------------------- + AS-->>BR: 302 FOUND
Location: / +``` + +### Token Exchange + +Token exchange is a hybrid variation of cookie-based authorization: + +- The session is still maintained by a cookie +- A short-lived access token is also returned to the JavaScript application + +The BFF retains responsibility for token refresh, and the long-lived refresh token +is never disclosed to the browser. At the same time, the frontend can call the +resource server (RS) API directly using the short-lived AT. + +--- + +Continue to: + +- [Configuration](/products/console/project-configuration/auth-flow/configuration.mdx) — Envoy API Gateway and BFF setup +- [Frontend Application](/products/console/project-configuration/auth-flow/frontend-application.mdx) — JavaScript client library and React integration + diff --git a/docs/products/console/project-configuration/authorization-flow.md b/docs/products/console/project-configuration/auth-flow/authorization-flow.md similarity index 91% rename from docs/products/console/project-configuration/authorization-flow.md rename to docs/products/console/project-configuration/auth-flow/authorization-flow.md index 4e2f7c2330..388eaf82e1 100644 --- a/docs/products/console/project-configuration/authorization-flow.md +++ b/docs/products/console/project-configuration/auth-flow/authorization-flow.md @@ -31,7 +31,7 @@ Mia-Platform built a secure infrastructure that provides both authentication and For each request to any resource, the user identity is verified and its permissions checked. This way, only authorized users will be given access to the requested resource. Below you can see an image that shows Mia-Platform authentication and authorization flow: -![Authorization Flow Sequence](./img/authorization-flow-sequence.png) +![Authorization Flow Sequence](../img/authorization-flow-sequence.png) As you can see, different components are involved and each of them has a specific role in this flow. We will now proceed in describing them and explaining how they interact with each other. @@ -53,7 +53,7 @@ The Authentication Manager should then return a response body containing user id :::info -For more information about how you can configure the Authorization Service to call a custom Authentication Manager and how to choose the headers that should be proxied to it, visit this [link](/runtime-components/plugins/authorization-service/30_usage.md) +For more information about how you can configure the Authorization Service to call a custom Authentication Manager and how to choose the headers that should be proxied to it, visit the [Authorization Service usage documentation](/runtime-components/plugins/authorization-service/30_usage.md) ::: @@ -62,9 +62,9 @@ For more information about how you can configure the Authorization Service to ca Since every authentication process differs from the others for certain aspects it is advisable that you create your own service that will handle authentication and that will contact an Identity Provider (IDP), if you intend to use one. In any case, Mia-Platform has a set of services that can handle the authentication process: -* [Client Credentials](/runtime-components/plugins/client-credentials/10_configuration.md) -* [Auth0 Client](/runtime-components/plugins/auth0-client/20_configuration.md) -* Authentication Service +- [Client Credentials](/runtime-components/plugins/client-credentials/10_configuration.md) +- [Auth0 Client](/runtime-components/plugins/auth0-client/20_configuration.md) +- Authentication Service This component is expected to receive a subset of the request headers from the Authorization Service. One of these headers should contain the token that the user obtained during the login process. This token will be verified by the Authentication Manager (either internally or by sending them to an IDP). In case the received token is valid, this custom Authentication Manager should then send the retrieved user properties back as response body to the Authorization service. @@ -81,12 +81,12 @@ Its purpose is to receive the request, perform some custom application logic and ## Authorization with Rönd -In addition to the above-mentioned services that are involved in an authentication and authorization flow, -Mia-Platform provides another service: Rönd. +In addition to the above-mentioned services that are involved in an authentication and authorization flow, +Mia-Platform provides another service: Rönd. [Rönd](https://rond-authz.io) is an authorization mechanism integrated in Console that provides more advanced and efficient authorization logics, guaranteeing security and governance benefits in managing your project. -Rönd is the core service responsible for handling advanced authorization mechanisms through the -[evaluation of policies](https://rond-authz.io/docs/policy-integration) written in Rego, +Rönd is the core service responsible for handling advanced authorization mechanisms through the +[evaluation of policies](https://rond-authz.io/docs/policy-integration) written in Rego, the [OPA](https://www.openpolicyagent.org/docs/latest/) language. In order to prevent single point of failures in your project architecture, authorization policies are evaluated with a decentralized strategy by different Rönd instances that are distributed within your project. @@ -95,9 +95,9 @@ In fact, when you enable Rönd on one of your services, a new instance of Rönd Rönd intercepts the traffic directed to your service and executes the authorization logics, evaluating your policies and performing related decisions or actions, such as: -* authorization enforcement on request attributes (such as: request path, headers, query parameters, path parameters, client type, etc) and all the user properties provided by the Authorization Service in the `miauserid` and `miausergroups` platform headers. [See more information here](https://rond-authz.io/docs/policy-integration#rbac-data-model) -* query generation for data [filtering on request](https://rond-authz.io/docs/policy-integration#rows-filtering). -* [response body filtering](https://rond-authz.io/docs/policy-integration#response-filtering) to remove or change data returned by your services. +- authorization enforcement on request attributes (such as: request path, headers, query parameters, path parameters, client type, etc) and all the user properties provided by the Authorization Service in the `miauserid` and `miausergroups` platform headers. [See more information here](https://rond-authz.io/docs/policy-integration#rbac-data-model) +- query generation for data [filtering on request](https://rond-authz.io/docs/policy-integration#rows-filtering). +- [response body filtering](https://rond-authz.io/docs/policy-integration#response-filtering) to remove or change data returned by your services. -To know more about how managing and implementing RBAC functionalities, +To know more about how managing and implementing RBAC functionalities, visit the [Rönd documentation page](https://rond-authz.io/docs/policy-integration). diff --git a/docs/products/console/project-configuration/auth-flow/configuration.mdx b/docs/products/console/project-configuration/auth-flow/configuration.mdx new file mode 100644 index 0000000000..221fe9e598 --- /dev/null +++ b/docs/products/console/project-configuration/auth-flow/configuration.mdx @@ -0,0 +1,718 @@ +--- +id: configuration +title: Configuration +sidebar_label: Configuration +--- + +import Schema from "@site/static/schemas/platform/auth/authtool/authtool-bff/0.2.1/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" + +:::info +This authorization flow is in **preview** and requires the **Advanced** or **Customization** section of the Console Project configuration. +::: + +The **Mia Marketplace** provides components that implement both cookie-based and token-exchange patterns. +Two main components are involved: + +- The BFF +- The API Gateway + +The BFF (`authtool_bff`) handles: + +- Session management +- Authorization code flow with AS +- Secure storage of tokens +- OIDC logout flow + +The API Gateway (`envoy`) handles: + +- Token validation +- Cookie \<-> Token exchange + +```mermaid +flowchart LR + U([User]) + C[Browser / Client] + + subgraph E[Envoy API Gateway] + direction TB + LUA[[1. Lua Filter]] + JWT[[2. JWTAuthentication Filter]] + end + + BFF[BFF /token] + UP[(Upstream Service)] + + U --> C -->|HTTPS request| LUA + LUA -->|calls| BFF + BFF -->|returns token / headers| LUA + LUA --> JWT + JWT -->|forward| UP + UP -->|response| C --> U +``` + +## Envoy API Gateway + +To set up filters in Envoy, refer to [`on-request-scripts.yaml`](/products/console/api-console/advanced-section/api-gateway-envoy/extensions.md#lua-scripts) +and [`http-filters.yaml`](/products/console/api-console/advanced-section/api-gateway-envoy/extensions.md#http-filters) +in the Console Project advanced configuration for **API Gateway Envoy**. + +The Lua filter that exchanges the token can be downloaded from [Mia CDN](https://cdn.mia-platform.eu/runtime/platform/auth/authtool/token-exchange/latest/init.lua) +or by specific version: + +```shell +# available versions are +# -> latest +# -> 0.1.0 (=latest) +version=0.1.0 +curl -o token-exchange.lua "https://cdn.mia-platform.eu/runtime/platform/auth/authtool/token-exchange/${version}/init.lua" +``` + +Mount the script on the envoy microservice as a configmap on path `/etc/lua/lib` as +a file named `token-exchange.lua`. + +Then update `on-request-scripts.yaml` with: + +```yaml +- listener_name: frontend + body: |- + package.path = '/etc/lua/lib/?.lua;' .. package.path + local token_exchange = require('token-exchange') + + -- Configuration + 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) +``` + +Keep in mind that: + +- `authtool-bff` is the name of the microservice associated with the `authtool_bff` + confidential client in your project +- The `authtool_bff` API is served under a `/bff/` path prefix +- The cookie name must match your `sessionIdHardening` setting (here, `__Host-session_id`) + +Token validation is handled by the [JwtAuthentication filter](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter), +which verifies JWT signatures against your AS / OpenID Provider public key or JWK Set. + +Add a filter in `http-filters.yaml` as in the following non-normative example: + +```yaml +- listener_name: frontend + order: 65 + name: envoy.filters.http.jwt_authn + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication + providers: + "": + issuer: "" + audiences: [ + # your token audiences + ] + remote_jwks: + http_uri: + uri: "" + cluster: "" + timeout: 1s + # + # refer to envoy documentation to + # decide how to handle to token after validation, + # for instance: + # + forward: false + forward_payload_header: x-jwt-payload + # + rules: [ + # add rules to match + # the part of the API + # that must be Authorized by + # a valid token Authorization header + # + # for instance: + # + - match: + prefix: "/api" + requires: + provider_name: "" + ] +``` + +Finally, add the issuer as a cluster in `clusters.yaml` as in +the following non-normative example: + +```yaml +- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster + name: "" + connect_timeout: 30s + type: LOGICAL_DNS + lb_policy: ROUND_ROBIN + dns_lookup_family: V4_ONLY + load_assignment: + cluster_name: "" + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: "
" + port_value: 443 + # + # handle TLS here: + # + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + validation_context: + trusted_ca: + filename: /etc/ssl/certs/ca-certificates.crt +``` + +## BFF + +The BFF is implemented by a service called `authtool_bff`, available as an OCI image: + +```text +/platform/auth/authtool/authtool_bff: +``` + +or as a **Mia Marketplace** plugin. + +The full configuration schema is available on +[Mia CDN](https://cdn.mia-platform.eu/runtime/platform/auth/authtool/authtool-bff/0.2.1/authtool_bff.schema.json) +and rendered below: + + + +Architecturally, `authtool_bff` interacts with four components: + +- **Session** — browser cookie +- **Cache** — token storage (Redis) +- **Client** — HTTP calls to the Authorization Server +- **REST API server** — exposes endpoints to the frontend + +```mermaid +flowchart LR + U["Browser"] -->|"session cookie"| BFF["authtool-bff"] + BFF -->|"OAuth2 Code Flow"| AS["Authorization Server"] + BFF -->|"store/retrieve tokens"| R[("Redis")] + BFF -->|"Set-Cookie"| U +``` + +### Session Management + +The `session` configuration block controls how `authtool-bff` creates, secures, and maintains the cookie-based session with the browser. + +#### Cookie Creation + +When the OAuth2 callback completes, the BFF sets a session cookie. The `mode` property determines how: + +- **`bff`** — the cookie carries only an opaque session ID; tokens never reach the browser +- **`tokenMediation`** *(default)* — the cookie still maintains the session, but a short-lived access token can be returned to JavaScript + +Cookie attributes are configured under `mode`: + +```json +{ + "session": { + "mode": { + "type": "bff", + "sessionIdHardening": "host", + "sameSite": "strict", + "secure": true, + "http_only": true, + "path": "/", + "domain": null, + "partitioned": false, + "expirationSecs": 3600 + } + } +} +``` + +This produces a cookie like: + +``` +Set-Cookie: __Host-session_id=; HttpOnly; Secure; SameSite=Strict; Path=/ +``` + +#### Cookie Hardening + +The `sessionIdHardening` field controls the cookie name prefix (defaults to `host`): + +| Value | Cookie name prefix | Effect | +|----------|-------------------|---------------------------------------------------------| +| `host` | `__Host-` | Forces `Secure`, `Path=/`, no `Domain` *(default)* | +| `secure` | `__Secure-` | Forces `Secure` | +| `none` | *(no prefix)* | No enforcement, useful for local development over HTTP | + +:::info +The `http_only` cookie attribute defaults to `true` but can only be set to `false` in **debug builds**. +In release builds it is always enforced to `true`. +::: + +#### Cookie Signing and Token Encryption + +Two keys protect session integrity and token storage: + +```json +{ + "session": { + "signingKey": { "type": "env", "key": "SESSION_SIGNING_KEY" }, + "encryptionKey": { "type": "env", "key": "SESSION_ENCRYPTION_KEY" }, + "encryptAccessToken": true + } +} +``` + +- **`signingKey`** — signs the cookie value so the BFF can detect tampering +- **`encryptionKey`** — encrypts **both** access tokens and refresh tokens at rest in Redis +- **`encryptAccessToken`** — can be set to `false` to skip AT encryption for performance when tokens are short-lived (default: `true`) + +#### Token Storage and Expiration + +The BFF stores tokens in Redis and controls their lifecycle: + +```json +{ + "session": { + "authorizationFlowCache": { "connectionName": "redis-session" }, + "refreshTokenExpirationSecs": 604800, + "refreshTokenRotation": false, + "sessionJitterSecs": 7 + } +} +``` + +- **`authorizationFlowCache`** — points to a Redis connection either by reference (`connectionName`) or inline Redis config, where tokens and authorization state are persisted +- **`refreshTokenExpirationSecs`** — TTL for stored refresh tokens (default: 7 days / `604800`) +- **`refreshTokenRotation`** — when `true`, each refresh issues a new RT and invalidates the old one (default: `false`) +- **`refreshTokenExpirationPayloadField`** — optional key in the token response containing the RT expiration (not mandated by OAuth 2.0 spec) +- **`sessionJitterSecs`** — subtracts seconds from token expiry to avoid using tokens that expire mid-flight (default: `7`) + +Redis client prefixes keys with a namespace from `connections..namespace`. The service supports +multireplicas. + +When multiple sessions are needed for different OAuth 2.x clients (different Cliend IDs), the +same redis instance can be used with the same namespace (defaults to `authtool_bff`). + +Otherwise namespace can be used to separate keys on the same Redis instance. + +#### Subsequent Requests + +On every request carrying the session cookie: + +1. BFF verifies the cookie signature using `signingKey` +2. Looks up the session in Redis +3. Decrypts the stored access token using `encryptionKey` +4. If the AT is expired (minus jitter), uses the RT to refresh +5. Returns the valid AT (in BFF mode, only internally; in token mediation mode, to the caller) + +#### User Claims + +The `userClaims` array selects which ID token claims are stored in the session and exposed via `/session`: + +```json +{ + "session": { + "userClaims": ["email", "name", "picture"] + } +} +``` + +Defaults to `["email"]`. + +#### Error Handling + +The `errorRedirects` field accepts a URL where users are redirected when session or token errors occur: + +```json +{ + "session": { + "errorRedirects": "https://mydomain.com/error" + } +} +``` + +### OpenID Connect Integration + +The `authtool_bff` acts as an OAuth 2.0 confidential client that manages the Authorization Code flow with an external Identity Provider (IdP) or Authorization Server (AS). The BFF securely stores tokens and user claims in the session, never exposing sensitive material to the browser. + +**Configuration options:** + +- **Issuer and Metadata Discovery:** + The `issuer` field specifies the base URL of the IdP/AS. The `issuerMetadataSegment` (default: `/.well-known/openid-configuration`) is appended to discover OIDC endpoints automatically. + +- **Client Registration:** + The `clientId`, `redirect_uri`, and `credentials` fields register the BFF as an OIDC client. Credentials can be a client secret or a private key. + +- **Scopes and Extra Parameters:** + The `scope` field defines requested scopes (e.g., `openid`, `profile`, `email`). The `extra` field allows custom query parameters (e.g., `audience` for Auth0). + +- **Token Endpoint Authentication:** + The `preferredTokenEndpointAuthMethod` and `preferredTokenEndpointSigningAlgorithm` fields let you override the default authentication and signing algorithms, supporting providers with non-standard requirements. + +- **ID Token Validation:** + The `idTokenValidationIssuer` field allows custom issuer validation, useful when the IdP issues tokens with a different issuer than advertised in metadata. + +- **Logout Flow:** + The `postLogoutRedirectUri` field enables OIDC-compliant logout, redirecting users to a registered URI after logging out at the IdP. + +**How OIDC integrates with session management:** + +- After successful authentication, the BFF stores access and refresh tokens (encrypted) in Redis and sets a secure, signed session cookie. +- User claims from the ID token (as configured in `userClaims`) are extracted and exposed via the `/session` endpoint. +- The session lifecycle (creation, refresh, expiration, logout) is managed transparently using OIDC endpoints. +- All sensitive operations (token exchange, refresh, logout) happen server-side. + +### 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. + +- **Token Storage:** + Access and refresh tokens are encrypted and stored in Redis, keyed by the session identifier. +- **Session Lookup:** + On each request, the BFF retrieves and decrypts session data from Redis to validate and refresh tokens as needed. +- **Configuration:** + Redis connections are defined in the `connections` block and referenced via `authorizationFlowCache`. + +This design supports horizontal scaling, as all BFF instances share the same session store. + +### Server Settings + +The `server` section configures the BFF REST API server: + +- **Listening Address and Port:** + Controls which network interface and port the BFF binds to, suitable for deployment behind a reverse proxy or API gateway. +- **API Prefix:** + The `apiPrefix` setting groups all BFF endpoints under a common URL path (e.g., `/bff/`), simplifying routing and API gateway integration. + +### BFF Key Setup Script + +The `setup_bff_keys.sh` script automates the generation of cryptographic keys required by the BFF for cookie signing and token encryption. + +The script generates values for the `SESSION_SIGNING_KEY` and `SESSION_ENCRYPTION_KEY` environment variables, which the BFF configuration references to sign session cookies and encrypt tokens at rest in Redis. + +You can download the latest version of the script using: + +```shell +version=latest +curl -o setup_bff_keys.sh "https://cdn.mia-platform.eu/runtime/platform/auth/authtool/authtool_bff/${version}/setup_bff_keys.sh" + +# enable execution +chmod +x setup_bff_keys.sh +``` + +### Private Key / Public Key for `private_key_jwt` Assertion + +When configuring the BFF to use `private_key_jwt` authentication, the client proves its identity by signing a JWT with its private key instead of sending a client secret. The Authorization Server verifies the JWT using the registered public key. + +**How it works:** +- The private key is referenced in the configuration under `credentials` (type: `privateKey`). +- During the token exchange, the BFF creates and signs a JWT assertion, presenting it to the token endpoint as proof of identity. +- The Authorization Server must have the matching public key registered for the client. + +**Key points:** +- This method is more secure than a client secret because the private key never leaves the BFF. +- The private key should be generated and stored securely, using the `setup_bff_keys.sh` script or a similar process. +- The public key must be registered with the IdP during client registration. + +**Example configuration snippet:** +```json +{ + "client": { + "credentials": { + "privateKey": { + "type": "file", + "path": "/path/to/key.pem" + } + }, + "preferredTokenEndpointAuthMethod": "private_key_jwt" + } +} +``` + +This setup provides strong asymmetric authentication between the BFF and the Authorization Server. + +### Endpoint Rewrites + +Due to how the **Mia Platform** Console manages Envoy routing, every BFF route must be explicitly declared in the `endpoints.yaml` advanced configuration file. Each route needs both a `path` match (exact) and a `prefix` match (with trailing slash) to handle requests correctly. + +All BFF routes must disable the Lua token-exchange filter, since these endpoints handle authentication themselves and must not go through the token-exchange pipeline. Depending on your setup, you may also need to disable `jwt_authn` (when JWT validation covers the whole listener) and/or `ext_authz` (when external authorization is active). + +Add the following entries to `endpoints.yaml`: + +```yaml +## BFF ## + +### login +- listener_name: frontend + match: + prefix: /bff/login/ + route: + prefix_rewrite: /bff/login/ + 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/login + route: + prefix_rewrite: /bff/login + 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 + +### logout +- listener_name: frontend + match: + prefix: /bff/logout/ + route: + prefix_rewrite: /bff/logout/ + 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/logout + route: + prefix_rewrite: /bff/logout + 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 + +### oidc/logout/callback +- listener_name: frontend + match: + prefix: /bff/oidc/logout/callback/ + route: + prefix_rewrite: /bff/oidc/logout/callback/ + 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/callback + route: + prefix_rewrite: /bff/oidc/logout/callback + 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 + +### session +- listener_name: frontend + match: + prefix: /bff/session/ + route: + prefix_rewrite: /bff/session/ + 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/session + route: + prefix_rewrite: /bff/session + 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 + +### oauth/callback +- listener_name: frontend + match: + prefix: /bff/oauth/callback/ + route: + prefix_rewrite: /bff/oauth/callback/ + 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/oauth/callback + route: + prefix_rewrite: /bff/oauth/callback + 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 +Each route requires **two entries** — one with `path` (exact match) and one with `prefix` (trailing slash) — to ensure that both `/bff/login` and `/bff/login/?redirect_uri=...` are routed correctly. +::: + +### Alternative: Cluster-Based Routing + +Instead of declaring every BFF route individually in `endpoints.yaml`, you can skip the Console [Endpoints](/products/console/api-console/api-design/endpoints.md) section entirely and route all `/bff` traffic through a single catch-all entry. This approach requires three steps: + +**1. Do not create a BFF endpoint in the Console Design section** + +The `authtool-bff` microservice should still be deployed in your project, but you should **not** create an endpoint for it in the Console's **Design > Endpoints** section. Routing will be handled entirely via Envoy advanced configuration. + +**2. Add the `authtool-bff` cluster in `clusters.yaml`** + +Since the Console is not managing the endpoint, you need to register the BFF as an Envoy cluster manually: + +```yaml +- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster + name: "authtool-bff" + connect_timeout: 30s + type: LOGICAL_DNS + lb_policy: ROUND_ROBIN + dns_lookup_family: V4_ONLY + load_assignment: + cluster_name: "authtool-bff" + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: "authtool-bff" + port_value: 3000 +``` + +Adjust `address` and `port_value` to match your service name and listening port. + +**3. Add a single `/bff` prefix route in `endpoints.yaml`** + +Instead of one entry per BFF route, add a single prefix match that catches all `/bff` traffic: + +```yaml +## BFF (catch-all) +- listener_name: frontend + match: + prefix: /bff + route: + prefix_rewrite: /bff + 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 +``` + +This single entry replaces all the per-route declarations from the previous section. The trade-off is that the BFF service is not visible in the Console's Endpoints UI, and any future BFF routes are automatically covered without configuration changes. diff --git a/docs/products/console/project-configuration/auth-flow/frontend-application.mdx b/docs/products/console/project-configuration/auth-flow/frontend-application.mdx new file mode 100644 index 0000000000..842edd2c6c --- /dev/null +++ b/docs/products/console/project-configuration/auth-flow/frontend-application.mdx @@ -0,0 +1,284 @@ +--- +id: frontend-application +title: Frontend Application +sidebar_label: Frontend Application +--- + +:::info +This authorization flow is in **preview** and requires the **Advanced** or **Customization** section of the Console Project configuration. +::: + +The JavaScript client library `@mia-platform-internal/authtool-client-js` is the frontend +counterpart of the `authtool_bff`. It abstracts the session lifecycle, token management, and +login/logout redirects so that your application only needs to react to authentication state changes. + +The library ships two entry points: + +| Entry point | Import path | Description | +|---|---|---| +| Core | `@mia-platform-internal/authtool-client-js` | Framework-agnostic client (vanilla JS, Svelte, Vue, etc.) | +| React | `@mia-platform-internal/authtool-client-js/react` | React Context, hooks, and helper components | + +Both ESM and CommonJS builds are provided. + +## Installation + +```shell +npm install @mia-platform-internal/authtool-client-js +# or +yarn add @mia-platform-internal/authtool-client-js +``` + +React bindings require `react` and `react-dom` as peer dependencies (v18 or v19). + +## Core Usage (Framework-Agnostic) + +The library exposes an `init` function that creates a `UserManager` — the single object +your application interacts with. + +```typescript +import { init, isUnauthorized } from '@mia-platform-internal/authtool-client-js' + +const manager = init({ + // base URL where authtool-bff is reachable (default: '/') + baseUrl: '/bff/', + + // 'bff' → cookie-only session + // 'tokenExchange' → cookie session + short-lived AT returned to JS + mode: 'bff', + + // whether to call `start()` immediately (default: true) + // NOTE: currently requires an explicit `start()` call in vanilla JS; + // the React hook `useAuthtoolContext()` calls `start()` automatically + autostart: true, + + // max random jitter subtracted from token expiry before scheduling a refresh (default: 7000 ms) + maxJitterMs: 7000, +}) +``` + +### `UserManager` API + +| Property / Method | Type | Description | +|---|---|---| +| `baseUrl` | `string` | Normalized BFF base URL. | +| `client.loginUrl(redirectUri?)` | `URL` | Builds the login URL. Optionally accepts a `redirect_uri` so the BFF redirects back after login. | +| `client.logoutUrl(redirectUri?)` | `URL` | Builds the logout URL. | +| `current$` | `Observable` | Emits the current user (or an error) whenever the session state changes. | +| `accessToken$` | `Observable` | *(token-exchange mode only)* Emits the latest access token context. | +| `authorizedFetch` | `typeof fetch` | A `fetch` wrapper that automatically attaches the `Authorization: Bearer ` header when a valid access token is available. | +| `fetchSession()` | `void` | Manually triggers a session check against the BFF `/session` endpoint. | +| `fetchAccessToken()` | `void` | Manually requests a new access token from the BFF `/token` endpoint and schedules the next refresh. | +| `start()` | `void` | Starts the manager: fetches the session and, in `tokenExchange` mode, begins the token refresh loop. In React, `useAuthtoolContext()` calls this automatically. In vanilla JS, you must call `start()` explicitly. | + +### Subscribing to State + +`current$` and `accessToken$` are lightweight observables. Subscribe to be notified +whenever the session or access token changes: + +```typescript +const subscription = manager.current$.subscribe((value) => { + if (isUnauthorized(value)) { + // redirect to login + window.location.href = manager.client.loginUrl(window.location.href).href + return + } + + // value is of type User + console.log('User info:', value.userinfo) + console.log('Session valid until:', value.valid_until) +}) + +// later, to tear down: +subscription.unsubscribe() +``` + +### Making Authorized API Calls + +In `tokenExchange` mode, the manager keeps a valid access token in memory and +exposes `authorizedFetch` — a drop-in replacement for `window.fetch`: + +```typescript +const manager = init({ baseUrl: '/bff/', mode: 'tokenExchange' }) + +// later, once the session is established: +const response = await manager.authorizedFetch('/api/data') +const data = await response.json() +``` + +`authorizedFetch` automatically adds the `Authorization` header with the +current bearer token. Token refresh is handled transparently in the background. + +### Token Refresh Lifecycle + +When `mode` is set to `tokenExchange`: + +1. On `start()`, the manager calls the BFF `POST /token` endpoint. +2. The BFF returns an `AccessToken` containing `access_token` and `expires_in`. +3. The manager schedules the next refresh at `expires_in * 1000 - randomJitter` milliseconds (with a minimum delay of 1 second). +4. If the refresh fails with a `401`, the manager stops and emits an `UnauthorizedError` + on `accessToken$`. + +## React Integration + +The React entry point wraps the core `UserManager` with React Context and hooks. + +### Setup + +Create the manager once and pass it to `AuthtoolProvider` at the top of your component tree: + +```tsx +import { init } from '@mia-platform-internal/authtool-client-js' +import { AuthtoolProvider } from '@mia-platform-internal/authtool-client-js/react' + +const manager = init({ baseUrl: '/bff/', mode: 'tokenExchange' }) + +function Root() { + return ( + + + + ) +} +``` + +### `useAuthtoolContext` Hook + +Inside the provider tree, call `useAuthtoolContext()` to access authentication state +reactively. The hook subscribes to `current$` and `accessToken$` and triggers +re-renders when their values change. + +```tsx +import { + useAuthtoolContext, + isUnauthorized +} from '@mia-platform-internal/authtool-client-js/react' + +function Dashboard() { + const { user, accessToken, fetch, loginUrl, logoutUrl } = useAuthtoolContext() + + if (user === undefined) { + return

Loading…

+ } + + if (isUnauthorized(user)) { + return

Not authenticated

+ } + + return ( +
+

Welcome, {String(user.userinfo.name)}

+ +
+ ) +} +``` + +The object returned by `useAuthtoolContext()` contains: + +| Field | Type | Description | +|---|---|---| +| `user` | `User \| Error \| undefined` | Current session state. `undefined` while loading. | +| `accessToken` | `AccessTokenContext \| Error \| undefined` | Current access token *(token-exchange mode)*. | +| `fetch` | `typeof fetch` | `authorizedFetch` — fetch with auto-attached bearer token. | +| `baseUrl` | `string` | BFF base URL. | +| `loginUrl(redirectUri?)` | `(redirectUri?: string) => URL` | Builds the login URL. | +| `logoutUrl(redirectUri?)` | `(redirectUri?: string) => URL` | Builds the logout URL. | + +### `RedirectToLogin` Component + +A convenience component that automatically redirects unauthenticated users to the login page: + +```tsx +import { RedirectToLogin } from '@mia-platform-internal/authtool-client-js/react' + +function App() { + return ( + <> + + {/* rest of your application */} + + ) +} +``` + +When the session check returns a `401`, `RedirectToLogin` navigates the browser to the +BFF login endpoint, passing the current URL as `redirect_uri` so the user returns to the +same page after authentication. + +## Error Handling + +The library exposes two typed error classes: + +- **`UnauthorizedError`** — the BFF returned `401`; the session is invalid or expired. +- **`UnhandledError`** — any other non-OK response from the BFF. + +Both carry the original `Response` object for inspection. Use the `isUnauthorized` helper +to discriminate: + +```typescript +import { isUnauthorized } from '@mia-platform-internal/authtool-client-js' + +manager.current$.subscribe((value) => { + if (isUnauthorized(value)) { + // handle 401 — e.g., redirect to login + } else if ('error' in value) { + // handle other errors + console.error(value.error) + } else { + // value is User + } +}) +``` + +## Putting It All Together + +A minimal end-to-end React application: + +```tsx +import React from 'react' +import { createRoot } from 'react-dom/client' + +import { init } from '@mia-platform-internal/authtool-client-js' +import { + AuthtoolProvider, + useAuthtoolContext, + RedirectToLogin, + isUnauthorized +} from '@mia-platform-internal/authtool-client-js/react' + +const manager = init({ baseUrl: '/bff/', mode: 'tokenExchange' }) + +function App() { + const { user, fetch } = useAuthtoolContext() + + if (user === undefined) { + return

Loading…

+ } + + return ( + <> + + {!isUnauthorized(user) && ( +
+

Welcome, {String(user.userinfo.email)}

+ +
+ )} + + ) +} + +createRoot(document.getElementById('root')!).render( + + + +) +``` diff --git a/docs/runtime-components/applications/secure-api-gateway/10_overview.md b/docs/runtime-components/applications/secure-api-gateway/10_overview.md index 2a528ea04c..bc33054a62 100644 --- a/docs/runtime-components/applications/secure-api-gateway/10_overview.md +++ b/docs/runtime-components/applications/secure-api-gateway/10_overview.md @@ -40,7 +40,7 @@ To have an overview on how to use the **Secure API Gateway** application to secu ## Authorization Service The [authorization-service](/runtime-components/plugins/authorization-service/10_overview.md) is added by the application if it does not already exist. -The **authorization-service** is used to manage the [authorization flow](/products/console/project-configuration/authorization-flow.md). It is created with a standard configuration, with `USERINFO_URL` pointing to the `auth0-client` service. +The **authorization-service** is used to manage the [authorization flow](/products/console/project-configuration/auth-flow/authorization-flow.md). It is created with a standard configuration, with `USERINFO_URL` pointing to the `auth0-client` service. To have a complete overview of all the possible configurations for the **authorization-service** read the [dedicated documentation page](/runtime-components/plugins/authorization-service/20_configuration.md). diff --git a/docs/runtime-components/plugins/form-service-backend/30_form_assignments_configuration.md b/docs/runtime-components/plugins/form-service-backend/30_form_assignments_configuration.md index 91a4d38d22..0061ffd6b6 100644 --- a/docs/runtime-components/plugins/form-service-backend/30_form_assignments_configuration.md +++ b/docs/runtime-components/plugins/form-service-backend/30_form_assignments_configuration.md @@ -98,7 +98,7 @@ Here an example: :::caution -The Form Service doesn't provide any authentication or authorization system nor user management systems. Further information can be found [here](/products/console/project-configuration/authorization-flow.md). +The Form Service doesn't provide any authentication or authorization system nor user management systems. Further information can be found [here](/products/console/project-configuration/auth-flow/authorization-flow.md). ::: diff --git a/docs/runtime-components/plugins/oauth-login-site/10_overview.md b/docs/runtime-components/plugins/oauth-login-site/10_overview.md index db7df5479a..d7a03e5139 100644 --- a/docs/runtime-components/plugins/oauth-login-site/10_overview.md +++ b/docs/runtime-components/plugins/oauth-login-site/10_overview.md @@ -30,6 +30,6 @@ Here is an example of a possible flow where a user logins into an application th :::info -If you want an in-depth description of how Mia-Platform authorization flow works after the login process visit this [link](/products/console/project-configuration/authorization-flow.md) +If you want an in-depth description of how Mia-Platform authorization flow works after the login process visit the [authorization flow documentation](/products/console/project-configuration/auth-flow/authorization-flow.md) ::: diff --git a/sidebars.json b/sidebars.json index 6edbc595f8..a017d49b38 100644 --- a/sidebars.json +++ b/sidebars.json @@ -438,8 +438,28 @@ ] }, { - "id": "products/console/project-configuration/authorization-flow", - "type": "doc" + "id": "products/console/project-configuration/auth-flow/authorization-flow", + "type": "doc", + "label": "Authorization Flow" + }, + { + "label": "Auth Flow BYO Provider", + "type": "category", + "collapsed": true, + "link": { + "id": "products/console/project-configuration/auth-flow/auth-flow", + "type": "doc" + }, + "items": [ + { + "id": "products/console/project-configuration/auth-flow/configuration", + "type": "doc" + }, + { + "id": "products/console/project-configuration/auth-flow/frontend-application", + "type": "doc" + } + ] }, { "id": "products/console/project-configuration/documentation-portal", diff --git a/static/schemas/platform/auth/authtool/authtool-bff/0.2.1/authtool_bff.schema.json b/static/schemas/platform/auth/authtool/authtool-bff/0.2.1/authtool_bff.schema.json new file mode 100644 index 0000000000..b08932022a --- /dev/null +++ b/static/schemas/platform/auth/authtool/authtool-bff/0.2.1/authtool_bff.schema.json @@ -0,0 +1,751 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Configuration", + "type": "object", + "properties": { + "client": { + "$ref": "#/definitions/AuthorizationClientSettings" + }, + "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" + } + ] + }, + "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" + ] + }, + "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" + }, + "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": [ + "issuer", + "clientId", + "redirect_uri", + "credentials" + ] + }, + "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" + }, + "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 diff --git a/static/schemas/platform/auth/authtool/authtool-bff/examples/example1.json b/static/schemas/platform/auth/authtool/authtool-bff/examples/example1.json new file mode 100644 index 0000000000..b6efc12942 --- /dev/null +++ b/static/schemas/platform/auth/authtool/authtool-bff/examples/example1.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://cdn.mia-platform.eu/runtime/platform/auth/authtool/authtool-bff/0.2.1/authtool_bff.schema.json", + "connections": { + "authorization-cache": { + "type": "redis", + "config": { + "host": "authorization-cache" + } + } + }, + "client": { + "issuer": "https://keycloak.mia-demo-re5gu6.gcp.mia-platform.eu/realms/platform-demo", + "issuerMetadataSegment": "/realms/platform-demo/.well-known/openid-configuration", + "clientId": "catalog-website", + "redirect_uri": "https://{{PROJECT_HOST}}/bff/oauth/callback", + "scope": "openid email profile catalog-mcp", + "credentials": { + "privateKey": { + "type": "file", + "path": "/run/secrets/authtool-bff-keys/key.pem" + } + }, + "preferredTokenEndpointAuthMethod": "private_key_jwt", + "postLogoutRedirectUri": "https://catalog.mia-demo-re5gu6.gcp.mia-platform.eu/bff/oidc/logout/callback" + }, + "session": { + "signingKey": { + "type": "file", + "path": "/run/secrets/authtool-bff-keys/cookie-secret.key" + }, + "authorizationFlowCache": { + "connectionName": "authorization-cache" + }, + "encryptionKey": { + "type": "file", + "path": "/run/secrets/authtool-bff-keys/redis-token-enc.key" + }, + "refreshTokenExpirationPayloadField": "refresh_expires_in" + }, + "server": { + "apiPrefix": "/bff" + } +}