diff --git a/fern/docs.yml b/fern/docs.yml
index 5cbb9334c..44384fd0a 100644
--- a/fern/docs.yml
+++ b/fern/docs.yml
@@ -351,7 +351,7 @@ redirects:
- source: /learn/docs/building-and-customizing-your-docs/custom-domain
destination: /learn/docs/preview-publish/setting-up-your-domain
- source: /learn/docs/building-and-customizing-your-docs/rbac
- destination: /learn/docs/authentication/rbac
+ destination: /learn/docs/authentication/features/rbac
- source: /learn/docs/building-and-customizing-your-docs/search
destination: /learn/docs/customization/search
@@ -395,13 +395,49 @@ redirects:
destination: /learn/docs/api-references/customize-api-reference-layout#configuration-options
- source: /learn/docs/api-references/server-urls
- destination: /learn/docs/api-references/api-explorer/overview#multiple-environments
- - source: /learn/docs/api-references/api-explorer
- destination: /learn/docs/api-references/api-explorer/overview
+ destination: /learn/docs/api-references/api-explorer#multiple-environments
- source: /learn/docs/api-references/api-playground/:slug*
destination: /learn/docs/api-references/api-explorer/:slug*
- source: /learn/docs/api-references/api-playground
- destination: /learn/docs/api-references/api-explorer/overview
+ destination: /learn/docs/api-references/api-explorer
+
+ # Authentication redirects — old locations → new Setup/Features structure
+ - source: /learn/docs/authentication/password-protection
+ destination: /learn/docs/authentication/setup/password-protection
+ - source: /learn/docs/authentication/sso
+ destination: /learn/docs/authentication/setup/sso
+ - source: /learn/docs/authentication/jwt
+ destination: /learn/docs/authentication/setup/jwt
+ - source: /learn/docs/authentication/oauth
+ destination: /learn/docs/authentication/setup/oauth
+ - source: /learn/docs/authentication/rbac
+ destination: /learn/docs/authentication/features/rbac
+ - source: /learn/docs/authentication/api-key-injection
+ destination: /learn/docs/authentication/features/api-key-injection
+
+ # Authentication redirects — legacy paths
+ - source: /learn/docs/authentication/rbac/overview
+ destination: /learn/docs/authentication/features/rbac
+ - source: /learn/docs/authentication/api-key-injection/overview
+ destination: /learn/docs/authentication/features/api-key-injection
+ - source: /learn/docs/authentication/set-up-oauth
+ destination: /learn/docs/authentication/setup/oauth
+ - source: /learn/docs/authentication/jwt-setup
+ destination: /learn/docs/authentication/setup/jwt
+ - source: /learn/docs/authentication/oauth-setup
+ destination: /learn/docs/authentication/setup/oauth
+ - source: /learn/docs/authentication/auth-setup/jwt-setup
+ destination: /learn/docs/authentication/setup/jwt
+ - source: /learn/docs/authentication/auth-setup/oauth-setup
+ destination: /learn/docs/authentication/setup/oauth
+ - source: /learn/docs/authentication/rbac/jwt-setup
+ destination: /learn/docs/authentication/setup/jwt
+ - source: /learn/docs/authentication/rbac/oauth-setup
+ destination: /learn/docs/authentication/setup/oauth
+ - source: /learn/docs/authentication/api-key-injection/jwt-setup
+ destination: /learn/docs/authentication/setup/jwt
+ - source: /learn/docs/authentication/api-key-injection/oauth-setup
+ destination: /learn/docs/authentication/setup/oauth
- source: /learn/user-feedback
destination: /learn/docs/user-feedback
- source: /learn/docs/customization/what-is-docs-yml
@@ -648,7 +684,7 @@ redirects:
- source: /learn/docs/alternatives/mintlify
destination: /learn/docs/getting-started/overview
- source: /learn/docs/api-references/api-explorer/advanced-configuration
- destination: /learn/docs/api-references/api-explorer/overview
+ destination: /learn/docs/api-references/api-explorer
- source: /learn/docs/enterprise/self-hosted
destination: /learn/docs/self-hosted/overview
- source: /learn/docs/content/visual-editor-beta
diff --git a/fern/products/api-def/openapi-pages/extensions/explorer.mdx b/fern/products/api-def/openapi-pages/extensions/explorer.mdx
index a3056e3b3..43c4eac45 100644
--- a/fern/products/api-def/openapi-pages/extensions/explorer.mdx
+++ b/fern/products/api-def/openapi-pages/extensions/explorer.mdx
@@ -4,7 +4,7 @@ headline: API Explorer control (OpenAPI)
description: Enable or disable the API Explorer using `x-fern-explorer`
---
-The [API Explorer](/learn/docs/api-references/api-explorer/overview) is enabled by default for all endpoints. Use `x-fern-explorer` to disable or it globally or to override it per operation. This is commonly used to disable the Explorer for destructive operations, payment processing, or admin-only endpoints.
+The [API Explorer](/learn/docs/api-references/api-explorer) is enabled by default for all endpoints. Use `x-fern-explorer` to disable or it globally or to override it per operation. This is commonly used to disable the Explorer for destructive operations, payment processing, or admin-only endpoints.
## Global
@@ -55,4 +55,4 @@ paths:
post:
operationId: charge_payment
# Remains disabled (global setting applies)
-```
\ No newline at end of file
+```
diff --git a/fern/products/docs/docs.yml b/fern/products/docs/docs.yml
index c2c8e18a6..c0b58dd19 100644
--- a/fern/products/docs/docs.yml
+++ b/fern/products/docs/docs.yml
@@ -255,13 +255,9 @@ navigation:
path: ./pages/api-references/sdk-snippets.mdx
- page: HTTP snippets
path: ./pages/api-references/http-snippets.mdx
- - section: API Explorer
+ - page: API Explorer
+ path: ./pages/api-references/api-explorer.mdx
slug: api-explorer
- contents:
- - page: Overview
- path: ./pages/api-references/api-explorer.mdx
- - page: Auto-populate API keys
- path: ./pages/api-references/autopopulate-api-key.mdx
- page: Audiences
path: ./pages/api-references/audiences.mdx
- page: Customize API Reference layout
@@ -294,18 +290,27 @@ navigation:
contents:
- page: Overview
path: ./pages/authentication/overview.mdx
- - page: Role based access control (RBAC)
- path: ./pages/authentication/rbac.mdx
- slug: rbac
- - page: Set up OAuth for RBAC
- path: ./pages/authentication/set-up-oauth.mdx
- slug: set-up-oauth
- - page: API key injection
- path: ./pages/api-references/autopopulate-api-key.mdx
- - page: SSO
- path: ./pages/authentication/sso.mdx
- - page: Password protection
- path: ./pages/authentication/password.mdx
+ - section: Setup
+ slug: setup
+ contents:
+ - page: Password protection
+ path: ./pages/authentication/password.mdx
+ - page: SSO
+ path: ./pages/authentication/sso.mdx
+ - page: JWT
+ path: ./pages/authentication/jwt-setup.mdx
+ slug: jwt
+ - page: OAuth
+ path: ./pages/authentication/oauth-setup.mdx
+ slug: oauth
+ - section: Features
+ slug: features
+ contents:
+ - page: Role-based access control (RBAC)
+ path: ./pages/authentication/rbac.mdx
+ slug: rbac
+ - page: API key injection
+ path: ./pages/authentication/api-key-injection.mdx
- section: Security
collapsed: true
contents:
diff --git a/fern/products/docs/pages/api-references/api-explorer.mdx b/fern/products/docs/pages/api-references/api-explorer.mdx
index 9d5b29056..501f97bce 100644
--- a/fern/products/docs/pages/api-references/api-explorer.mdx
+++ b/fern/products/docs/pages/api-references/api-explorer.mdx
@@ -22,6 +22,8 @@ Once a user sets their authentication credentials, their credentials persist thr
Authentication credentials are only stored client-side using cookies. No sensitive user information is collected or stored.
+To automatically populate API keys for logged-in users, see [API key injection](/learn/docs/authentication/features/api-key-injection).
+
## Multiple environments
When multiple server URLs are configured in [OpenAPI](/learn/api-definitions/openapi/extensions/server-names) or the [Fern Definition](/learn/api-definition/fern/api-yml/environments), users can switch between environments (e.g., production and sandbox) from a dropdown in the API Explorer. The selected environment persists as they navigate between pages.
diff --git a/fern/products/docs/pages/api-references/autopopulate-api-key.mdx b/fern/products/docs/pages/api-references/autopopulate-api-key.mdx
deleted file mode 100644
index ed2383e0e..000000000
--- a/fern/products/docs/pages/api-references/autopopulate-api-key.mdx
+++ /dev/null
@@ -1,308 +0,0 @@
----
-title: Autopopulate API keys
-subtitle: Allow users to log in and automatically populate their API keys in the API Explorer.
----
-
-
-
-Fern can integrate with your authentication flow, allowing users to log in and have their API key automatically populated with the click of a button.
-
-
-
-
-User credentials are stored only in browser cookies and never transmitted to Fern's servers. Learn more in the [Security overview](/learn/docs/security/overview).
-
-
-## The `fern` payload
-
-API key injection works by reading a `fern` claim from a JWT stored in the user's browser. The `fern` claim contains a `playground` object that tells the API Explorer what values to pre-fill.
-
-The top-level structure is:
-
-```json
-{
- "fern": {
- "playground": {
- "initial_state": { ... },
- "env_state": { ... }
- }
- }
-}
-```
-
-| Field | Description |
-|---|---|
-| `initial_state` | Default values applied to all API Explorer requests. |
-| `env_state` | Per-environment overrides that are merged on top of `initial_state` when the user selects a matching environment. |
-
-### Auth types
-
-The `initial_state.auth` object supports bearer token and basic authentication.
-
-
-```json Bearer
-{
- "fern": {
- "playground": {
- "initial_state": {
- "auth": {
- "bearer_token": "eyJhbGciOiJIUzI1c"
- }
- }
- }
- }
-}
-```
-
-```json Basic
-{
- "fern": {
- "playground": {
- "initial_state": {
- "auth": {
- "basic": {
- "username": "your_username",
- "password": "your_password"
- }
- }
- }
- }
- }
-}
-```
-
-
-### Custom headers and parameters
-
-You can also pre-fill headers, path parameters, and query parameters instead of (or along with) auth credentials. Values are applied to any matching fields in the API Explorer.
-
-```json
-{
- "fern": {
- "playground": {
- "initial_state": {
- "auth": {
- "bearer_token": "eyJhbGciOiJIUzI1c"
- },
- "headers": {
- "API-Version": "2024-02-02"
- },
- "path_parameters": {
- "plantId": "plant_1234"
- },
- "query_parameters": {
- "sort": "DESCENDING"
- }
- }
- }
- }
-}
-```
-
-### Multiple API keys
-
-To provide multiple API keys (for example, one per application), set `bearer_token` to a JSON-encoded array of key-value objects. Each object maps an application name to its key.
-
-```json
-{
- "fern": {
- "playground": {
- "initial_state": {
- "auth": {
- "bearer_token": "[{\"Greenhouse app\": \"sk-greenhouse-abc123\"}, {\"Garden app\": \"sk-garden-def456\"}]"
- }
- }
- }
- }
-}
-```
-
-The API Explorer displays a dropdown so the user can choose which application key to use for requests.
-
-### Per-environment keys
-
-Use `env_state` to provide different credentials for each environment (for example, production vs. staging). Each key in `env_state` is matched against the selected environment URL using substring matching.
-
-```json
-{
- "fern": {
- "playground": {
- "initial_state": {
- "auth": {
- "bearer_token": "default-token"
- }
- },
- "env_state": {
- "prod": {
- "auth": {
- "bearer_token": "prod-token-abc123"
- }
- },
- "staging": {
- "auth": {
- "bearer_token": "staging-token-def456"
- }
- }
- }
- }
- }
-}
-```
-
-When the user selects an environment containing `prod` (such as `https://api.prod.example.com`), the API Explorer uses `prod-token-abc123`. The `env_state` values are merged on top of `initial_state`: auth is replaced entirely, while headers, path parameters, and query parameters are shallow-merged.
-
-You can combine multiple API keys with per-environment keys:
-
-```json
-{
- "fern": {
- "playground": {
- "env_state": {
- "prod": {
- "auth": {
- "bearer_token": "[{\"Greenhouse app\": \"sk-prod-abc\"}, {\"Garden app\": \"sk-prod-def\"}]"
- }
- },
- "dev": {
- "auth": {
- "bearer_token": "[{\"Greenhouse app\": \"sk-dev-abc\"}, {\"Garden app\": \"sk-dev-def\"}]"
- }
- }
- }
- }
- }
-}
-```
-
-## Integrating with your auth flow
-
-API key injection requires a JWT containing the `fern` payload described above to be stored in the user's browser as a cookie called `fern_token`. There are two ways to set this up:
-
-* **JWT:** You handle the entire auth flow and set the `fern_token` cookie yourself.
-* **OAuth:** You give Fern access, and Fern initiates the OAuth handshake process directly.
-
-
-
-
-1. When a user clicks the **Login** button in the API Explorer, they're redirected to your authentication page.
-2. After successful authentication, your system sets a cookie called `fern_token` in the user's browser.
-3. This token is a [JWT](https://jwt.io) signed with a secret key from Fern. The JWT payload contains the `fern` claim with the user's API key.
-
-
-
-
-
-#### Set up
-
-To enable API key injection with JWT:
-1. Reach out to Fern to get your secret key.
-1. Send Fern the URL of your authentication page. This is where users are redirected after clicking the **Login** button in the API Explorer.
-1. Add logic to your service to set the `fern_token` cookie when a user logs in.
-
-
-This Next.js endpoint handles the callback from your authentication page. It reads the `state` parameter to determine where to redirect the user, mints a `fern_token` JWT using [jose](https://github.com/panva/jose), sets it as a cookie, and redirects the user back to the docs.
-
-```typescript title="app/api/fern-token/route.ts"
-import { SignJWT } from "jose";
-import { cookies } from "next/headers";
-import { type NextRequest, NextResponse } from "next/server";
-
-export async function GET(req: NextRequest): Promise {
- const domain = getDomain(req); // your logic to determine the docs domain
-
- // use the state param to determine redirect location
- const returnTo = req.nextUrl.searchParams.get("state");
- const redirectLocation = returnTo ?? `https://${domain}`;
-
- // fetch the user's API key and secret (from your config or database)
- const apiKey = await getApiKeyForUser();
- const secret = await getSecretForDomain(domain);
-
- if (!apiKey || !secret) {
- // redirect with an error if credentials are missing
- const url = new URL(redirectLocation);
- url.searchParams.set("error", "missing_credentials");
- return NextResponse.redirect(url);
- }
-
- // mint the JWT using the secret key
- const fernToken = await mintFernToken({ secret, apiKey });
-
- if (!fernToken) {
- const url = new URL(redirectLocation);
- url.searchParams.set("error", "token_creation_failed");
- return NextResponse.redirect(url);
- }
-
- // set the fern_token as a cookie on the docs domain
- const cookieJar = await cookies();
- cookieJar.set("fern_token", fernToken, {
- httpOnly: true,
- secure: true,
- sameSite: "lax",
- domain,
- });
-
- // redirect the user back to the docs
- return NextResponse.redirect(redirectLocation);
-}
-
-const encoder = new TextEncoder();
-
-async function mintFernToken({
- secret,
- apiKey,
- payload,
-}: {
- secret: string;
- apiKey: string;
- payload?: Record;
-}): Promise {
- const jwtPayload = payload ?? {
- fern: {
- playground: {
- initial_state: {
- auth: {
- bearer_token: apiKey,
- },
- },
- },
- },
- };
-
- return await new SignJWT(jwtPayload)
- .setProtectedHeader({ alg: "HS256", typ: "JWT" })
- .setIssuedAt()
- .setExpirationTime("1d") // set to any value
- .setIssuer("https://buildwithfern.com")
- .sign(encoder.encode(secret)); // sign using the secret provided by Fern
-}
-```
-
-
-
-
-
-1. When a user clicks the **Login** button in the API Explorer, Fern initiates an OAuth flow by making a request to your authorization endpoint.
-1. The user is redirected to your OAuth provider's login page where they authenticate using your existing auth system.
-1. After successful authentication, your OAuth provider redirects back to Fern with an authorization code, which Fern exchanges for an access token at your token endpoint.
-1. Fern sets a `fern_token` cookie containing the user's authentication credentials and automatically populates their API key in the API Explorer.
-
-
-
-
-
-#### Set up
-
-To enable API key injection with OAuth:
-1. Set up an authenticated account for Fern so Fern can authorize users on your behalf.
-1. Configure your OAuth application to return user API keys when Fern requests them.
-
-Then, send Fern the following items:
-- The client ID and client secret for Fern's authenticated account
-- The URL of your authentication endpoint. This is where users are redirected after clicking the **Login** button in the API Explorer.
-- The URL of your token endpoint. This is where Fern exchanges codes for tokens.
-
-
-
-
diff --git a/fern/products/docs/pages/ask-fern/features.mdx b/fern/products/docs/pages/ask-fern/features.mdx
index 1f99277e1..d38c1b6fc 100644
--- a/fern/products/docs/pages/ask-fern/features.mdx
+++ b/fern/products/docs/pages/ask-fern/features.mdx
@@ -63,7 +63,7 @@ https://{{PAGE_URL}}?query={{QUERY2}}
## Role-based access control
-Ask Fern automatically respects the [role-based access control (RBAC) settings configured in your documentation](/learn/docs/authentication/rbac). When users query Ask Fern, they only receive answers from documentation they have permission to access based on their assigned roles.
+Ask Fern automatically respects the [role-based access control (RBAC) settings configured in your documentation](/learn/docs/authentication/features/rbac). When users query Ask Fern, they only receive answers from documentation they have permission to access based on their assigned roles.
This works at all levels, from entire sections down to individual pages and conditional content within pages.
diff --git a/fern/products/docs/pages/ask-fern/slack-app.mdx b/fern/products/docs/pages/ask-fern/slack-app.mdx
index 27b80bf5f..19a635df8 100644
--- a/fern/products/docs/pages/ask-fern/slack-app.mdx
+++ b/fern/products/docs/pages/ask-fern/slack-app.mdx
@@ -62,7 +62,7 @@ Use the `/fern` slash command in any channel to adjust the settings:
| Command | Description | Example |
|---------|-------------|---------|
| **respond_to** | Controls whether the Ask Fern bot responds to all messages (`all`), reponds only when directly mentioned with `@Ask Fern` (`mentions_only`), or determines when to respond to messages depending on context (`auto`). Set to `auto` by default. | `/fern respond_to all` |
-| **roles** | Specifies which RBAC roles (comma-separated) should be used to filter Ask Fern responses (if you have [role-based access control](/docs/authentication/rbac) configured) | `/fern roles developer,admin` |
+| **roles** | Specifies which RBAC roles (comma-separated) should be used to filter Ask Fern responses (if you have [role-based access control](/learn/docs/authentication/features/rbac) configured) | `/fern roles developer,admin` |
| **show** | Show the current settings | `/fern show` |
| **help** | Get help with Ask Fern slash commands | `/fern help` |
diff --git a/fern/products/docs/pages/ask-fern/what-is-ask-fern.mdx b/fern/products/docs/pages/ask-fern/what-is-ask-fern.mdx
index 26f3bdbf1..5c3f961dd 100644
--- a/fern/products/docs/pages/ask-fern/what-is-ask-fern.mdx
+++ b/fern/products/docs/pages/ask-fern/what-is-ask-fern.mdx
@@ -68,7 +68,7 @@ ai-search:
Fern automatically processes your documentation pages and Fern-generated SDK code, breaking them into semantic chunks and converting each chunk into a vector using sentence embedding models. They're stored in a database that serves as Ask Fern's search index.
- When users ask questions, Ask Fern vectorizes their query and searches the database to find the most relevant documentation and code chunks. If you have [role-based access control](/learn/docs/authentication/rbac) configured, Ask Fern filters results based on the user's permissions.
+ When users ask questions, Ask Fern vectorizes their query and searches the database to find the most relevant documentation and code chunks. If you have [role-based access control](/learn/docs/authentication/features/rbac) configured, Ask Fern filters results based on the user's permissions.
Ask Fern uses the retrieved chunks as context to generate accurate answers with [citations](/learn/docs/ai-features/ask-fern/features#citations) for the user. If the initial context isn't sufficient, it performs an additional keyword search.
diff --git a/fern/products/docs/pages/authentication/api-key-injection.mdx b/fern/products/docs/pages/authentication/api-key-injection.mdx
new file mode 100644
index 000000000..5159bc695
--- /dev/null
+++ b/fern/products/docs/pages/authentication/api-key-injection.mdx
@@ -0,0 +1,103 @@
+---
+title: API key injection
+subtitle: Automatically populate API keys in the API Explorer for logged-in users.
+---
+
+
+
+API key injection is a feature of [JWT](/learn/docs/authentication/setup/jwt) and [OAuth](/learn/docs/authentication/setup/oauth) authentication. When a user logs in, a [`fern_token`](/learn/docs/authentication/overview#how-authentication-works) cookie is set in their browser with a `fern.playground` claim that tells the [API Explorer](/learn/docs/api-references/api-explorer) what values to pre-fill — API keys, headers, or other credentials. You can combine it with [RBAC](/learn/docs/authentication/features/rbac) in a single token.
+
+
+
+
+User credentials are stored only in browser cookies and never transmitted to Fern's servers. Learn more in the [Security overview](/learn/docs/security/overview).
+
+
+## Setup
+
+To enable API key injection, follow the [JWT](/learn/docs/authentication/setup/jwt) or [OAuth](/learn/docs/authentication/setup/oauth) setup guide.
+
+### Advanced payload configuration
+
+With [JWT setup](/learn/docs/authentication/setup/jwt), you have full control over the `fern.playground` payload. These options let you go beyond a single bearer token — pre-filling custom headers, supporting multiple API keys, or varying credentials by environment. These options are not available with OAuth, where Fern manages the token.
+
+
+
+You can pre-fill headers, path parameters, and query parameters alongside auth credentials:
+
+```json maxLines=10 startLine=5
+{
+ "fern": {
+ "playground": {
+ "initial_state": {
+ "auth": {
+ "bearer_token": "eyJhbGciOiJIUzI1c"
+ },
+ "headers": {
+ "API-Version": "2024-02-02"
+ },
+ "path_parameters": {
+ "plantId": "plant_1234"
+ },
+ "query_parameters": {
+ "sort": "DESCENDING"
+ }
+ }
+ }
+ }
+}
+```
+
+
+
+To provide multiple API keys (for example, one per application), set `bearer_token` to a JSON-encoded array of key-value objects. Each object maps an application name to its key.
+
+```json maxLines=5 startLine=2
+{
+ "fern": {
+ "playground": {
+ "initial_state": {
+ "auth": {
+ "bearer_token": "[{\"Greenhouse app\": \"sk-greenhouse-abc123\"}, {\"Garden app\": \"sk-garden-def456\"}]"
+ }
+ }
+ }
+ }
+}
+```
+
+The API Explorer displays a dropdown so the user can choose which application key to use for requests.
+
+
+
+Use `env_state` to provide different credentials for each environment (for example, production vs. staging). Each key in `env_state` is matched against the selected environment URL using substring matching.
+
+```json maxLines=10 startLine=2
+{
+ "fern": {
+ "playground": {
+ "initial_state": {
+ "auth": {
+ "bearer_token": "default-token"
+ }
+ },
+ "env_state": {
+ "prod": {
+ "auth": {
+ "bearer_token": "prod-token-abc123"
+ }
+ },
+ "staging": {
+ "auth": {
+ "bearer_token": "staging-token-def456"
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+When the user selects an environment containing `prod` (such as `https://api.prod.example.com`), the API Explorer uses `prod-token-abc123`. The `env_state` values are merged on top of `initial_state`: auth is replaced entirely, while headers, path parameters, and query parameters are shallow-merged.
+
+
diff --git a/fern/products/docs/pages/authentication/jwt-setup.mdx b/fern/products/docs/pages/authentication/jwt-setup.mdx
new file mode 100644
index 000000000..0d1ab2de7
--- /dev/null
+++ b/fern/products/docs/pages/authentication/jwt-setup.mdx
@@ -0,0 +1,170 @@
+---
+title: Set up JWT
+subtitle: Self-managed authentication integrated with your login system
+---
+
+With JWT, you manage the entire auth flow. This involves building and signing a [`fern_token`](/learn/docs/authentication/overview#how-authentication-works) cookie that integrates your docs with your existing login system. Like [OAuth](/learn/docs/authentication/setup/oauth), JWT enables:
+
+- **Login only** — gate docs behind authentication
+- **[RBAC](/learn/docs/authentication/features/rbac)** — restrict content by user role
+- **[API key injection](/learn/docs/authentication/features/api-key-injection)** — pre-fill API keys in the [API Explorer](/learn/docs/api-references/api-explorer)
+
+## How it works
+
+1. A user clicks **Login** on your docs site and is redirected to your authentication page.
+2. After authentication, your system signs a [JWT](https://jwt.io) with a secret key from Fern and sets it as a `fern_token` cookie.
+3. Fern reads the token to determine the user's access and credentials.
+
+
+
+
+
+## Configuration
+
+
+
+Reach out to Fern to get your secret key and send them the URL of your authentication page. This is where users are redirected after clicking **Login**.
+
+
+
+The JWT payload must include a `fern` claim. What you include in the token's `fern` claim controls which features are enabled: login only, RBAC, or API key injection.
+
+
+```json Login only
+{
+ "fern": {}
+}
+```
+
+```json RBAC
+{
+ "fern": {
+ "roles": ["partners"]
+ }
+}
+```
+
+```json API key injection
+{
+ "fern": {
+ "playground": {
+ "initial_state": {
+ "auth": { "bearer_token": "eyJhbGciOiJIUzI1c" }
+ }
+ }
+ }
+}
+```
+
+```json API key injection + RBAC
+{
+ "fern": {
+ "roles": ["partners"],
+ "playground": {
+ "initial_state": {
+ "auth": { "bearer_token": "eyJhbGciOiJIUzI1c" }
+ }
+ }
+ }
+}
+```
+
+
+
+
+Add logic to your service to sign the JWT and set it as a `fern_token` cookie when a user logs in.
+
+
+This Next.js endpoint handles the callback from your authentication page. It reads the `state` parameter to determine where to redirect the user, mints a `fern_token` JWT using [jose](https://github.com/panva/jose), sets it as a cookie, and redirects the user back to the docs.
+
+```typescript title="app/api/fern-token/route.ts"
+import { SignJWT } from "jose";
+import { cookies } from "next/headers";
+import { type NextRequest, NextResponse } from "next/server";
+
+export async function GET(req: NextRequest): Promise {
+ const domain = getDomain(req); // your logic to determine the docs domain
+
+ // use the state param to determine redirect location
+ const returnTo = req.nextUrl.searchParams.get("state");
+ const redirectLocation = returnTo ?? `https://${domain}`;
+
+ // fetch the user's API key, roles, and secret (from your config or database)
+ const apiKey = await getApiKeyForUser();
+ const roles = await getRolesForUser();
+ const secret = await getSecretForDomain(domain);
+
+ if (!secret) {
+ // redirect with an error if credentials are missing
+ const url = new URL(redirectLocation);
+ url.searchParams.set("error", "missing_credentials");
+ return NextResponse.redirect(url);
+ }
+
+ // mint the JWT using the secret key
+ const fernToken = await mintFernToken({ secret, apiKey, roles });
+
+ if (!fernToken) {
+ const url = new URL(redirectLocation);
+ url.searchParams.set("error", "token_creation_failed");
+ return NextResponse.redirect(url);
+ }
+
+ // set the fern_token as a cookie on the docs domain
+ const cookieJar = await cookies();
+ cookieJar.set("fern_token", fernToken, {
+ httpOnly: true,
+ secure: true,
+ sameSite: "lax",
+ domain,
+ });
+
+ // redirect the user back to the docs
+ return NextResponse.redirect(redirectLocation);
+}
+
+const encoder = new TextEncoder();
+
+async function mintFernToken({
+ secret,
+ apiKey,
+ roles,
+}: {
+ secret: string;
+ apiKey?: string;
+ roles?: string[];
+}): Promise {
+ const fern: Record = {};
+
+ if (roles) {
+ fern.roles = roles;
+ }
+
+ if (apiKey) {
+ fern.playground = {
+ initial_state: {
+ auth: {
+ bearer_token: apiKey,
+ },
+ },
+ };
+ }
+
+ return await new SignJWT({ fern })
+ .setProtectedHeader({ alg: "HS256", typ: "JWT" })
+ .setIssuedAt()
+ .setExpirationTime("1d") // set to any value
+ .setIssuer("https://buildwithfern.com")
+ .sign(encoder.encode(secret)); // sign using the secret provided by Fern
+}
+```
+
+
+
+
+Once your `fern_token` is working, configure the features you need:
+
+- **[Role-based access control](/learn/docs/authentication/features/rbac)** — define roles in `docs.yml` and restrict navigation items or page content by role.
+- **[API key injection](/learn/docs/authentication/features/api-key-injection)** — configure the `playground` payload, including custom headers, multiple API keys, and per-environment credentials.
+
+
diff --git a/fern/products/docs/pages/authentication/oauth-setup.mdx b/fern/products/docs/pages/authentication/oauth-setup.mdx
new file mode 100644
index 000000000..541105642
--- /dev/null
+++ b/fern/products/docs/pages/authentication/oauth-setup.mdx
@@ -0,0 +1,119 @@
+---
+title: Set up OAuth
+subtitle: Fern-managed authentication integrated with your login system
+---
+
+With OAuth, Fern manages the auth flow for you. You give Fern access to your OAuth provider, and Fern handles the [`fern_token`](/learn/docs/authentication/overview#how-authentication-works) cookie that integrates your docs with your existing login system. Like [JWT](/learn/docs/authentication/setup/jwt), OAuth enables:
+
+- **[RBAC](/learn/docs/authentication/features/rbac)** — restrict content by user role
+- **[API key injection](/learn/docs/authentication/features/api-key-injection)** — pre-fill API keys in the [API Explorer](/learn/docs/api-references/api-explorer)
+
+## How it works
+
+1. A user clicks **Login** on your docs site and Fern initiates an OAuth flow with your provider.
+1. The user authenticates on your OAuth provider's login page.
+1. Your provider redirects back to Fern with an authorization code, which Fern exchanges for an access token.
+1. Fern sets a `fern_token` cookie containing the user's access and credentials.
+
+
+
+
+
+## Configuration
+
+
+
+Go to your OAuth provider's dashboard and create a new **web application** client.
+
+
+
+Allowlist the following callback in your OAuth provider:
+`https:///api/fern-docs/oauth2/callback`.
+
+Replace `` with your Fern Docs domain. If you use both a `.docs.buildwithfern.com` and a custom domain, allowlist both.
+
+
+
+Send the following to support@buildwithfern.com or your dedicated Slack channel:
+
+- [ ] Docs domain
+- [ ] Client ID
+- [ ] Client secret
+- [ ] Authorization URL (e.g. `https:///oauth2/authorize`)
+- [ ] Token URL (e.g. `https:///oauth2/token`)
+- [ ] Scopes (e.g. `openid`, `profile`, `email`)
+- [ ] Issuer URL (e.g. `https://`)
+
+
+If your client is connected to an API, you may need to specify an audience in the authentication request.
+
+The updated authorization URL may look like this: `https:///oauth2/authorize?audience=`
+
+
+
+
+Fern will configure OAuth on your site. You'll receive a notification when authentication is ready.
+
+
+
+Once OAuth is working, configure the features you need:
+
+
+
+Add a custom claim to your OAuth provider's token response so that Fern can determine each user's roles. The resulting token response should look something like this:
+
+```json {12-15} maxLines=7 startLine=10
+{
+ "iss": "https://your-tenant.us.auth0.com/",
+ "sub": "auth0|507f1f77bcf86cd799439011",
+ "aud": "your_client_id_here",
+ "iat": 1728388800,
+ "exp": 1728475200,
+ "email": "user@example.com",
+ "email_verified": true,
+ "name": "John Doe",
+ "nickname": "johndoe",
+ "picture": "https://s.gravatar.com/avatar/...",
+ "roles": [
+ "custom-role",
+ "user-specific-role"
+ ]
+}
+```
+
+
+Some OAuth providers have strict requirements for custom claims. If you need to use a claim other than `roles`, reach out to Fern and specify which claim should be parsed for the user's roles.
+
+
+
+To add a custom claim to Auth0, you need to create a **custom action**. This action will be used to add the custom claim to the token response.
+
+1. Go to the **Actions** tab in the Auth0 dashboard.
+2. Create a **Custom Action**.
+3. Select **Login/Post Login**.
+4. Add logic to set a roles.
+ ```js Example Action
+ exports.onExecutePostLogin = async (event, api) => {
+ const roles = event.user.app_metadata?.roles; // or however you store user roles
+
+ if (roles) {
+ const namespace: "https://.com"; // important: custom claims must be namespaced
+ api.accessToken.setCustomClaim(`${namespace}/roles`, roles);
+ }
+ };
+ ```
+5. Click **Create**.
+6. Add the action to your **Post Login Flow**.
+
+
+
+
+Set up an authenticated account for Fern so Fern can authorize users on your behalf, and configure your OAuth application to return user API keys when Fern requests tokens. Contact support@buildwithfern.com to coordinate this setup.
+
+
+
+
+Once your token response includes roles, define those roles in `docs.yml` and assign them to navigation items and page content. See [Role-based access control](/learn/docs/authentication/features/rbac) for the full setup.
+
+
+
diff --git a/fern/products/docs/pages/authentication/overview.mdx b/fern/products/docs/pages/authentication/overview.mdx
index fc91e521d..936378245 100644
--- a/fern/products/docs/pages/authentication/overview.mdx
+++ b/fern/products/docs/pages/authentication/overview.mdx
@@ -3,29 +3,34 @@ title: Overview of authentication options
description: Understand the different authentication options Fern offers
---
-Fern supports three ways to restrict access to your documentation:
-
-- **RBAC** — Control which content different users can see and supports [API key injection](/learn/docs/authentication/api-key-injection). Users authenticate via JWT or [OAuth](/learn/docs/authentication/set-up-oauth). Recommended for most use cases.
-- **SSO** — Require login without role management. Good for internal docs where all users see the same content.
-- **Password protection** — A single shared password for the entire site. Good for pre-release docs or basic access restriction.
-
-
-Learn how Fern handles user credentials and authentication in the [Security overview](/learn/docs/security/overview).
-
-
-Learn more about Fern's authentication options:
+Fern offers four ways to authenticate users on your documentation site.
-
- Granular access for different audiences
+
+ A single shared password for the entire site
-
- JWT or OAuth flows available
+
+ Corporate credentials for internal docs
-
- Basic functionality and simple setup
+
+ Self-managed auth integrated with your login system
-
- Shared password for simple access control
+
+ Fern-managed auth via your OAuth provider
+
+## Which option should I use?
+
+- **[Password protection](/learn/docs/authentication/setup/password-protection)** — You need quick gating with a single shared password. No per-user accounts, no setup beyond `docs.yml`.
+- **[SSO](/learn/docs/authentication/setup/sso)** — Your team should log in with corporate credentials (Okta, Google Workspace, etc.) for internal docs or wikis.
+- **[JWT](/learn/docs/authentication/setup/jwt)** — You want to integrate with your existing login system and control the entire auth flow yourself. Enables [role-based access control](/learn/docs/authentication/features/rbac) and [API key injection](/learn/docs/authentication/features/api-key-injection).
+- **[OAuth](/learn/docs/authentication/setup/oauth)** — You want to integrate with your existing login system but have Fern manage the auth flow via your OAuth provider. Enables [role-based access control](/learn/docs/authentication/features/rbac) and [API key injection](/learn/docs/authentication/features/api-key-injection).
+
+JWT and OAuth share the same capabilities — the difference is who manages the auth flow. Both can be used for login-only gating, or combined with [RBAC](/learn/docs/authentication/features/rbac) and [API key injection](/learn/docs/authentication/features/api-key-injection) for granular access control and pre-filled API keys.
+
+## How authentication works
+
+JWT, OAuth, and SSO are all powered by a [browser cookie](/learn/docs/security/overview) called `fern_token` that tells Fern who the user is and what they can access. The token can carry user roles for [RBAC](/learn/docs/authentication/features/rbac), API keys for the [API Explorer](/learn/docs/api-references/api-explorer), or simply verify that a user is logged in.
+
+[Password protection](/learn/docs/authentication/setup/password-protection) works differently — it uses a shared password rather than per-user tokens.
diff --git a/fern/products/docs/pages/authentication/rbac.mdx b/fern/products/docs/pages/authentication/rbac.mdx
index dff955fea..112ca73d0 100644
--- a/fern/products/docs/pages/authentication/rbac.mdx
+++ b/fern/products/docs/pages/authentication/rbac.mdx
@@ -6,35 +6,15 @@ description: Learn how to restrict access to your documentation using role-based
-## Introduction
+RBAC is a feature of [JWT](/learn/docs/authentication/setup/jwt) and [OAuth](/learn/docs/authentication/setup/oauth) authentication. Once a user logs in through either method, Fern checks the [`fern_token`](/learn/docs/authentication/overview#how-authentication-works) cookie to determine their roles and controls access to pages, sections, and other navigation items accordingly.
-Fern allows you to restrict parts of your navigation to individuals with specific roles. RBAC enables you to create different levels of access for different user types within your documentation.
+RBAC is useful for partner docs, beta features, tiered access, and internal content. You can combine it with [API key injection](/learn/docs/authentication/features/api-key-injection) in a single token. When RBAC is configured, [Ask Fern](/learn/docs/ai-features/ask-fern/overview) automatically respects these permissions.
-When RBAC is configured, [Ask Fern](/learn/docs/ai-features/ask-fern/overview) automatically respects these permissions so users only receive answers from content they're authorized to view.
+If you'd like restricted pages to be visible but locked to unauthenticated users (rather than completely hidden), let Fern know during setup.
-### Use cases
+## Setup
-Role-based access control is helpful for scenarios such as:
-
-- **Partner documentation**: Provide exclusive API documentation to integration partners while keeping internal docs private
-- **Beta features**: Share new features with beta users before general release
-- **Internal documentation**: Restrict sensitive documentation to employees only
-- **Tiered access**: Offer different documentation levels based on subscription tiers
-- **Customer-specific content**: Show different documentation based on customer type or plan
-
-### How it works
-
-Every user automatically has the `everyone` role, including unauthenticated visitors. Pages marked with the `everyone` role are publicly accessible without needing to log in.
-
-If a user visits content that requires authentication (i.e., content not marked as visible to `everyone`), Fern checks for an authentication cookie to determine the user's roles. If the user lacks the required role or isn't authenticated, Fern redirects them to the login URL you provided during setup.
-
-## Set up RBAC
-
-
-
-### Define all the `roles` in your docs.yml
-
-Start by using a `roles` key to define all the different roles:
+To enable RBAC, follow the [JWT](/learn/docs/authentication/setup/jwt) or [OAuth](/learn/docs/authentication/setup/oauth) setup guide, then define your roles in `docs.yml`:
```yml docs.yml
roles:
@@ -44,88 +24,41 @@ roles:
- admins
```
-### Configure authentication via a `fern_token`
-
-Fern uses a [browser cookie](/learn/docs/security/overview) called `fern_token` to identify authenticated users and their roles. If this cookie isn't present when a user tries to access restricted content, Fern redirects them to your login page.
-
-You can set up this authentication using either JWT or OAuth:
-
-
-
-**You are responsible for creating and setting the `fern_token` cookie in your authentication system.**
-
-Upon login, set a JWT for the user using a secret key that Fern provides. The JWT must include a `fern` claim with a `roles` array:
-
-```json
-{
- "fern": {
- "roles": ["partners"]
- }
-}
-```
+Every user automatically has the `everyone` role, including unauthenticated visitors. If a user lacks the required role or isn't authenticated, Fern redirects them to your login page.
-
+## Restricting content
-
-
+Once RBAC is configured, use `viewers` in your navigation and the `` component in your pages to control what each role can see.
-When a user needs authentication, Fern initiates an OAuth flow and redirects them to your authentication endpoint. You configure your OAuth endpoints to return user role information.
+### In navigation
-See [Set up OAuth for RBAC](/learn/docs/authentication/set-up-oauth) for a step-by-step guide.
-
-
-
-
-
-
-### Contact Fern for setup
-
-When you're ready to implement RBAC, contact support@buildwithfern.com.
-
-If you'd like restricted pages to be visible but locked to unauthenticated users (rather than completely hidden), notify Fern during this step.
-
-
-
-### Access control within navigation
-
-You can designate viewers on the following navigation items:
-- `products`
-- `versions`
-- `tabs`
-- `sections`
-- `pages`
-- `api references`
-- `changelogs`
+You can assign `viewers` to the following navigation items: `products`, `versions`, `tabs`, `sections`, `pages`, `api references`, and `changelogs`.
If you don't specify viewers, the content will be visible to any _authenticated_ user. To make content publicly accessible, explicitly set viewers to `everyone`.
```yml docs.yml {6-7, 13-15}
navigation:
- tab: Home
- layout:
+ layout:
- page: Welcome # this page is public
path: pages/welcome.mdx
- viewers:
- - everyone
+ viewers:
+ - everyone
- tab: Documentation
layout:
- page: Overview # this page is visible to all logged-in users
- path: pages/overview.mdx
+ path: pages/overview.mdx
- section: Beta Release # this section is visible to beta-users and admins
viewers:
- beta-users
- admins
- contents:
+ contents:
...
```
Viewership is inherited. For example, if a section can only be viewed by `admins`, then all its pages and nested sections can also only be viewed by admins.
-### Access control within MDX pages
-
-You can restrict specific content within your MDX pages to users with certain roles. This allows you to show different content to different user types on the same page.
-
-#### Basic usage
+### In MDX pages
Use the `` component to conditionally render content based on user roles:
@@ -137,8 +70,6 @@ Use the `` component to conditionally render content based on user roles:
```
-#### Multiple roles
-
You can specify multiple roles. Content will be visible to users who have **any** of the specified roles:
```mdx
diff --git a/fern/products/docs/pages/authentication/set-up-oauth.mdx b/fern/products/docs/pages/authentication/set-up-oauth.mdx
deleted file mode 100644
index c6bc701a0..000000000
--- a/fern/products/docs/pages/authentication/set-up-oauth.mdx
+++ /dev/null
@@ -1,96 +0,0 @@
----
-title: Set up OAuth for RBAC
-subtitle: Integrate your OAuth provider with Fern Docs to authenticate users and assign roles
----
-
-This guide walks you through connecting your OAuth provider to Fern so that users are authenticated and assigned roles for [role-based access control (RBAC)](/learn/docs/authentication/rbac). If you haven't already, start by [defining your roles](/learn/docs/authentication/rbac#define-all-the-roles-in-your-docsyml) in `docs.yml`.
-
-
-
-Go to your OAuth provider's dashboard. Create a new **web application** client. This is the client that will be used by Fern to authenticate users with your OAuth provider.
-
-
-
-You will need to allowlist the following callback in your OAuth provider:
-`https:///api/fern-docs/oauth2/callback`.
-
-Replace `` with whatever domain you are using for your Fern Docs site. If you want to authenticate both your `.docs.buildwithfern.com` and custom domain, you will need to allowlist both.
-
-
-
-Fern will need the following details to configure OAuth authentication:
-- [ ] Docs domain
-- [ ] Client ID
-- [ ] Client secret
-- [ ] Authorization URL (e.g. `https:///oauth2/authorize`)
-- [ ] Token URL (e.g. `https:///oauth2/token`)
-- [ ] Scopes (e.g. `openid`, `profile`, `email`)
-- [ ] Issuer URL (e.g. `https://`)
-
-
-If your client is connected to an API, you may need to specify an audience in the authentication request.
-
-The updated authorization URL may look like this: `https:///oauth2/authorize?audience=`
-
-
-Send these details to support@buildwithfern.com or in your dedicated Slack channel.
-
-
-
-Wait for Fern to configure OAuth. You will receive a notification when the site is ready to use authentication.
-
-
-
-Add a custom claim to your OAuth provider's token response. This claim will be used to set the user's roles in Fern Docs. The resulting token response should look something like this:
-
-```json {12-15}
-{
- "iss": "https://your-tenant.us.auth0.com/",
- "sub": "auth0|507f1f77bcf86cd799439011",
- "aud": "your_client_id_here",
- "iat": 1728388800,
- "exp": 1728475200,
- "email": "user@example.com",
- "email_verified": true,
- "name": "John Doe",
- "nickname": "johndoe",
- "picture": "https://s.gravatar.com/avatar/...",
- "roles": [
- "custom-role",
- "user-specific-role"
- ]
-}
-```
-
-
-Some OAuth providers have strict requirements for custom claims. If you need to use a claim other than `roles`, reach out to Fern and specify which claim should be parsed for the user's roles.
-
-
-
-
-To add a custom claim to Auth0, you need to create a **custom action**. This action will be used to add the custom claim to the token response.
-
-1. Go to the **Actions** tab in the Auth0 dashboard.
-2. Create a **Custom Action**.
-3. Select **Login/Post Login**.
-4. Add logic to set a roles.
- ```js Example Action
- exports.onExecutePostLogin = async (event, api) => {
- const roles = event.user.app_metadata?.roles; // or however you store user roles
-
- if (roles) {
- const namespace: "https://.com"; // important: custom claims must be namespaced
- api.accessToken.setCustomClaim(`${namespace}/roles`, roles);
- }
- };
- ```
-5. Click **Create**.
-6. Add the action to your **Post Login Flow**.
-
-
-If you are using a different OAuth provider, reach out to Fern with any questions on setting up a custom claim.
-
-
-
-
-
\ No newline at end of file
diff --git a/fern/products/docs/pages/authentication/sso.mdx b/fern/products/docs/pages/authentication/sso.mdx
index a0c5884a7..23af85bce 100644
--- a/fern/products/docs/pages/authentication/sso.mdx
+++ b/fern/products/docs/pages/authentication/sso.mdx
@@ -1,26 +1,19 @@
---
title: Single Sign-On
-subtitle: Fern SSO enables enterprise teams to access documentation securely using corporate credentials. Supports SAML 2.0 and OIDC protocols.
+subtitle: Secure access to your documentation using corporate credentials
---
-Fern’s Single Sign-On (SSO) is an enterprise feature that lets your team securely access your docs through your organization’s identity provider.
+SSO lets your team access your docs through your organization's identity provider using SAML 2.0 or OIDC. Like [RBAC](/learn/docs/authentication/features/rbac) and [API key injection](/learn/docs/authentication/features/api-key-injection), SSO uses the [`fern_token`](/learn/docs/authentication/overview#how-authentication-works) cookie to identify authenticated users. SSO unlocks the [Fern Editor](/learn/docs/writing-content/fern-editor) for browser-based editing and [authenticated preview links](/learn/docs/preview-publish/preview-changes#preview-links).
-SSO does not support Role-Based Access Control or API Key Injection.
-
-## What SSO unlocks
-
-When SSO is enabled for your organization, authenticated users of Fern can:
-
-- **Use the Fern Editor**: Make edits to your docs from the browser
-- **Send Authenticated Preview Links** Only authenticated users can view [preview links](/learn/docs/preview-publish/preview-changes#preview-links)
+SSO provides login-based access control but does not support role management or API key injection. For granular access control, use [RBAC](/learn/docs/authentication/features/rbac) instead.
## How it works
-Fern's SSO integration allows your team to use their existing corporate credentials to access your Fern Docs site.
+When a user clicks **Login**, Fern redirects them to your identity provider. After authenticating with their corporate credentials, the identity provider redirects back to Fern with a `fern_token`, granting access to your docs.
-### Architecture diagram
+
```mermaid
sequenceDiagram
@@ -29,25 +22,18 @@ sequenceDiagram
participant F as Fern Docs
participant I as Identity Provider
- U->>F: Click "Login"
+ U->>F: Click "Login"
F->>I: Redirect to SSO login
-
+
Note over I: User authenticates with corporate credentials
-
+
I->>I: Validate user credentials
I->>F: Redirect back with fern_token
F->>F: Grant access to organizational features
F->>U: Show docs site
```
+
-## Supported identity providers
-
-Fern supports SSO integration with any identity provider that implements industry-standard protocols:
-
-- **SAML 2.0**: Compatible with providers like Okta, OneLogin, Azure AD, and others
-- **OIDC (OpenID Connect)**: Works with Google Workspace, Auth0, and other OIDC-compliant providers
-
-## Setting up SSO
-
-Reach out via Slack or [contact our team](https://buildwithfern.com/contact) to begin the SSO setup process. Fern will work with one of your security administrators to connect to your identity provider.
+## Setup
+Fern supports any SAML 2.0 or OIDC provider (Okta, Google Workspace, Auth0, Azure AD, OneLogin, etc.). [Contact Fern](https://buildwithfern.com/contact) or reach out via Slack. Fern will work with your security team to connect to your identity provider.
diff --git a/fern/products/docs/pages/changelog/2025-11-11.mdx b/fern/products/docs/pages/changelog/2025-11-11.mdx
index 424a3c923..f077e6ec8 100644
--- a/fern/products/docs/pages/changelog/2025-11-11.mdx
+++ b/fern/products/docs/pages/changelog/2025-11-11.mdx
@@ -28,4 +28,4 @@ Then, the API Explorer automatically displays all available authentication optio
-Learn more about configuring authentication in the [API Explorer documentation](/learn/docs/api-references/api-explorer/overview).
+Learn more about configuring authentication in the [API Explorer documentation](/learn/docs/api-references/api-explorer).
diff --git a/fern/products/docs/pages/changelog/2026-01-17.mdx b/fern/products/docs/pages/changelog/2026-01-17.mdx
index d27ecc662..e369cbb0b 100644
--- a/fern/products/docs/pages/changelog/2026-01-17.mdx
+++ b/fern/products/docs/pages/changelog/2026-01-17.mdx
@@ -22,4 +22,4 @@ The `` component now supports `products` and `versions` props, allowing you
You can also use the `not` prop to invert the condition, showing content when the current product or version does not match.
-Learn more about the [If component](/learn/docs/authentication/rbac#access-control-within-mdx-pages).
+Learn more about the [If component](/learn/docs/authentication/features/rbac#in-mdx-pages).
diff --git a/fern/products/docs/pages/changelog/2026-02-11.mdx b/fern/products/docs/pages/changelog/2026-02-11.mdx
index 3f5585678..02e188d38 100644
--- a/fern/products/docs/pages/changelog/2026-02-11.mdx
+++ b/fern/products/docs/pages/changelog/2026-02-11.mdx
@@ -1,9 +1,9 @@
## API key injection for self-hosted docs
-Self-hosted deployments support [API key injection](/learn/docs/authentication/api-key-injection) via environment variables. Enable `FERN_API_KEY_INJECTION_ENABLED` to show a **Login** button in the API Explorer without requiring login for the entire site. Use `FERN_AUTH_ALLOWLIST` and `FERN_AUTH_DENYLIST` to control page-level access.
+Self-hosted deployments support [API key injection](/learn/docs/authentication/features/api-key-injection) via environment variables. Enable `FERN_API_KEY_INJECTION_ENABLED` to show a **Login** button in the API Explorer without requiring login for the entire site. Use `FERN_AUTH_ALLOWLIST` and `FERN_AUTH_DENYLIST` to control page-level access.
Learn more about [self-hosted authentication](/learn/docs/self-hosted/authentication#api-key-injection).
## Multiple API keys and per-environment keys
-API key injection supports [multiple API keys](/learn/docs/authentication/api-key-injection#multiple-api-keys) and [per-environment credentials](/learn/docs/authentication/api-key-injection#per-environment-keys). Provide multiple keys as a JSON-encoded array in `bearer_token`, and use `env_state` to set different credentials per environment with substring matching.
+API key injection supports [multiple API keys](/learn/docs/authentication/setup/jwt#build-the-fern-claim) and [per-environment credentials](/learn/docs/authentication/setup/jwt#build-the-fern-claim). Provide multiple keys as a JSON-encoded array in `bearer_token`, and use `env_state` to set different credentials per environment with substring matching.
diff --git a/fern/products/docs/pages/enterprise/self-hosted-authentication.mdx b/fern/products/docs/pages/enterprise/self-hosted-authentication.mdx
index 4886d8a80..7f66a719b 100644
--- a/fern/products/docs/pages/enterprise/self-hosted-authentication.mdx
+++ b/fern/products/docs/pages/enterprise/self-hosted-authentication.mdx
@@ -203,7 +203,7 @@ ENV FERN_AUTH_DENYLIST="/api-reference/(.*)"
## API key injection
-You can enable [API key injection](/learn/docs/authentication/api-key-injection) in the API Explorer for self-hosted deployments. This shows a **Login** button in the API Explorer so users can authenticate and have their API keys auto-populated, without requiring login for the entire documentation site.
+You can enable [API key injection](/learn/docs/authentication/features/api-key-injection) in the API Explorer for self-hosted deployments. This shows a **Login** button in the API Explorer so users can authenticate and have their API keys auto-populated, without requiring login for the entire documentation site.
Add `FERN_API_KEY_INJECTION_ENABLED` to your Dockerfile alongside the basic token verification variables:
@@ -222,4 +222,4 @@ ENV FERN_AUTH_ALLOWLIST="/(.*)"
RUN fern-generate
```
-With `FERN_AUTH_ALLOWLIST="/(.*)"`, all doc pages are publicly accessible (no login wall), but the API Explorer still shows the **Login** button. When a user logs in, their JWT's `fern` payload is read and the API key is injected into the API Explorer. See [Autopopulate API keys](/learn/docs/authentication/api-key-injection) for the full `fern` payload reference.
+With `FERN_AUTH_ALLOWLIST="/(.*)"`, all doc pages are publicly accessible (no login wall), but the API Explorer still shows the **Login** button. When a user logs in, their JWT's `fern` payload is read and the API key is injected into the API Explorer. See [Autopopulate API keys](/learn/docs/authentication/features/api-key-injection) for the full `fern` payload reference.
diff --git a/fern/products/docs/pages/enterprise/self-hosted.mdx b/fern/products/docs/pages/enterprise/self-hosted.mdx
index aa849bab8..f67121ece 100644
--- a/fern/products/docs/pages/enterprise/self-hosted.mdx
+++ b/fern/products/docs/pages/enterprise/self-hosted.mdx
@@ -21,7 +21,7 @@ Unless you have specific requirements that prevent using Fern's default hosting,
Self-hosted deployments include core Fern documentation website features like auto-generated API references, interactive documentation, SDK code snippets, search, and customizable branding.
-However, features requiring external connections aren't supported, including [Ask Fern](/learn/docs/ai-features/ask-fern/overview) (AI chat), [analytics](/learn/docs/integrations/overview), and [SSO integrations](/learn/docs/authentication/sso).
+However, features requiring external connections aren't supported, including [Ask Fern](/learn/docs/ai-features/ask-fern/overview) (AI chat), [analytics](/learn/docs/integrations/overview), and [SSO integrations](/learn/docs/authentication/setup/sso).
**PDF export** and **offline AI chat functionality** are not yet available on any plan but are in development for enterprise self-hosted deployments. [Email us](mailto:support@buildwithfern.com) if you're interested in these features.
diff --git a/fern/products/docs/pages/getting-started/how-it-works.mdx b/fern/products/docs/pages/getting-started/how-it-works.mdx
index 5f5bee683..bbdb2a6b6 100644
--- a/fern/products/docs/pages/getting-started/how-it-works.mdx
+++ b/fern/products/docs/pages/getting-started/how-it-works.mdx
@@ -105,7 +105,7 @@ The merged PR triggers a Fern GitHub Action. The action retrieves your API speci
### Deploy to hosting platform
-The processed content is deployed to Vercel as a documentation site with an embedded [API explorer](/learn/docs/api-references/api-explorer/overview) that allows users to test endpoints directly within the documentation.
+The processed content is deployed to Vercel as a documentation site with an embedded [API Explorer](/learn/docs/api-references/api-explorer) that allows users to test endpoints directly within the documentation.
Vercel Edge middleware handles the underlying routing, authentication, and performance optimization.
diff --git a/fern/products/docs/pages/navigation/tabs.mdx b/fern/products/docs/pages/navigation/tabs.mdx
index 3a4e4efcc..ebd41e5ec 100644
--- a/fern/products/docs/pages/navigation/tabs.mdx
+++ b/fern/products/docs/pages/navigation/tabs.mdx
@@ -133,7 +133,7 @@ theme:
## Tab variants
-Tab variants let you display different content variations within a single tab, and [support RBAC](/learn/docs/authentication/rbac). This is useful for showing different user types, implementation approaches, or experience levels without creating separate tabs.
+Tab variants let you display different content variations within a single tab, and [support RBAC](/learn/docs/authentication/features/rbac). This is useful for showing different user types, implementation approaches, or experience levels without creating separate tabs.
Use **variants** for different perspectives on the same content area (REST vs. GraphQL, beginner vs. advanced). Use **tabs** for completely different documentation sections (guides vs. API Reference).
diff --git a/fern/products/docs/pages/resources/stainless-comparison.mdx b/fern/products/docs/pages/resources/stainless-comparison.mdx
index 9baa049fe..745d24857 100644
--- a/fern/products/docs/pages/resources/stainless-comparison.mdx
+++ b/fern/products/docs/pages/resources/stainless-comparison.mdx
@@ -14,8 +14,8 @@ Choose Fern for enterprise requirements, regulated industries, or production-cri
|------------|------|-----------|
| **Maturity** | Production-ready, stable APIs | Early access, breaking changes expected |
| **Authoring** | [Visual editor](/learn/docs/writing-content/fern-editor) + Git | Git only |
-| **Enterprise features** | [RBAC](/learn/docs/authentication/rbac), [SSO](/learn/docs/authentication/sso), [versioning](/learn/docs/configuration/site-level-settings) | None |
-| **API testing** | [Interactive explorer](/learn/docs/api-references/api-explorer/overview) | None |
+| **Enterprise features** | [RBAC](/learn/docs/authentication/features/rbac), [SSO](/learn/docs/authentication/setup/sso), [versioning](/learn/docs/configuration/site-level-settings) | None |
+| **API testing** | [Interactive explorer](/learn/docs/api-references/api-explorer) | None |
| **Deployment** | Cloud + [full-stack self-hosted](/learn/docs/self-hosted/overview) | Static files only |
## Detailed feature comparison
@@ -46,11 +46,11 @@ Stainless uses Git-based workflows for all content authoring. Fern supports both
-Stainless doesn't provide an interactive testing environment. Fern's [API Explorer](/learn/docs/api-references/api-explorer/overview) enables in-browser endpoint testing with automatic credential injection.
+Stainless doesn't provide an interactive testing environment. Fern's [API Explorer](/learn/docs/api-references/api-explorer) enables in-browser endpoint testing with automatic credential injection.
| Feature | Fern Docs | Stainless Docs |
|---------|-----------|----------------|
-| **API Explorer** | ✅ [Full playground](/learn/docs/api-references/api-explorer/overview) with [auto-populated credentials](/learn/docs/api-references/autopopulate-api-key), WebSocket, and audio streaming support | ❌ None |
+| **API Explorer** | ✅ [Full playground](/learn/docs/api-references/api-explorer) with [auto-populated credentials](/learn/docs/authentication/features/api-key-injection), WebSocket, and audio streaming support | ❌ None |
| **SDK documentation** | ✅ [Multi-language support](/learn/docs/api-references/sdk-snippets) | ✅ Language-specific pages |
| **API spec formats** | ✅ [OpenAPI, AsyncAPI, gRPC, OpenRPC, Fern](/learn/docs/api-references/generate-api-ref) | ⚠️ OpenAPI + Stainless config only |
@@ -58,11 +58,11 @@ Stainless doesn't provide an interactive testing environment. Fern's [API Explor
-Stainless doesn't offer audience-specific content filtering, multi-version APIs, or unified multi-product sites. Fern provides [RBAC](/learn/docs/authentication/rbac) and [versioning](/learn/docs/configuration/site-level-settings) capabilities for complex API portfolios.
+Stainless doesn't offer audience-specific content filtering, multi-version APIs, or unified multi-product sites. Fern provides [RBAC](/learn/docs/authentication/features/rbac) and [versioning](/learn/docs/configuration/site-level-settings) capabilities for complex API portfolios.
| Feature | Fern Docs | Stainless Docs |
|---------|-----------|----------------|
-| **RBAC** | ✅ [Audience-based content filtering](/learn/docs/authentication/rbac) for internal vs. public sites | ❌ None |
+| **RBAC** | ✅ [Audience-based content filtering](/learn/docs/authentication/features/rbac) for internal vs. public sites | ❌ None |
| **API versioning** | ✅ [Multi-version support](/learn/docs/configuration/site-level-settings) with availability states and per-version content | ❌ None |
| **Multi-product sites** | ✅ [Multiple products](/learn/docs/configuration/site-level-settings) in unified documentation | ⚠️ Basic tabs (Guides/Reference) |
@@ -83,12 +83,12 @@ Stainless offers RAG-based search. Fern provides additional AI features includin
-Stainless's [authentication](https://www.stainless.com/docs/docs-platform/advanced/stainless-authentication/) focuses on API keys for their service. Fern's [WorkOS integration](/learn/docs/authentication/sso) provides enterprise SSO (SAML/OIDC) for end-user authentication with role-based content visibility.
+Stainless's [authentication](https://www.stainless.com/docs/docs-platform/advanced/stainless-authentication/) focuses on API keys for their service. Fern's [WorkOS integration](/learn/docs/authentication/setup/sso) provides enterprise SSO (SAML/OIDC) for end-user authentication with role-based content visibility.
| Feature | Fern Docs | Stainless Docs |
|---------|-----------|----------------|
-| **Enterprise SSO** | ✅ [WorkOS](/learn/docs/authentication/sso) (SAML/OIDC/SCIM) with [RBAC](/learn/docs/authentication/rbac) | ❌ None |
-| **Authenticated pages** | ✅ [Login-protected documentation](/learn/docs/authentication/overview) with [auto-populated credentials](/learn/docs/api-references/autopopulate-api-key) | ⚠️ API keys for Stainless service only |
+| **Enterprise SSO** | ✅ [WorkOS](/learn/docs/authentication/setup/sso) (SAML/OIDC/SCIM) with [RBAC](/learn/docs/authentication/features/rbac) | ❌ None |
+| **Authenticated pages** | ✅ [Login-protected documentation](/learn/docs/authentication/overview) with [auto-populated credentials](/learn/docs/authentication/features/api-key-injection) | ⚠️ API keys for Stainless service only |
diff --git a/fern/products/docs/pages/security/overview.mdx b/fern/products/docs/pages/security/overview.mdx
index 4d9caf93f..7423e36cf 100644
--- a/fern/products/docs/pages/security/overview.mdx
+++ b/fern/products/docs/pages/security/overview.mdx
@@ -13,9 +13,9 @@ Fern's documentation platform is built with security as a core principle, using
Fern supports [multiple authentication methods](/learn/docs/authentication/overview) to secure your documentation. All methods use a client-side `fern_token` cookie stored entirely in the browser:
-- [Role-Based Access Control (RBAC)](/learn/docs/authentication/rbac) controls which users can access specific documentation content based on their roles (stores user roles)
-- [API key injection](/learn/docs/authentication/api-key-injection) automatically populates code examples with user-specific API keys for a personalized experience (stores authentication tokens via JWT or OAuth)
-- [Single Sign-On (SSO)](/learn/docs/authentication/sso) integrates with your existing identity provider for seamless authentication (stores identity provider tokens)
+- [Role-Based Access Control (RBAC)](/learn/docs/authentication/features/rbac) controls which users can access specific documentation content based on their roles (stores user roles)
+- [API key injection](/learn/docs/authentication/features/api-key-injection) automatically populates code examples with user-specific API keys for a personalized experience (stores authentication tokens via JWT or OAuth)
+- [Single Sign-On (SSO)](/learn/docs/authentication/setup/sso) integrates with your existing identity provider for seamless authentication (stores identity provider tokens)
These cookies are managed entirely client-side and automatically cleared when the user logs out or the session expires. This approach ensures that sensitive credentials remain under your control and are never exposed to Fern's infrastructure.
diff --git a/fern/products/sdks/reference/generators-yml-reference.mdx b/fern/products/sdks/reference/generators-yml-reference.mdx
index a87e409b7..273385536 100644
--- a/fern/products/sdks/reference/generators-yml-reference.mdx
+++ b/fern/products/sdks/reference/generators-yml-reference.mdx
@@ -60,7 +60,7 @@ groups:
## `auth-schemes`
-Define authentication methods for your SDKs and the [API Explorer](/learn/docs/api-references/api-explorer/overview) that your endpoints can reference. Authentication schemes defined in `generators.yml` take precedence over authentication schemes [defined in your spec](/learn/api-definitions/openapi/authentication).
+Define authentication methods for your SDKs and the [API Explorer](/learn/docs/api-references/api-explorer) that your endpoints can reference. Authentication schemes defined in `generators.yml` take precedence over authentication schemes [defined in your spec](/learn/api-definitions/openapi/authentication).
After defining an authentication scheme, you must use [`api.auth`](#auth) to apply it as the default across all endpoints. Alternatively, you can [define authentication for individual SDKs](#override-api-authentication-settings) if you need different auth behavior per language.