Authentication setup
Set up user authentication to control access to pages and API references using password, OAuth, JWT, or Mintlify-managed private access.
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. For example, example.com/docs.
To identify visitors while keeping pages public, use personalization. Personalization supports custom subpaths and can prefill API playground inputs without requiring visitors to authenticate before viewing a page.
Choose an authentication method
Section titled “Choose an authentication method”Use this comparison to pick the method that fits your use case. See 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
Section titled “Configure authentication”Password prerequisites
- Your security requirements allow sharing passwords among users.
Password setup
Create a password.
- In your dashboard, go to Access.
- Set Visibility to Private.
- Set Method to Password.
- Enter a secure password.
- Click Save.
After you save, your site redeploys. When it finishes deploying, anyone who visits your site must enter the password to access your content.
Distribute access.
Securely share the password and documentation URL with authorized users.
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.
Private authentication prerequisites
- Everyone who needs to access your site must be a member of your Mintlify organization.
Private authentication setup
Enable private authentication.
- In your dashboard, go to Access.
- Set Visibility to Private.
- Set Method to Authenticated.
- Click Save.
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.
Add authorized users.
- In your dashboard, go to Members.
- Add each person who should have access to your documentation.
- Assign appropriate roles based on their editing permissions.
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.
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
Configure your OAuth settings.
- In your dashboard, go to Access.
- Set Visibility to Private.
- Set Method to Custom.
- Click OAuth.
- 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 completeprovider.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 aGETrequest and does not append query parameters. Include any parameters (for example,returnTo) directly in the URL.
- Click Save.
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.
Configure your OAuth server.
- Copy the Redirect URL from your Access settings.
- Add the redirect URL as an authorized redirect URL for your OAuth server.
Create your user info endpoint for group access (optional).
To enable group-based access control, create an API endpoint that:
- Responds to
GETrequests. - Accepts an
Authorization: Bearer <access_token>header for authentication. - Returns user data in the
Userformat. See 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 Access settings.
- Responds to
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 project, use values such as:
{
"source": "id_token",
"groupsClaim": "groups",
"groupsDelimiter": ","
}source: Selectsid_tokenoraccess_token. If you selectid_token, include theopenidscope in your OAuth scopes.groupsClaim: Identifies the token claim that contains groups. Defaults togroups.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, omitgroupsDelimiter.
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:
{
"groups": ["engineering", "admin"],
"expiresAt": 1893456000,
"apiPlaygroundInputs": {
"header": {
"Authorization": "Bearer user_abc123"
}
}
}Configure your OAuth server to allow redirects to your callback URL.
JWT prerequisites
- An authentication system that can generate and sign JWTs.
- A backend service that can create redirect URLs.
JWT setup
Generate a private key.
- In your dashboard, go to Access.
- Set Visibility to Private.
- Set Method to Custom.
- Click JWT.
- Enter the URL of your existing login flow.
- 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.
- Click Save.
- Click Generate new key.
- 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.
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
Userformat. See 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-callbackpath of your docs, including the JWT as the hash.
- Create a JWT containing the authenticated user's info in the
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.
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}.
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}`);
}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)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.
- User attempts to visit a protected page:
https://docs.foo.com/quickstart. - Redirect to your login URL with a redirect query parameter:
https://foo.com/docs-login?redirect=%2Fquickstart. - After authentication, redirect to
https://docs.foo.com/login/jwt-callback?redirect=%2Fquickstart#{SIGNED_JWT}. - User lands in their original destination.
Make pages public
Section titled “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
Section titled “Individual pages”To make a page public, add public: true to the page's frontmatter.
---
title: "Public page"
public: true
---Groups of pages
Section titled “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.
{
"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
Section titled “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 for details.
{
"groups": ["admin", "beta-users"],
"expiresAt": 1893456000
}Specify which groups can access specific pages using the groups property in frontmatter.
---
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
Section titled “How groups interact with public pages”- All pages require authentication by default.
- Pages with a
groupsproperty are only accessible to authenticated users in those groups. - Pages without a
groupsproperty are accessible to all authenticated users. - Pages with
public: trueand nogroupsproperty are accessible to everyone.
---
title: "Public guide"
public: true
------
title: "API reference"
------
title: "Advanced configurations"
groups: ["pro", "enterprise"]
---User data format
Section titled “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.
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>;
};
};{
"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"
}
}
}-
host(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. -
expiresAt(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.
groups(string[]) — List of groups the user belongs to. With authentication, pages with matchinggroupsin 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.
-
content(Record<string, any>) — Custom data accessible in MDX pages via theuservariable for personalized content. -
apiPlaygroundInputs(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.
properties
header(Record<string, unknown>) — Header values to prefill, keyed by header name.query(Record<string, unknown>) — Query parameter values to prefill, keyed by parameter name.cookie(Record<string, unknown>) — Cookie values to prefill, keyed by cookie name.server(Record<string, string>) — Server variable values to prefill, keyed by variable name.path(Record<string, unknown>) — Path parameter values to prefill, keyed by parameter name.
Feature availability
Section titled “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 | Full support | Available behind authentication, so AI tools may not be able to access the files | Publicly accessible, reflecting public pages only |
| MCP server | Full support | Requires authentication to connect | Available without authentication for public pages and with authentication for protected pages |
| Markdown export | Full support | Full support, respects user groups | Full support, respects user groups |
| PDF export | 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 | Full support | Full support, respects user groups | Full support, respects user groups |
| Assistant | Full support | Full support, respects user groups | Full support, respects user groups |
| skill.md | Full support | Not supported | Not supported |
| Sitemap | Full support | Available behind authentication, but excludes pages in groups | Available behind authentication, but excludes pages in groups |
| robots.txt | Full support | Available behind authentication | Available behind authentication |
| Live preview | Full support | Supported for Mintlify authentication | Supported for Mintlify authentication |