mirror of
https://github.com/mintlify/docs.git
synced 2026-09-14 13:35:46 +08:00
886aa0b3a2
Co-authored-by: locadex-agent[bot] <217277504+locadex-agent[bot]@users.noreply.github.com>
470 lines
18 KiB
Plaintext
470 lines
18 KiB
Plaintext
---
|
||
title: "认证设置"
|
||
description: "通过用户认证来控制对文档的访问。"
|
||
keywords: ['authentication', 'auth', 'OAuth', 'JWT', 'password']
|
||
---
|
||
|
||
<Info>
|
||
[Pro 方案](https://mintlify.com/pricing?ref=authentication) 包含密码认证。
|
||
|
||
[Enterprise 方案](https://mintlify.com/pricing?ref=authentication) 包含所有认证方式。
|
||
</Info>
|
||
|
||
启用认证后,用户需先登录才能访问你的文档。
|
||
|
||
启用认证后,用户必须先登录才能访问任何内容。你可以将特定页面或分组配置为公开,而将其他页面设为受保护状态。
|
||
|
||
<div id="configure-authentication">
|
||
## 配置认证
|
||
</div>
|
||
|
||
选择要配置的握手方式。
|
||
|
||
<Tabs>
|
||
<Tab title="密码">
|
||
<Info>
|
||
密码认证仅提供访问控制,**不**支持用户级功能,例如基于用户组的访问控制或 API 操作台中的预填数据。
|
||
</Info>
|
||
|
||
### 前提条件
|
||
|
||
* 你的安全策略允许在多个用户之间共享密码。
|
||
|
||
### 设置
|
||
|
||
<Steps>
|
||
<Step title="创建密码。">
|
||
1. 在控制台中,前往 [Authentication](https://dashboard.mintlify.com/settings/deployment/authentication)。
|
||
2. 启用认证。
|
||
3. 在 **Password Protection** 部分输入一个安全的密码。
|
||
|
||
输入密码后,你的网站会重新部署。部署完成后,任何访问你站点的用户都必须输入该密码才能访问你的内容。
|
||
</Step>
|
||
|
||
<Step title="分发访问权限。">
|
||
以安全方式将密码和文档 URL 分享给获授权的用户。
|
||
</Step>
|
||
</Steps>
|
||
|
||
### 示例
|
||
|
||
你将文档托管在 `docs.foo.com`,只需要基础访问控制,而不需要跟踪单个用户。你希望阻止公众访问,同时保持设置简单。
|
||
|
||
在控制台中**创建一个强密码**,并将**凭证分享**给获授权的用户。就这么简单!
|
||
</Tab>
|
||
|
||
<Tab title="Mintlify 控制台">
|
||
### 前提条件
|
||
|
||
* 所有需要访问你文档的人都必须是你 Mintlify 组织的成员。
|
||
|
||
### 设置
|
||
|
||
<Steps>
|
||
<Step title="在 Mintlify 控制台中启用认证。">
|
||
1. 在控制台中,前往 [Authentication](https://dashboard.mintlify.com/settings/deployment/authentication)。
|
||
2. 启用认证。
|
||
3. 在 **Custom Authentication** 部分,点击 **Mintlify Auth**。
|
||
4. 点击 **Enable Mintlify Auth**。
|
||
|
||
启用 Mintlify 认证后,你的网站会重新部署。部署完成后,任何访问你网站的人都必须登录到你的 Mintlify 组织才能访问你的内容。
|
||
</Step>
|
||
|
||
<Step title="添加授权用户。">
|
||
1. 在控制台中,前往 [Members](https://dashboard.mintlify.com/settings/organization/members)。
|
||
2. 添加所有需要访问你文档的人员。
|
||
3. 根据他们的编辑权限分配合适的角色。
|
||
</Step>
|
||
</Steps>
|
||
|
||
### 示例
|
||
|
||
你将文档托管在 `docs.foo.com`,并且整个团队都能访问你的控制台。你希望仅将访问权限限制在团队成员。
|
||
|
||
在控制台设置中**启用 Mintlify 认证**。
|
||
|
||
通过检查所有团队成员在你的组织中是否为激活状态来**验证团队访问权限**。
|
||
</Tab>
|
||
|
||
<Tab title="OAuth 2.0">
|
||
### 前提条件
|
||
|
||
* 支持 Authorization Code Flow(授权码流程)的 OAuth 或 OIDC 服务器。
|
||
* 能够创建可通过 OAuth 访问令牌访问的 API 端点(可选,用于启用基于用户组的访问控制)。
|
||
|
||
### 设置
|
||
|
||
<Steps>
|
||
<Step title="配置你的 OAuth 设置。">
|
||
1. 在控制台中前往 [Authentication](https://dashboard.mintlify.com/settings/deployment/authentication)。
|
||
2. 启用认证。
|
||
3. 在 **Custom Authentication** 部分,点击 **OAuth**。
|
||
4. 配置以下字段:
|
||
|
||
* **Authorization URL**:你的 OAuth 端点。
|
||
* **Client ID**:你的 OAuth 2.0 客户端标识符。
|
||
* **Client Secret**:你的 OAuth 2.0 客户端密钥。
|
||
* **Scopes**(可选):要请求的权限。复制 **完整的** scope 字符串(例如,对于 `provider.users.docs` 这样的 scope,复制完整的 `provider.users.docs`)。如果需要不同的访问级别,可以使用多个 scope。
|
||
* **Additional authorization parameters**(可选):要添加到初始授权请求中的其他 query 参数。
|
||
* **Token URL**:你的 OAuth 令牌交换端点。
|
||
* **Info API URL**(可选):你服务器上的一个端点,Mintlify 会调用它来获取用户信息。对于基于用户组的访问控制是必填项。如果省略,OAuth 流程只会验证身份。
|
||
* **Logout URL**(可选):你的 OAuth 提供方自带的登出 URL。用户登出时,Mintlify 会将登出重定向与该配置的 URL 进行校验,以确保安全性。只有当重定向地址与配置的 `logoutUrl` 完全匹配时,重定向才会成功。如果你未配置登出 URL,用户会被重定向到 `/login`。Mintlify 会使用 `GET` 请求重定向用户,并且不会追加任何 query 参数,因此请将所有参数(例如 `returnTo`)直接包含在 URL 中。
|
||
* **Redirect URL**(可选):在认证完成后重定向用户的 URL。
|
||
|
||
5. 点击 **保存更改**。
|
||
|
||
配置完 OAuth 设置后,你的网站会重新部署。部署完成后,任何访问你站点的用户都必须登录到你的 OAuth 提供方才能访问内容。
|
||
</Step>
|
||
|
||
<Step title="配置你的 OAuth 服务器。">
|
||
1. 从你的[认证设置](https://dashboard.mintlify.com/settings/deployment/authentication)中复制 **Redirect URL**。
|
||
2. 将该 Redirect URL 添加为 OAuth 服务器中授权的重定向 URL。
|
||
</Step>
|
||
|
||
<Step title="创建用户信息端点(可选)。">
|
||
为启用基于用户组的访问控制,创建一个 API 端点,该端点需满足:
|
||
|
||
* 响应 `GET` 请求。
|
||
* 接受 `Authorization: Bearer <access_token>` 头部用于认证。
|
||
* 以 `User` 格式返回用户数据。更多信息参见 [User data format](#user-data-format)。
|
||
|
||
Mintlify 使用 OAuth 访问令牌调用此端点以获取用户信息。不会发送额外的 query 参数。
|
||
|
||
将此端点 URL 填入你[认证设置](https://dashboard.mintlify.com/settings/deployment/authentication)中的 **Info API URL** 字段。
|
||
</Step>
|
||
</Steps>
|
||
|
||
### 示例
|
||
|
||
你将文档托管在 `foo.com/docs`,并且你有一个现有的 OAuth 服务器 `auth.foo.com`,它支持 Authorization Code Flow。
|
||
|
||
在控制台中**配置你的 OAuth 服务器详细信息**:
|
||
|
||
* **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%2Ffoo.com%2Fdocs`
|
||
|
||
在 `api.foo.com/docs/user-info` 上**创建一个用户信息端点**,该端点要求使用带有 `provider.users.docs` scope 的 OAuth 访问令牌,并返回:
|
||
|
||
```json
|
||
{
|
||
"groups": ["engineering", "admin"],
|
||
"expiresAt": 1735689600,
|
||
"apiPlaygroundInputs": {
|
||
"header": {
|
||
"Authorization": "Bearer user_abc123"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
<Note>
|
||
使用用户信息响应中的 `expiresAt` 字段控制会话时长。该字段为 Unix 时间戳(自纪元以来的秒数),用于指示会话何时过期。更多详情请参阅 [用户数据格式](#user-data-format)。
|
||
</Note>
|
||
|
||
**将你的 OAuth 服务器配置为允许重定向**到回调 URL。
|
||
</Tab>
|
||
|
||
<Tab title="JWT(JSON Web Token)">
|
||
### 前提条件
|
||
|
||
* 一个可以生成并签名 JWT 的认证系统。
|
||
* 一个可以创建重定向 URL 的后端服务。
|
||
|
||
### 设置
|
||
|
||
<Steps>
|
||
<Step title="生成私钥。">
|
||
1. 在控制台中前往 [Authentication](https://dashboard.mintlify.com/settings/deployment/authentication)。
|
||
2. 启用认证。
|
||
3. 在 **Custom Authentication** 部分,点击 **JWT**。
|
||
4. 输入你现有登录流程的 URL。
|
||
5. 点击 **Save changes(保存更改)**。
|
||
6. 点击 **Generate new key(生成新密钥)**。
|
||
7. 将你的 key 安全存储在后端可以访问的位置。
|
||
|
||
生成私钥后,你的网站会重新部署。部署完成后,任何访问你网站的人都必须登录到你的 JWT 认证系统才能访问你的内容。
|
||
</Step>
|
||
|
||
<Step title="将 Mintlify 认证集成到你的登录流程中。">
|
||
修改你现有的登录流程,在用户通过认证后增加以下步骤:
|
||
|
||
* 按 `User` 格式创建一个包含已认证用户信息的 JWT。更多信息参见 [User data format](#user-data-format)。
|
||
* 使用 EdDSA 算法,用你的密钥对 JWT 进行签名。
|
||
* 创建一个返回到文档 `/login/jwt-callback` 路径的重定向 URL,并将 JWT 放在 URL 片段(hash)中。
|
||
</Step>
|
||
</Steps>
|
||
|
||
### 示例
|
||
|
||
你在 `docs.foo.com` 上托管文档,并在 `foo.com` 上已有认证系统。你希望扩展登录流程,在保持文档与控制台分离的同时,为文档授予访问权限(或者如果你没有控制台,则直接为文档授予访问权限)。
|
||
|
||
在 `https://foo.com/docs-login` 创建一个登录端点,用于扩展你现有的认证逻辑。
|
||
|
||
在验证用户凭据之后:
|
||
|
||
* 按 Mintlify 的格式生成包含用户数据的 JWT。
|
||
* 对 JWT 进行签名并重定向到 `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 signingKey = await jose.importPKCS8(process.env.MINTLIFY_PRIVATE_KEY, 'EdDSA');
|
||
|
||
export async function handleRequest(req: Request, res: Response) {
|
||
const user = {
|
||
expiresAt: Math.floor((Date.now() + TWO_WEEKS_IN_MS) / 1000), // 2 周会话过期时间
|
||
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') // JWT 10 秒后过期
|
||
.sign(signingKey);
|
||
|
||
return res.redirect(`https://docs.foo.com/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, '')
|
||
|
||
@router.get('/auth')
|
||
async def return_mintlify_auth_status(current_user):
|
||
jwt_token = jwt.encode(
|
||
payload={
|
||
'exp': int((datetime.now() + timedelta(seconds=10)).timestamp()), # JWT 10 秒后过期
|
||
'expiresAt': int((datetime.now() + timedelta(weeks=2)).timestamp()), # 2 周会话过期时间
|
||
'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.foo.com/login/jwt-callback#{jwt_token}', status_code=302)
|
||
```
|
||
</CodeGroup>
|
||
|
||
### 重定向未认证用户
|
||
|
||
当未认证用户尝试访问受保护页面时,系统在重定向到你的登录 URL 时会保留用户的目标地址。
|
||
|
||
1. 用户尝试访问受保护页面:`https://docs.foo.com/quickstart`。
|
||
2. 重定向到带有 redirect 查询参数的登录 URL:`https://foo.com/docs-login?redirect=%2Fquickstart`。
|
||
3. 认证完成后,重定向到 `https://docs.foo.com/login/jwt-callback?redirect=%2Fquickstart#{SIGNED_JWT}`。
|
||
4. 用户将进入其最初想要访问的页面。
|
||
</Tab>
|
||
</Tabs>
|
||
|
||
<div id="make-pages-public">
|
||
## 公开页面
|
||
</div>
|
||
|
||
在使用认证时,所有页面默认都需要通过认证才能访问。你可以在页面或分组级别通过 `public` 属性将特定页面设置为无需认证即可访问。
|
||
|
||
<div id="individual-pages">
|
||
### 单个页面
|
||
</div>
|
||
|
||
要将页面设为公开,请在该页面的 frontmatter 中添加 `public: true`。
|
||
|
||
```mdx Public page example
|
||
---
|
||
title: "公开页面"
|
||
public: true
|
||
---
|
||
```
|
||
|
||
<div id="groups-of-pages">
|
||
### 页面分组
|
||
</div>
|
||
|
||
要将某个分组中的所有页面设为公开,请在 `docs.json` 的 `navigation` 对象中,该分组名称下添加 `"public": true`。
|
||
|
||
```json Public group example
|
||
{
|
||
"navigation": {
|
||
"groups": [
|
||
{
|
||
"group": "公开组",
|
||
"public": true,
|
||
"icon": "play",
|
||
"pages": [
|
||
"quickstart",
|
||
"installation",
|
||
"settings"
|
||
]
|
||
},
|
||
{
|
||
"group": "私有组",
|
||
"icon": "pause",
|
||
"pages": [
|
||
"private-information",
|
||
"secret-settings"
|
||
]
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
<div id="control-access-with-groups">
|
||
## 使用 groups 控制访问
|
||
</div>
|
||
|
||
当你使用 OAuth 或 JWT(JSON Web Token)进行认证时,可以将特定页面仅限于某些用户组访问。若希望不同用户根据其角色或属性查看不同内容,这将非常有用。
|
||
|
||
通过在认证过程中传递的用户数据来管理 groups。详见 [用户数据格式](#user-data-format)。
|
||
|
||
```json Example user info
|
||
{
|
||
"groups": ["admin", "beta-users"],
|
||
"expiresAt": 1735689600
|
||
}
|
||
```
|
||
|
||
使用 frontmatter 中的 `groups` 属性来指定哪些 groups 可以访问特定页面。
|
||
|
||
```mdx Example page restricted to the admin group highlight={3}
|
||
---
|
||
title: "管理员控制台"
|
||
groups: ["admin"]
|
||
---
|
||
```
|
||
|
||
用户必须至少属于所列的一个 groups 才能访问该页面。如果用户在不具备所需分组的情况下尝试访问页面,将会收到 404 错误。
|
||
|
||
<div id="how-groups-interact-with-public-pages">
|
||
### groups 如何与公共页面配合使用
|
||
</div>
|
||
|
||
* 默认情况下,所有页面都需要认证。
|
||
* 具有 `groups` 属性的页面仅对属于这些 groups 的已认证用户可访问。
|
||
* 没有 `groups` 属性的页面对所有已认证用户可访问。
|
||
* 具有 `public: true` 且没有 `groups` 属性的页面对所有人可访问。
|
||
|
||
<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>
|
||
|
||
<div id="user-data-format">
|
||
## 用户数据格式
|
||
</div>
|
||
|
||
当使用 OAuth 或 JWT 认证时,系统会返回用户数据,用于控制会话时长、基于用户组成员关系的访问控制,以及[内容个性化](/zh/create/personalization)。
|
||
|
||
<CodeGroup>
|
||
```tsx Format
|
||
type User = {
|
||
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>;
|
||
};
|
||
};
|
||
```
|
||
|
||
```json Example
|
||
{
|
||
"expiresAt": 1735689600,
|
||
"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="expiresAt" type="number">
|
||
会话过期时间,以自 epoch 起算的秒数表示。当当前时间超过该值时,用户必须重新完成认证。
|
||
|
||
<Warning>**对于 JWT:** 这不同于 JWT 的 `exp` 声明,后者用于决定 JWT 何时被视为无效。出于安全考虑,应将 JWT 的 `exp` 声明设置为较短的时长(10 秒或更少)。使用 `expiresAt` 来表示实际会话时长(从数小时到数周)。</Warning>
|
||
</ParamField>
|
||
|
||
<ParamField path="groups" type="string[]">
|
||
用户所属用户组的列表。frontmatter 中带有匹配 `groups` 的页面对该用户可访问。
|
||
|
||
**示例**:具有 `groups: ["admin", "engineering"]` 的用户可以访问带有 `admin` 或 `engineering` 用户组标记的页面。
|
||
</ParamField>
|
||
|
||
<ParamField path="content" type="Record<string, any>">
|
||
可在 MDX 页面中通过 `user` 变量访问的自定义数据,用于[个性化内容](/zh/create/personalization#dynamic-mdx-content)。
|
||
</ParamField>
|
||
|
||
<ParamField path="apiPlaygroundInputs" type="object">
|
||
使用用户特定的值预填 API 操作台中的字段。当用户完成认证后,这些值会填充到 API 操作台中对应的输入字段。用户可以覆盖预填的值,其修改会持久保存在本地存储中。
|
||
|
||
只有与当前端点的安全方案匹配的值才会被应用。
|
||
|
||
<Expandable title="properties">
|
||
<ParamField path="header" type="Record<string, unknown>">
|
||
要预填的 Header 值,以 Header 名称作为 key。
|
||
</ParamField>
|
||
|
||
<ParamField path="query" type="Record<string, unknown>">
|
||
要预填的查询参数值,以参数名称作为 key。
|
||
</ParamField>
|
||
|
||
<ParamField path="cookie" type="Record<string, unknown>">
|
||
要预填的 Cookie 值,以 Cookie 名称作为 key。
|
||
</ParamField>
|
||
|
||
<ParamField path="server" type="Record<string, string>">
|
||
要预填的服务器变量值,以变量名称作为 key。
|
||
</ParamField>
|
||
</Expandable>
|
||
</ParamField> |