From 3fc410c9eb2d66265f162110106603eaba73992c Mon Sep 17 00:00:00 2001 From: DeweyMarco Date: Tue, 11 Aug 2026 14:50:24 -0700 Subject: [PATCH 1/4] Document personalization and JWT login options --- changelog.mdx | 2 +- create/personalization.mdx | 56 +++++++++++++++++++++++++++++++-- customize/custom-scripts.mdx | 10 +++--- deploy/authentication-setup.mdx | 17 +++++++--- 4 files changed, 73 insertions(+), 12 deletions(-) diff --git a/changelog.mdx b/changelog.mdx index 99013426b9..8a7de3c66d 100644 --- a/changelog.mdx +++ b/changelog.mdx @@ -77,7 +77,7 @@ noindex: true - **Redesigned editor:** New navigation tree, top bar, and page design. Create files in **Home** and structure your navigation in **Publishing**. - **Organization-level credits:** Credits are now shared at the organization level instead of per deployment, so every deployment on a plan draws from one shared balance. See [Credit pricing](/credits) for more information. - **Editor performance:** Navigation validation, page lookups, and tab switches in the editor are now linear instead of quadratic, so large sites open and edit faster. - - **Personalization moved to add-ons:** [Personalization](/create/personalization) settings are now available under add-ons in the dashboard and no longer require a custom base path. + - **Personalization moved to add-ons:** [Personalization](/create/personalization) settings are now available under add-ons in the dashboard and can be configured on deployments that use a custom base path. - **Truncated pagination titles:** Long titles in next/previous footer links truncate so they no longer overflow. - **Long filename handling:** Uploaded files with names over 255 characters now truncate. diff --git a/create/personalization.mdx b/create/personalization.mdx index dd6a96fc55..396cc7fd55 100644 --- a/create/personalization.mdx +++ b/create/personalization.mdx @@ -5,10 +5,62 @@ keywords: ["content personalization", "personalization", "user data", "groups", --- - Personalization requires [authentication](/deploy/authentication-setup) configured with OAuth or JWT. + Personalization identifies visitors without restricting access to your pages. It can use Shared session, JWT, or OAuth independently of [authentication](/deploy/authentication-setup), including on sites hosted at a [custom subpath](/deploy/docs-subpath). -Customize content for your users when they log in to your documentation site. You can prefill API keys, show content specific to a user's plan or role, and filter API reference content based on group membership. +Customize content for identified visitors while keeping your documentation public. You can prefill API keys, show content specific to a user's plan or role, and filter API reference content based on group membership. Visitors who aren't identified continue to see public content normally. + +## Configure personalization + +Personalization is available under **Add-ons** in the dashboard. It cannot be enabled alongside full authentication on the same deployment. JWT and OAuth full authentication already include personalization features. + +1. Open the [Add-ons](https://app.mintlify.com/settings/deployment/addons) page for your deployment. +2. In **Personalization**, choose Shared session, JWT, or OAuth. +3. Configure the selected method and click **Save changes**. + +| Method | Best for | Visitor identification | +| :--- | :--- | :--- | +| Shared session | Documentation and an existing application that can share a browser session | Mintlify requests user data from your Info API with the visitor's session cookies. | +| JWT | An existing login flow that can sign Mintlify user data | Your login flow redirects the visitor back with a signed JWT. | +| OAuth | An existing OAuth 2.0 provider | Mintlify completes an OAuth flow and requests user data from your Info API. | + +### Shared session + +Shared session reuses a visitor's existing application session, so they don't need to log in again on your documentation site. + +1. Select **Shared session** in the **Personalization** settings. +2. Enter an **Info API URL** that returns the current visitor's [user data](#user-data-format). +3. Optionally enter a **Login URL**. Mintlify displays a login link when the Info API does not return user data. +4. Click **Save changes**. + +Mintlify sends a `GET` request to the Info API from the visitor's browser with credentials included. Return a successful JSON response for an identified visitor: + +```json User data response +{ + "expiresAt": 1786492800, + "content": { + "firstName": "Jane", + "plan": "Enterprise" + }, + "apiPlaygroundInputs": { + "header": { + "Authorization": "Bearer user_abc123" + } + } +} +``` + +For a visitor without a valid session, return a non-success response such as `401`. Mintlify leaves the visitor unidentified and keeps public content available. + +If the Info API is on a different origin from your documentation, configure it to allow credentialed cross-origin requests from the exact documentation origin. Do not use a wildcard origin with credentials. Prevent browsers and intermediary caches from storing user data by returning `Cache-Control: private, no-store`. + + + Values in `apiPlaygroundInputs` are available to the browser so the API playground can send them. Return short-lived, appropriately scoped credentials, and avoid exposing a privileged application session token when a dedicated documentation token is available. + + +### JWT and OAuth + +JWT and OAuth personalization use the same user data format as Shared session but leave all documentation pages public. Configure them in **Add-ons** rather than **Authentication**. See the [JWT and OAuth authentication flows](/deploy/authentication-setup#configure-authentication) for the corresponding URLs, signing keys, and user-data requirements. ## API key prefilling diff --git a/customize/custom-scripts.mdx b/customize/custom-scripts.mdx index e306a21460..55d05c287d 100644 --- a/customize/custom-scripts.mdx +++ b/customize/custom-scripts.mdx @@ -412,11 +412,11 @@ Calls made before the client initializes queue. Values apply to open and future Do not use server variables for API keys, tokens, or other secrets. -### Access authenticated user data +### Access personalized user data -If your site uses [authentication](/deploy/authentication-setup), custom scripts can read the signed-in user from `window.mintlify.user`. This is the same object exposed to MDX pages as the [`user` variable](/create/personalization#dynamic-mdx-content), so it reflects the `content` field of your user data. +If your site uses [authentication](/deploy/authentication-setup) or [personalization](/create/personalization), custom scripts can read the identified visitor from `window.mintlify.user`. This is the same object exposed to MDX pages as the [`user` variable](/create/personalization#dynamic-mdx-content), so it reflects the `content` field of your user data. -Because custom scripts run before user info resolves, listen for the `mintlify:user` event to identify when the user object is available. The event fires when user info resolves and again on any change. Its `detail` is the user object, or `null` when the visitor is signed out. +Because custom scripts run before user info resolves, listen for the `mintlify:user` event to identify when the user object is available. The event fires when user info resolves and again on any change. Its `detail` is the user object, or `null` when the visitor is signed out or unidentified. ```js Read the user after it resolves window.addEventListener('mintlify:user', (event) => { @@ -436,8 +436,8 @@ if (user) { } ``` -`window.mintlify.user` is `undefined` until user info resolves and when the visitor is signed out. Use optional chaining when reading nested fields. +`window.mintlify.user` is `undefined` until user info resolves and when the visitor is signed out or unidentified. Use optional chaining when reading nested fields. Client-side scripts can access anything you place in the user `content` field. Do not include secrets or credentials that shouldn't be readable in the browser. - \ No newline at end of file + diff --git a/deploy/authentication-setup.mdx b/deploy/authentication-setup.mdx index 8a529bdd23..2f4a59c8dc 100644 --- a/deploy/authentication-setup.mdx +++ b/deploy/authentication-setup.mdx @@ -18,6 +18,8 @@ You can configure full authentication for all pages or partial authentication wh Authentication is only available for sites hosted on a custom domain or Mintlify subdomain. For example, `docs.example.com` or `example.mintlify.site`. Authentication is **not supported** for sites with a [custom subpath](/deploy/docs-subpath). For example, `example.com/docs`. +To identify visitors while keeping pages public, use [personalization](/create/personalization). Personalization supports custom subpaths and can prefill API playground inputs without requiring visitors to authenticate before viewing a page. + ## Choose an authentication method Use this comparison to pick the method that fits your use case. See [Feature availability](#feature-availability) for how each method interacts with other Mintlify features. @@ -209,9 +211,10 @@ You host your documentation at `docs.foo.com` and you have an existing OAuth ser 3. Click **Custom**. 4. Click **JWT**. 5. Enter the URL of your existing login flow. - 6. Click **Save changes**. - 7. Click **Generate new key**. - 8. Store your key securely where your backend can access it. + 6. To offer more than one login flow, click **Add login URL** and enter a display name and URL for each option. You can configure up to 10 login URLs. + 7. Click **Save changes**. + 8. Click **Generate new key**. + 9. Store your key securely where your backend can access it. After you generate a private key, your site redeploys. When it finishes deploying, anyone who visits your site must log in to your JWT authentication system to access your content. @@ -224,6 +227,12 @@ You host your documentation at `docs.foo.com` and you have an existing OAuth ser +When JWT authentication has one login URL, unauthenticated visitors redirect to it automatically. With two or more named login URLs, visitors first see a selection page and then continue to the selected login flow. Mintlify forwards the validated `redirect` parameter so the visitor returns to the documentation page they originally requested. + + + Multiple login URLs are available for full and partial JWT authentication. JWT [personalization](/create/personalization) accepts one login URL because it identifies visitors without requiring a login before they can view public content. + + ### JWT example You host your documentation at `docs.foo.com` with an existing authentication system at `foo.com`. You want to extend your login flow to grant access to the docs while keeping your docs separate from your dashboard (or you don't have a dashboard). @@ -410,7 +419,7 @@ groups: ["pro", "enterprise"] ## User data format -When using OAuth or JWT authentication, your system returns user data that controls session length, group membership, and [content personalization](/create/personalization). +When using OAuth or JWT authentication or standalone personalization, your system returns user data that controls session length, group membership, and [content personalization](/create/personalization). ```tsx Format From 3a98f1a38b6cd5ca7fff74df937a5d0b4c4f07cf Mon Sep 17 00:00:00 2001 From: Ethan Palm <56270045+ethanpalm@users.noreply.github.com> Date: Wed, 12 Aug 2026 15:02:31 -0700 Subject: [PATCH 2/4] update expiresAt value, clarify auth vs. personalization --- deploy/authentication-setup.mdx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/deploy/authentication-setup.mdx b/deploy/authentication-setup.mdx index 2f4a59c8dc..86cf17438f 100644 --- a/deploy/authentication-setup.mdx +++ b/deploy/authentication-setup.mdx @@ -181,7 +181,7 @@ You host your documentation at `docs.foo.com` and you have an existing OAuth ser ```json { "groups": ["engineering", "admin"], - "expiresAt": 1735689600, + "expiresAt": 1893456000, "apiPlaygroundInputs": { "header": { "Authorization": "Bearer user_abc123" @@ -372,7 +372,7 @@ Manage groups through user data passed during authentication. See [User data for ```json Example user info { "groups": ["admin", "beta-users"], - "expiresAt": 1735689600 + "expiresAt": 1893456000 } ``` @@ -441,7 +441,7 @@ type User = { ```json Example { "host": "docs.example.com", - "expiresAt": 1735689600, + "expiresAt": 1893456000, "groups": ["admin", "beta-users"], "content": { "firstName": "Jane", @@ -464,15 +464,15 @@ type User = { - Session expiration time in seconds since epoch. When the current time passes this value, the user must re-authenticate. + Session expiration time in seconds since epoch. When the current time passes this value, Mintlify expires the stored user data. The visitor must authenticate again or repeat the identification flow to refresh it. **For JWT:** This differs from the JWT's `exp` claim, which determines when a JWT is considered invalid. Set the JWT `exp` claim to a short duration (10 seconds or less) for security. Use `expiresAt` for the actual session length (hours to weeks). - List of groups the user belongs to. Pages with matching `groups` in their frontmatter are accessible to this user. + List of groups the user belongs to. With authentication, pages with matching `groups` in their frontmatter are accessible to this user. With standalone personalization, groups control page and content visibility but do not restrict access to a page's direct URL. - **Example**: A user with `groups: ["admin", "engineering"]` can access pages tagged with either the `admin` or `engineering` groups. + **Example**: A user with `groups: ["admin", "engineering"]` matches content tagged with either the `admin` or `engineering` groups. From e84f5c9fd51163a160923169103a0ed681565a3f Mon Sep 17 00:00:00 2001 From: Ethan Palm <56270045+ethanpalm@users.noreply.github.com> Date: Wed, 12 Aug 2026 15:02:46 -0700 Subject: [PATCH 3/4] Update custom-scripts.mdx --- customize/custom-scripts.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/customize/custom-scripts.mdx b/customize/custom-scripts.mdx index 55d05c287d..bc72f5133d 100644 --- a/customize/custom-scripts.mdx +++ b/customize/custom-scripts.mdx @@ -421,7 +421,7 @@ Because custom scripts run before user info resolves, listen for the `mintlify:u ```js Read the user after it resolves window.addEventListener('mintlify:user', (event) => { const user = event.detail; - if (!user) return; // Signed out. + if (!user) return; // Signed out or unidentified. renderAppLauncher(user); }); From 17e89a9004585f0514f44a274490fbc379bca5f8 Mon Sep 17 00:00:00 2001 From: Ethan Palm <56270045+ethanpalm@users.noreply.github.com> Date: Wed, 12 Aug 2026 15:24:07 -0700 Subject: [PATCH 4/4] copy edit --- create/personalization.mdx | 65 +++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/create/personalization.mdx b/create/personalization.mdx index 396cc7fd55..103449479c 100644 --- a/create/personalization.mdx +++ b/create/personalization.mdx @@ -1,22 +1,12 @@ --- title: "Personalized content" -description: "Show personalized content based on user authentication data, group memberships, and custom variables to tailor documentation per audience." +description: "Show personalized content based on identified visitor data, group memberships, and custom variables to tailor documentation per audience." keywords: ["content personalization", "personalization", "user data", "groups", "dynamic", "prefill"] --- - - Personalization identifies visitors without restricting access to your pages. It can use Shared session, JWT, or OAuth independently of [authentication](/deploy/authentication-setup), including on sites hosted at a [custom subpath](/deploy/docs-subpath). - - -Customize content for identified visitors while keeping your documentation public. You can prefill API keys, show content specific to a user's plan or role, and filter API reference content based on group membership. Visitors who aren't identified continue to see public content normally. - -## Configure personalization - -Personalization is available under **Add-ons** in the dashboard. It cannot be enabled alongside full authentication on the same deployment. JWT and OAuth full authentication already include personalization features. +Customize content for identified visitors while keeping your documentation public. Examples of personalization include prefilling API keys, showing content specific to a user's plan or role, and filtering API reference content based on group membership. -1. Open the [Add-ons](https://app.mintlify.com/settings/deployment/addons) page for your deployment. -2. In **Personalization**, choose Shared session, JWT, or OAuth. -3. Configure the selected method and click **Save changes**. +Personalization uses a shared session, JWT, or OAuth to identify visitors without restricting access to your pages. | Method | Best for | Visitor identification | | :--- | :--- | :--- | @@ -24,9 +14,18 @@ Personalization is available under **Add-ons** in the dashboard. It cannot be en | JWT | An existing login flow that can sign Mintlify user data | Your login flow redirects the visitor back with a signed JWT. | | OAuth | An existing OAuth 2.0 provider | Mintlify completes an OAuth flow and requests user data from your Info API. | +## Configure personalization + +Enable personalization on the [Add-ons](https://app.mintlify.com/settings/deployment/addons) page of your dashboard. Personalization is mutually exclusive with full authentication. JWT and OAuth authentication include personalization features. + +1. Navigate to the [Add-ons](https://app.mintlify.com/settings/deployment/addons) page of your dashboard. +2. In the **Personalization** section, select shared session, JWT, or OAuth. +3. Configure your selected personalization method. +4. Click **Save changes**. + ### Shared session -Shared session reuses a visitor's existing application session, so they don't need to log in again on your documentation site. +Shared session reuses a visitor's existing application session, so they don't need to log in again on your Mintlify site. 1. Select **Shared session** in the **Personalization** settings. 2. Enter an **Info API URL** that returns the current visitor's [user data](#user-data-format). @@ -37,7 +36,7 @@ Mintlify sends a `GET` request to the Info API from the visitor's browser with c ```json User data response { - "expiresAt": 1786492800, + "expiresAt": 1893456000, "content": { "firstName": "Jane", "plan": "Enterprise" @@ -60,7 +59,27 @@ If the Info API is on a different origin from your documentation, configure it t ### JWT and OAuth -JWT and OAuth personalization use the same user data format as Shared session but leave all documentation pages public. Configure them in **Add-ons** rather than **Authentication**. See the [JWT and OAuth authentication flows](/deploy/authentication-setup#configure-authentication) for the corresponding URLs, signing keys, and user-data requirements. +JWT and OAuth personalization use the same user data format as shared session but don't restrict access to your documentation. Configure these methods in **Add-ons**, not **Authentication**. + +For JWT personalization: + +1. Enter the URL of your existing login flow. +2. Click **Save changes**. +3. Click **Generate new key** and store the downloaded private key securely. +4. In your login flow, create a JWT containing the identified visitor's [user data](#user-data-format) and sign it with the generated private key using the ES256 algorithm. +5. Redirect the visitor to a page on your documentation site with the signed JWT as the URL fragment. For example, `https://docs.example.com/get-started#{SIGNED_JWT}`. For a custom subpath, include the subpath in this URL. + +Set the JWT's `exp` claim to a short duration of 10 seconds or less. Use the user data `expiresAt` field to control how long Mintlify stores the personalization data. + +For OAuth personalization: + +1. Enter your authorization URL, client ID, scopes, token URL, Info API URL, and any optional settings, then click **Save changes**. OAuth personalization uses the Authorization Code flow with Proof Key for Code Exchange (PKCE) and does not require a client secret. +2. Copy the **Redirect URL** from the dashboard and add it as an authorized redirect URL for your OAuth provider. +3. Configure the Info API to accept a `GET` request with the OAuth access token in the `Authorization: Bearer ` header and return [user data](#user-data-format). + +The OAuth redirect path is `/mintlify-oauth-callback`. On a custom subpath, the dashboard includes the subpath in the redirect URL. + +Mintlify exchanges the authorization code and requests user data from the visitor's browser. If the token or Info API endpoint is on a different origin from your documentation, configure it to allow cross-origin requests from the exact documentation origin. The Info API must allow the `Authorization` request header. ## API key prefilling @@ -111,11 +130,15 @@ For conditional rendering based on user data, use the `user` variable in JSX com The `user` variable is an empty object for logged-out users. Use optional chaining on all `user` fields to prevent errors. For example, `{user.org?.plan}` instead of `{user.org.plan}`. -To read the same user object from a [custom JavaScript file](/customize/custom-scripts#access-authenticated-user-data), use `window.mintlify.user` and listen for the `mintlify:user` event. +To read the same user object from a [custom JavaScript file](/customize/custom-scripts#access-personalized-user-data), use `window.mintlify.user` and listen for the `mintlify:user` event. ## Page visibility -Restrict pages to specific user groups by adding `groups` to page frontmatter. Users must belong to at least one listed group to access the page. +Control which pages appear in navigation based on user groups by adding `groups` to page frontmatter. + + + With personalization, `groups` control visibility but do not restrict access to a page. A visitor can still open a group-filtered page by navigating directly to its URL. Use [authentication](/deploy/authentication-setup) to restrict access to sensitive content. + ```mdx --- @@ -124,15 +147,13 @@ groups: ["admin"] --- ``` -For more details on how groups interact with public pages, see [Control access with groups](/deploy/authentication-setup#control-access-with-groups). - ## OpenAPI content filtering Filter API reference content based on user groups with the `x-mint` extension in your OpenAPI specification. You can filter entire endpoints, individual schema properties, `oneOf` variants, and enum values. ### Filter endpoints -Add `x-mint.groups` to an operation or path to restrict the endpoint page to specific user groups. Users not in the listed groups won't see the endpoint in navigation or be able to access its page. +Add `x-mint.groups` to an operation or path to show the endpoint in navigation only to specific user groups. With standalone personalization, users not in the listed groups can still open the endpoint page by its direct URL. @@ -259,7 +280,7 @@ In this example, all users see `free`. Users in the `pro` or `enterprise` groups ## User data format -Your authentication system returns user data that controls personalization. The `groups`, `content`, and `apiPlaygroundInputs` fields described on this page are all part of the user data object. +Your identification or authentication system returns user data that controls personalization. The `groups`, `content`, and `apiPlaygroundInputs` fields described on this page are all part of the user data object. For the full user data format and field reference, see [User data format](/deploy/authentication-setup#user-data-format).