Files
mintlify__docs/deploy/authentication-setup.mdx
Ethan Palm 9a4ec7b557 Editor docs overhaul (#7112)
* Fix editor docs accuracy, split media page, clear translation orphans

Accuracy fixes, each verified against mintlify/mint or mintlify/server:

- pages.mdx: "Editors and managers can restore them" -> "admins".
  "Manager" is not a Mintlify role.
- index.mdx: publishing on a feature branch does not automatically open a
  pull request; you choose commit-to-branch or PR. Contradicted the
  branching page's own table.
- branching-and-publishing.mdx: preview deployments build when you open a
  pull request, not on every branch save. Confirmed in gitlabWebhooks
  (handleMergeRequestOpenEvent) and cruxWebhooks (keyed on reviewId).
  tutorial.mdx was already correct.
- settings.mdx: rewritten against editor-settings-form.tsx. The panel has
  four sections, not "two layers", and the entire Appearance section was
  undocumented - including "Open live preview in new tab", which
  live-preview.mdx already linked readers to. Corrected three setting
  labels, noted that draft PRs default to on, added the 20,000-character
  instruction limit.
- live-preview.mdx: link now resolves to /editor/settings#appearance.

De-bloat:

- Split the media section out of pages.mdx (172 lines, 15 subtopics) into
  editor/media.mdx and added it to the nav.
- Trimmed index.mdx from 11 sidebar-duplicating cards to 4.

Translation orphans and redirects:

- Deleted 7 translated files whose English source was removed in April and
  June. The translate automation never cleaned them up, and in PR #6062 it
  wrote into es|fr|zh/editor/media.mdx 34 days after the English source was
  deleted.
- Dropped /editor/media -> /editor/pages so it stops shadowing the
  recreated page.
- Added /{es,fr,zh}/editor/publish, completing an English-only redirect.
- Added /editor/collaborate and its language variants. That page was
  deleted in June with no redirect and has been a live 404 since.

mint broken-links clean, mint a11y clean, vale 0/0/0 on changed files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Restructure editor docs: 14 pages to 9, cut UI description

The editor section had grown to 14 pages, about 40% of which described
things a user can see on screen. The ratio was 105 field-enumeration
bullets to 42 procedural steps.

New structure, organized around what is invisible rather than what is
on screen:

  Overview      absorbs git-essentials (concepts + the Git mapping table)
  Tutorial      unchanged
  Edit content  absorbs media and navigation
  Publish       branch mechanics, conflicts, git sync, commit signing
  Review        previews, pull requests, approve and merge
  Collaborate   comments + suggestions + real-time editing
  Editor agent  trimmed
  Settings      absorbs configurations as a pointer
  Keyboard shortcuts  unchanged

Removed: git-essentials, media, navigation, live-preview, comments,
suggestions, configurations, branching-and-publishing. Added: publish,
review, collaborate.

configurations.mdx was the clearest cut. It was a 193-line field-by-field
mirror of the Site configurations panel, which is a third rendering of
information already in the docs.json schema and in organize/settings-*
(2,694 lines). It is now a short section in settings.mdx covering only
what is specific to editing configuration from the editor: real-time
sync, the SVG logo constraint, and the redirects interface.

Also cut: block actions, the minimap, code block options, right-click
menu enumerations, page settings field lists (already delegated to
/organize/pages), and most screenshots of self-evident controls.

1,320 to 848 body lines. Field-enumeration bullets 105 to 29.

English redirects added for all eight removed pages. Language variants
deliberately omitted: es/fr/zh still carry the old structure, so
redirecting those paths now would shadow pages that still exist there.
The translation automation should mirror this restructure; verify
afterward that no orphans remain.

Vale vocabulary widened rather than reworded around: autocommit(s|ed|ing)?,
autosav(e|es|ed|ing), dotfile(s)?.

mint broken-links clean, mint a11y clean, vale 0 errors on editor/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Correct keyboard shortcuts against the editor keymap

Verified every documented shortcut against the bindings in mintlify/mint
rather than inferring them. The app-level set is exhaustive:
mod+backslash, mod+i, mod+k, mod+s, mod+shift+period, mod+z, shift+mod+d,
shift+mod+s. Visual-mode bindings come from the Tiptap extensions.

- Cmd/Ctrl+Shift+F ("Switch between Navigation and Files tree") does not
  exist. No such binding is registered anywhere in the dashboard. Replaced
  with the real binding at that spot in the UI: Cmd/Ctrl+Shift+. toggles
  "Show all files".
- Added Heading 5 and Heading 6. Heading.configure sets levels 1-6 and
  preserves the parent Mod-Alt-N shortcuts; the table stopped at 4.
- Added Cmd/Ctrl+G for accordion group, bound in Accordions.tsx.

Confirmed correct and unchanged: Cmd+K search, Cmd+I agent, Cmd+Shift+S
mode toggle, Cmd+Shift+D diff, Cmd+\ sidebar, Cmd+Shift+M comment,
Cmd+Shift+E suggesting mode, Cmd+Shift+X strikethrough, Cmd+K link,
Cmd+Enter line break, Cmd+Shift+8 unordered list, and that source mode
runs Monaco.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Document auto publish, drop the deprecated deploy branch lock

Auto publish is available to all deployments, so the settings panel shows
"Auto publish" rather than "Main branch autocommits". It is not just a
rename: when it is on, the editor hides the branch selector and the
Publish button entirely (TopBar returns null for both), so there is no
pending state and no review step. Documented that behavior in
settings.mdx and added a note at the top of publish.mdx, since that page
otherwise assumes a publish step exists.

The deployment branch lock was deprecated and removed, so it stays out of
the docs. It was already dropped from settings.mdx in the restructure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* 💅

* copy edit index

* Fix editor docs I verified against dead code

Three claims traced to code that never mounts. Ethan confirmed the diff
shortcut does not work, which exposed the pattern.

- Removed Cmd/Ctrl+Shift+D for diff view. It is registered only at
  SubBar/index.tsx:488, inside EditorSubBar, which is exported and
  imported by nothing. There is no live diff shortcut; exitDiff exists
  only as an onClick in EditorModeContextMenu.
- Source mode runs CodeMirror, not Monaco. SourceEditor resolves through
  source-editor/index.tsx to editor-view.tsx, which imports
  @codemirror/state and @codemirror/view. Monaco is used only by
  EmbedModal. The shortcut rows themselves were correct: CodeMirror's
  defaultKeymap and searchKeymap match VS Code for toggle comment, move
  line, duplicate line, find, and add cursor above/below, all confirmed
  against @codemirror/commands.
- Rewrote the left panel section of pages.mdx. The tabs are Home and
  Publishing (EditorNavigationSidebarContents.tsx:62), not Navigation and
  Files. The Navigation/Files switcher lives in the same dead SubBar
  component. Home renders the workspace file tree including private
  pages; Publishing renders the site navigation and site settings.
  Confirmed by the e2e helper openPublishingNavigation, which queries the
  Publishing tab by role against the running app.

Also corrected in that section: the tree hides docs.json, dotfiles,
extensionless files, all-caps Markdown, and css/js/jsx/mjs/cjs/pdf unless
Show all files is on; "unlisted" was an invented term, replaced with a
link to /organize/hidden-pages; and the folder menu item is "New page",
confirmed by e2e, not "New file".

Added four source-mode bindings that are real and undocumented: Cmd+B and
Cmd+I insert Markdown markers, Cmd+Option+Shift+[ and ] fold and unfold
all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* copy edit pages

* copy edit publish

* copy edit review

* copy edit settings

* copy edit collaborate

* Updated mintlify pages

- Updated quickstart.mdx

Mintlify-Source: dashboard-editor

* copy edit keyboard shortcuts

* Updated mintlify pages

- Updated quickstart.mdx

Mintlify-Source: dashboard-editor

* Revert "Updated mintlify pages"

This reverts commit 8d1b50cbfc.

* Updated mintlify pages

- Updated quickstart.mdx

Mintlify-Source: dashboard-editor

* fix editor edits

* Mirror the editor restructure into es, fr, and zh

Brings all three translated sites onto the same nine-page structure as
English instead of waiting for the translate automation, which has a
documented history of leaving orphans behind.

Content:
- Deleted 21 orphans: branching-and-publishing, comments, configurations,
  git-essentials, live-preview, navigation, and suggestions in each
  language. None had an English source after the restructure.
- Added publish, review, and collaborate in each language.
- Rewrote index, pages, settings, agent, and keyboard-shortcuts to match
  the current English content, including Home/Publishing tabs, CodeMirror
  source mode, Show all files, and the restored Appearance section.
- Kept the existing conventions: es uses usted, fr uses vous, keywords and
  product UI labels stay in English, and every heading is wrapped in a
  <div id="english-slug"> so cross-language anchors resolve. Verified that
  every English anchor exists in all three languages.

Navigation and redirects:
- es.json, fr.json, and zh.json now list the same nine pages as docs.json.
- Removed six locale redirects that would have shadowed the new publish
  and collaborate pages once they existed.
- Added 21 locale redirects for the removed pages and retargeted the
  destinations that pointed at them.
- Retargeted four /editor/configurations redirects that pointed at
  #site-configurations, an anchor dropped in the settings rewrite.
- Rewrote stale links in changelog, authentication-setup, concepts, and
  glossary across all three languages, since broken-links does not follow
  redirects.

English fixes needed first, all caused by the settings restructure:
- Restored ## Appearance. Two pages link to #appearance, and its four
  preferences were otherwise undocumented.
- Restored ## Create draft pull requests by default, which changelog
  links to and which defaults to on.
- Promoted Main branch autocommits from ### to ##; it was nested under PR
  instructions, and its warning named a setting no longer on the page.
- Retargeted publish.mdx from the removed #auto-publish anchor.
- Fixed a missing space in the settings page intro.

mint broken-links clean, mint a11y clean (1042 files), vale 0 errors,
all five JSON files valid.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
2026-08-25 19:40:14 -07:00

522 lines
23 KiB
Plaintext

---
title: "Authentication setup"
description: "Set up user authentication to control access to pages and API references using password, OAuth, JWT, or Mintlify-managed private access."
keywords: ['authentication', 'auth', 'OAuth', 'JWT', 'password', 'private']
---
<Info>
Private authentication for your Mintlify organization is available on all plans.
Password authentication requires a [Pro or Enterprise plan](https://mintlify.com/pricing?ref=authentication).
OAuth and JWT authentication require an [Enterprise plan](https://mintlify.com/pricing?ref=authentication).
</Info>
Authentication requires users to log in before accessing your content.
You can configure full authentication for all pages or partial authentication where some pages are public and others require authentication.
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.
| Method | Best for | Plan | Group-based access | API playground prefill | Personalization |
| :--- | :--- | :--- | :---: | :---: | :---: |
| Password | Simple shared access with no per-user tracking | Pro or Enterprise | No | No | No |
| Private authentication | Internal site for members of your Mintlify organization | All plans | No | No | No |
| OAuth 2.0 | Existing identity provider or SSO with per-user sessions | Enterprise | Yes | Yes | Yes |
| JWT | Custom auth backend or embedded content behind your own login | Enterprise | Yes | Yes | Yes |
## Configure authentication
<Tabs>
<Tab title="Password">
<Info>
Password authentication provides access control only and does **not** support user-specific features like group-based access control or API playground prefilling.
</Info>
### Password prerequisites
* Your security requirements allow sharing passwords among users.
### Password setup
<Steps>
<Step title="Create a password.">
1. In your dashboard, go to [Authentication](https://app.mintlify.com/products/authentication).
2. In the **Authentication method** section, set site visibility to **Private**.
3. Click **Password**.
4. Enter a secure password.
5. Click **Save changes**.
After you save, your site redeploys. When it finishes deploying, anyone who visits your site must enter the password to access your content.
</Step>
<Step title="Distribute access.">
Securely share the password and documentation URL with authorized users.
</Step>
</Steps>
### Password example
You host your documentation at `docs.foo.com` and you need basic access control without tracking individual users. You want to prevent public access while keeping setup simple.
**Create a strong password** in your dashboard. **Share credentials** with authorized users.
</Tab>
<Tab title="Private authentication">
### Private authentication prerequisites
* Everyone who needs to access your site must be a member of your Mintlify organization.
### Private authentication setup
<Steps>
<Step title="Enable private authentication.">
1. In your dashboard, go to [Authentication](https://app.mintlify.com/products/authentication).
2. In the **Authentication method** section, set site visibility to **Private**.
3. Click **Authenticated**.
4. Click **Save changes**.
After you save, your site redeploys. When it finishes deploying, anyone who visits your site must log in to your Mintlify organization to access your content.
</Step>
<Step title="Add authorized users.">
1. In your dashboard, go to [Members](https://app.mintlify.com/settings/organization/members).
2. Add each person who should have access to your documentation.
3. Assign appropriate roles based on their editing permissions.
</Step>
</Steps>
### Private example
You host your documentation at `docs.foo.com` and your entire team has access to your dashboard. You want to restrict access to team members only.
**Enable private authentication** in your dashboard settings.
**Verify team access** by checking that all team members are active in your organization.
</Tab>
<Tab title="OAuth 2.0">
### OAuth 2.0 prerequisites
* An OAuth or OIDC server that supports the Authorization Code Flow.
* Ability to create an API endpoint accessible by OAuth access tokens (optional, to enable group-based access control).
### OAuth 2.0 setup
<Steps>
<Step title="Configure your OAuth settings.">
1. In your dashboard, go to [Authentication](https://app.mintlify.com/products/authentication).
2. In the **Authentication method** section, set site visibility to **Private**.
3. Click **Custom**.
4. Click **OAuth**.
5. Configure these fields:
* **Authorization URL**: Your OAuth endpoint.
* **Client ID**: Your OAuth 2.0 client identifier.
* **Client Secret**: Your OAuth 2.0 client secret.
* **Scopes** (optional): Permissions to request. Copy the **entire** scope string (for example, for a scope like `provider.users.docs`, copy the complete `provider.users.docs`). Use multiple scopes if you need different access levels.
* **Additional authorization parameters** (optional): Additional query parameters to add to the initial authorization request.
* **Token URL**: Your OAuth token exchange endpoint.
* **Info API URL** (optional): Endpoint on your server that Mintlify calls to retrieve user info. Use this field for group-based access control. If omitted, the OAuth flow only verifies identity.
* **Logout URL** (optional): The native logout URL for your OAuth provider. When users log out, Mintlify validates the logout redirect against this configured URL for security. The redirect only succeeds if it exactly matches the configured `logoutUrl`. If you do not configure a logout URL, users redirect to `/login`. Mintlify redirects users with a `GET` request and does not append query parameters. Include any parameters (for example, `returnTo`) directly in the URL.
* **Redirect URL** (optional): The URL to redirect users to after authentication.
6. Click **Save changes**.
After you configure your OAuth settings, your site redeploys. When it finishes deploying, anyone who visits your site must log in to your OAuth provider to access your content.
</Step>
<Step title="Configure your OAuth server.">
1. Copy the **Redirect URL** from your [authentication settings](https://app.mintlify.com/products/authentication).
2. Add the redirect URL as an authorized redirect URL for your OAuth server.
</Step>
<Step title="Create your user info endpoint for group access (optional).">
To enable group-based access control, create an API endpoint that:
* Responds to `GET` requests.
* Accepts an `Authorization: Bearer <access_token>` header for authentication.
* Returns user data in the `User` format. See [User data format](#user-data-format) for more information.
Mintlify calls this endpoint with the OAuth access token to retrieve user information. No additional query parameters are sent.
Add this endpoint URL to the **Info API URL** field in your [authentication settings](https://app.mintlify.com/products/authentication).
</Step>
</Steps>
### Use groups from OAuth token claims
If your identity provider includes group membership in the ID token or access token, you can use those claims instead of an Info API URL. This option is available for OAuth configurations that use a client secret.
When configuring OAuth token claims for your deployment, use values such as:
```json
{
"source": "id_token",
"groupsClaim": "groups",
"groupsDelimiter": ","
}
```
* `source`: Selects `id_token` or `access_token`. If you select `id_token`, include the `openid` scope in your OAuth scopes.
* `groupsClaim`: Identifies the token claim that contains groups. Defaults to `groups`.
* `groupsDelimiter`: An optional delimiter from 1 to 4 characters. Mintlify uses it only to split string claim values. If the delimiter can be part of a group name, omit `groupsDelimiter`.
For example, with `"groups": "general,client"` and `groupsDelimiter` set to `","`, Mintlify uses `general` and `client` as separate groups.
Mintlify trims whitespace around each group and ignores empty segments. Without `groupsDelimiter`, the complete string is a single group. Array claims are always one group per string item and are not split.
### OAuth 2.0 example
You host your documentation at `docs.foo.com` and you have an existing OAuth server at `auth.foo.com` that supports the Authorization Code Flow.
**Configure your OAuth server details** in your dashboard:
- **Authorization URL**: `https://auth.foo.com/authorization`
- **Client ID**: `ydybo4SD8PR73vzWWd6S0ObH`
- **Scopes**: `['provider.users.docs']`
- **Token URL**: `https://auth.foo.com/exchange`
- **Info API URL**: `https://api.foo.com/docs/user-info`
- **Logout URL**: `https://auth.foo.com/logout?returnTo=https%3A%2F%2Fdocs.foo.com`
**Create a user info endpoint** at `api.foo.com/docs/user-info`, which requires an OAuth access token with the `provider.users.docs` scope, and returns:
```json
{
"groups": ["engineering", "admin"],
"expiresAt": 1893456000,
"apiPlaygroundInputs": {
"header": {
"Authorization": "Bearer user_abc123"
}
}
}
```
<Note>
Control session length with the `expiresAt` field in your user info response. This is a Unix timestamp (seconds since epoch) indicating when the session should expire. See [User data format](#user-data-format) for more details.
</Note>
**Configure your OAuth server to allow redirects** to your callback URL.
</Tab>
<Tab title="JWT">
### JWT prerequisites
* An authentication system that can generate and sign JWTs.
* A backend service that can create redirect URLs.
### JWT setup
<Steps>
<Step title="Generate a private key.">
1. In your dashboard, go to [Authentication](https://app.mintlify.com/products/authentication).
2. In the **Authentication method** section, set site visibility to **Private**.
3. Click **Custom**.
4. Click **JWT**.
5. Enter the URL of your existing login flow.
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.
</Step>
<Step title="Integrate Mintlify authentication into your login flow.">
Modify your existing login flow to include these steps after user authentication:
* Create a JWT containing the authenticated user's info in the `User` format. See [User data format](#user-data-format) for more information.
* Sign the JWT with your secret key, using the EdDSA algorithm.
* Create a redirect URL back to the `/login/jwt-callback` path of your docs, including the JWT as the hash.
</Step>
</Steps>
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.
<Note>
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.
</Note>
### 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).
Create a login endpoint at `https://foo.com/docs-login` that extends your existing authentication.
After verifying user credentials:
* Generate a JWT with user data in Mintlify's format.
* Sign the JWT and redirect to `https://docs.foo.com/login/jwt-callback#{SIGNED_JWT}`.
<CodeGroup>
```ts TypeScript
import * as jose from 'jose';
import { Request, Response } from 'express';
const TWO_WEEKS_IN_MS = 1000 * 60 * 60 * 24 * 7 * 2;
const DOCS_HOST = 'docs.example.com';
const signingKey = await jose.importPKCS8(process.env.MINTLIFY_PRIVATE_KEY, 'EdDSA');
export async function handleRequest(req: Request, res: Response) {
const user = {
host: DOCS_HOST, // Must match your docs URL
expiresAt: Math.floor((Date.now() + TWO_WEEKS_IN_MS) / 1000), // 2 week session expiration
groups: res.locals.user.groups,
apiPlaygroundInputs: {
header: {
"Authorization": `Bearer ${res.locals.user.apiKey}`,
},
},
};
const jwt = await new jose.SignJWT(user)
.setProtectedHeader({ alg: 'EdDSA' })
.setExpirationTime('10 s') // 10 second JWT expiration
.sign(signingKey);
return res.redirect(`https://${DOCS_HOST}/login/jwt-callback#${jwt}`);
}
```
```python Python
import jwt # pyjwt
import os
from datetime import datetime, timedelta
from fastapi.responses import RedirectResponse
private_key = os.getenv(MINTLIFY_JWT_PEM_SECRET_NAME, '')
DOCS_HOST = 'docs.example.com'
@router.get('/auth')
async def return_mintlify_auth_status(current_user):
jwt_token = jwt.encode(
payload={
'host': DOCS_HOST, # Must match your docs URL
'exp': int((datetime.now() + timedelta(seconds=10)).timestamp()), # 10 second JWT expiration
'expiresAt': int((datetime.now() + timedelta(weeks=2)).timestamp()), # 2 week session expiration
'groups': ['admin'] if current_user.is_admin else [],
'apiPlaygroundInputs': {
'header': {
'Authorization': f'Bearer {current_user.api_key}',
},
},
},
key=private_key,
algorithm='EdDSA'
)
return RedirectResponse(url=f'https://{DOCS_HOST}/login/jwt-callback#{jwt_token}', status_code=302)
```
</CodeGroup>
### Redirect unauthenticated users
When an unauthenticated user tries to access a protected page, the redirect to your login URL preserves the user's intended destination.
1. User attempts to visit a protected page: `https://docs.foo.com/quickstart`.
2. Redirect to your login URL with a redirect query parameter: `https://foo.com/docs-login?redirect=%2Fquickstart`.
3. After authentication, redirect to `https://docs.foo.com/login/jwt-callback?redirect=%2Fquickstart#{SIGNED_JWT}`.
4. User lands in their original destination.
</Tab>
</Tabs>
## Make pages public
When using authentication, all pages require authentication to access by default. You can make specific pages viewable without authentication at the page or group level with the `public` property.
### Individual pages
To make a page public, add `public: true` to the page's frontmatter.
```mdx Public page example
---
title: "Public page"
public: true
---
```
### Groups of pages
To make all pages in a group public, add `"public": true` beneath the group's name in the `navigation` object of your `docs.json`.
```json Public group example
{
"navigation": {
"groups": [
{
"group": "Public group",
"public": true,
"icon": "play",
"pages": [
"quickstart",
"installation",
"settings"
]
},
{
"group": "Private group",
"icon": "pause",
"pages": [
"private-information",
"secret-settings"
]
}
]
}
}
```
## Control access with groups
When you use OAuth or JWT authentication, you can restrict specific pages to certain user groups. This is useful when you want different users to see different content based on their role or attributes.
Manage groups through user data passed during authentication. See [User data format](#user-data-format) for details.
```json Example user info
{
"groups": ["admin", "beta-users"],
"expiresAt": 1893456000
}
```
Specify which groups can access specific pages using the `groups` property in frontmatter.
```mdx Example page restricted to the admin group highlight={3}
---
title: "Admin dashboard"
groups: ["admin"]
---
```
Users must belong to at least one of the listed groups to access the page. If a user tries to access a page without the required group, they'll receive a 404 error.
### How groups interact with public pages
- All pages require authentication by default.
- Pages with a `groups` property are only accessible to authenticated users in those groups.
- Pages without a `groups` property are accessible to all authenticated users.
- Pages with `public: true` and no `groups` property are accessible to everyone.
<CodeGroup>
```mdx Public page
---
title: "Public guide"
public: true
---
```
```mdx Protected page
---
title: "API reference"
---
```
```mdx Protected page with groups
---
title: "Advanced configurations"
groups: ["pro", "enterprise"]
---
```
</CodeGroup>
## User data format
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).
<CodeGroup>
```tsx Format
type User = {
host?: string;
expiresAt?: number;
groups?: string[];
content?: Record<string, any>;
apiPlaygroundInputs?: {
server?: Record<string, string>;
header?: Record<string, unknown>;
query?: Record<string, unknown>;
cookie?: Record<string, unknown>;
path?: Record<string, unknown>;
};
};
```
```json Example
{
"host": "docs.example.com",
"expiresAt": 1893456000,
"groups": ["admin", "beta-users"],
"content": {
"firstName": "Jane",
"company": "Acme Corp"
},
"apiPlaygroundInputs": {
"header": {
"Authorization": "Bearer user_abc123"
},
"server": {
"baseUrl": "https://api.foo.com"
}
}
}
```
</CodeGroup>
<ParamField path="host" type="string">
**Required for JWT authentication.** The hostname of your documentation site. The string must exactly match the domain where you deploy your documentation. Mintlify validates that the JWT's host matches the requesting host to prevent token reuse across different sites.
</ParamField>
<ParamField path="expiresAt" type="number">
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.
<Warning>**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).</Warning>
</ParamField>
<ParamField path="groups" type="string[]">
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"]` matches content tagged with either the `admin` or `engineering` groups.
</ParamField>
<ParamField path="content" type="Record<string, any>">
Custom data accessible in MDX pages via the `user` variable for [personalized content](/create/personalization#dynamic-mdx-content).
</ParamField>
<ParamField path="apiPlaygroundInputs" type="object">
Prefills API playground fields with user-specific values. When a user authenticates, these values populate the corresponding input fields in the API playground. Users can override prefilled values, and their overrides persist in local storage.
Mintlify applies only values that match the current endpoint's security scheme.
<Expandable title="properties">
<ParamField path="header" type="Record<string, unknown>">
Header values to prefill, keyed by header name.
</ParamField>
<ParamField path="query" type="Record<string, unknown>">
Query parameter values to prefill, keyed by parameter name.
</ParamField>
<ParamField path="cookie" type="Record<string, unknown>">
Cookie values to prefill, keyed by cookie name.
</ParamField>
<ParamField path="server" type="Record<string, string>">
Server variable values to prefill, keyed by variable name.
</ParamField>
<ParamField path="path" type="Record<string, unknown>">
Path parameter values to prefill, keyed by parameter name.
</ParamField>
</Expandable>
</ParamField>
## Feature availability
Some features behave differently or are unavailable when you enable authentication.
| Feature | Public | Fully authenticated (all pages protected) | Partially authenticated (some public pages) |
| :------ | :---------- | :---------------------------------- | :-------------------------------- |
| [llms.txt and llms-full.txt](/ai/llmstxt) | Full support | Available behind authentication, so AI tools may not be able to access the files | Publicly accessible, reflecting public pages only |
| [MCP server](/ai/model-context-protocol) | Full support | Requires authentication to connect | Available without authentication for public pages and with authentication for protected pages |
| [Markdown export](/ai/markdown-export) | Full support | Full support, respects user groups | Full support, respects user groups |
| [PDF export](/optimize/pdf-exports) | Full support | Full support, respects user groups. Authenticated pages export with images and assets included. | Full support, respects user groups. Authenticated pages export with images and assets included. |
| [Search](/assistant/index) | Full support | Full support, respects user groups | Full support, respects user groups |
| [Assistant](/assistant/index) | Full support | Full support, respects user groups | Full support, respects user groups |
| [skill.md](/ai/skillmd) | Full support | Not supported | Not supported |
| [Sitemap](/optimize/seo#sitemaps-and-robotstxt-files) | Full support | Available behind authentication, but excludes pages in groups | Available behind authentication, but excludes pages in groups |
| [robots.txt](/optimize/seo#sitemaps-and-robotstxt-files) | Full support | Available behind authentication | Available behind authentication |
| [Live preview](/editor/review#live-preview) | Full support | Supported for Mintlify authentication | Supported for Mintlify authentication |