> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Token 端点：授权码换取与刷新令牌 | Didit 认证 API

> POST /token/ 可用 PKCE 授权码换取令牌，或轮换刷新令牌。重复使用刷新令牌将撤销会话。Didit 认证 API 文档。

> [!WARNING]
>
> 认证功能是新增的。这些端点由您的应用的签发方提供，例如 `https://<name>.auth.didit.me`，并且绝不接受您的 Didit API 密钥。

刷新令牌只能使用一次。再次发送已使用过的刷新令牌会撤销整个会话，因此同一会话一次只执行一个刷新操作，并需同时替换全部三个令牌。参阅[刷新令牌](https://docs.didit.me/zh/authentication/tokens-and-jwks.md#刷新令牌)。


## OpenAPI

````yaml zh/openapi-authentication.json POST /token/
openapi: 3.0.3
info:
  title: Didit Authentication API
  version: 1.0.0
  description: >-
    Didit Authentication 面向服务器的 endpoint，该产品是为您应用的终端用户提供的登录产品。Authentication
    为**新功能**，将于 2026-11-01 版本在生产环境中正式推出。


    ## 基础 URL


    每个应用都是其独立的 OpenID Connect 签发方（issuer），以下所有 endpoint 均在该签发方的根路径下提供服务：


    - 生产环境应用为 `https://<name>.auth.didit.me`，沙盒应用为
    `https://<name>.sandbox.auth.didit.me`。

    - 您的自定义域名（一旦激活）。激活后，它将取代默认主机作为签发方：新 token 会在 `iss`
    中标明该域名，发现文档（discovery）会指向它，并且可发布密钥也会改变。在默认主机下签发的 access token 将无法用于调用
    `/session/`、`/step-up/` 和 `/monitoring-token/`，直至完成刷新。


    生产环境与沙盒是独立的应用，因此它们是拥有独立密钥的不同签发方。可发布密钥为 `pk_live_`（生产环境）或
    `pk_test_`（沙盒），后接签发方去除末尾斜杠后的无填充 base64url 编码：对其解码即可找到签发方。它是一个公开标识符，并非凭据。


    相同的 JSON endpoint 也在
    `https://apx.didit.me/auth/v2/authentication/{application_id}/`
    下提供。无论由哪个地址签发，token 始终会将上述签发方声明在内。


    ## 凭据


    这些 endpoint 绝不接受您的 Didit API 密钥或控制台 token。


    - 发现文档（Discovery）、JWKS 以及 `POST /token/` 不需要任何凭据。客户端为公开客户端：不存在 client
    secret，且授权码（authorization code）与其 PKCE 验证器（`S256`）绑定。`client_id` 即为您的应用 ID。

    - `/session/`、`/step-up/` 和 `/monitoring-token/` 接收用户的 access
    token：`Authorization: Bearer
    <access_token>`。每次成功的调用均计为该会话的活动，因此会重置会话的空闲超时时间。


    ## 请求体


    所有接收请求体的 endpoint 均支持 JSON、表单编码（form-encoded）和 multipart 请求体。本文档列出了 JSON，以及
    OAuth 客户端发送 `POST /token/` 时使用的表单编码。如果 endpoint 收到不支持的字段，将返回 `400
    {"<field>": "unknown_field"}`。


    ## Token


    - **Access token**：请求头为 `typ: at+jwt` 的 ES256 JWT，有效期最多 5
    分钟，且绝不超过其会话时长。Claims 为 `AccessTokenClaims`。

    - **ID token**：请求头为 `typ: JWT` 的 ES256 JWT。Claims 为 `IDTokenClaims`。绝不能作为
    access token 使用。

    - **Refresh token**：随机字符串，每次使用时均会轮换。再次发送已使用的 refresh token 将会撤销该会话。


    使用来自 `GET /.well-known/jwks.json` 的密钥验证 token：算法必须为 `ES256`，`typ` 必须匹配 token
    类型，`iss` 必须等于您配置的签发方，且 `aud` 必须为您的应用 ID。离线验证仅在 access token
    过期后才能察觉已撤销的会话；`GET /session/` 则是即时检查手段。


    ## 错误


    | 响应体 | 适用情况 |

    | --- | --- |

    | `{"error": "<code>"}` | 来自 `POST /token/` 的错误，状态码均为 `400`，响应体格式符合 [RFC
    6749 第 5.2
    节](https://www.rfc-editor.org/rfc/rfc6749#section-5.2)：`invalid_grant`、`invalid_client`
    以及 Didit 的
    `mfa_required`。服务错误：`temporarily_unavailable`、`monitoring_unavailable`、`monitoring_disabled`。
    |

    | `{"<field>": ["<message>"]}` 或 `{"<field>": "<code>"}` |
    请求字段未通过验证。`non_field_errors` 表示跨字段规则校验失败，`unknown_field` 表示 endpoint 不接受的字段。
    |

    | `{"detail": "<message>"}` |
    `401`（`invalid_token`）、`403`（`origin_not_allowed`）、`404`（未知应用或 step-up）以及
    `429`（速率限制）。此外还包括无法解析请求体的 `400`（`JSON parse error - ...`）、endpoint 不支持该方法的
    `405` 以及不支持的 `Content-Type` 对应的 `415`。 |


    `401` 响应还会携带 `WWW-Authenticate: Bearer`，而 `429` 响应会携带以秒为单位的 `Retry-After`。


    有两种拒绝响应不是 JSON 格式。非签发方地址在到达这些 endpoint 之前就会被拒绝，例如未使用的 `*.auth.didit.me`
    名称会返回空白的 `404`。若 `step_up_id` 不是 UUID，则会返回 `404` 页面。


    ## 速率限制


    请求必须通过两层限制。


    **Endpoint 限制**，按客户端 IP 地址、应用及 endpoint 分别计数：


    - 发现文档（Discovery）与 JWKS：每分钟 1,200 次请求，每小时 12,000 次请求。

    - 其他所有 endpoint：每分钟 30 次请求，每小时 300 次请求。`GET` 和 `DELETE /session/` 共享同一个计数。


    超出限制时返回：`429 {"detail": "Too many requests from this IP address. Please wait
    before trying again. Expected available in 60 seconds."}`，小时限制则为 `3600
    seconds`。


    **Bot 防护**，适用于所有 `POST` 和 `DELETE` 请求。其桶由该应用的所有 `POST` 和 `DELETE`
    请求共享（包括托管登录步骤），并持续补充：


    - 每个客户端 IP 地址每分钟 60 次请求。

    - 每个设备每分钟 30 次请求（适用于像浏览器和原生 SDK 那样标识自身设备的请求）。

    - 整个应用（涵盖所有 IP 地址）每分钟 3,000 次请求。Token 刷新（带有 `grant_type=refresh_token` 的
    `POST /token/`）则使用独立的桶：整个应用每分钟 6,000 次。


    超出桶容量时返回：`429 {"detail": "rate_limited Expected available in N seconds."}`（当
    N 为 1 时为 `1 second.`）。基于 IP 的桶跨 endpoint 共享，因此混合调用 token、step-up
    和退出登录调用的单一地址可能会在触及任何 endpoint 限制之前就耗尽其额度。应用级别的桶会统计该应用的所有客户端，包括浏览器。


    两层限制均在 endpoint 执行前进行拦截并返回响应，因此收到 `429` 的请求并未实际执行：请在 `Retry-After`
    指定的时间之后重试（包括刷新请求）。


    ## 浏览器请求


    携带 `Origin` 请求头的请求必须来自应用自身的源或其允许的源，否则将收到 `403
    origin_not_allowed`。服务器到服务器的请求不发送 `Origin`。


    ## 此处未涵盖的内容


    以下路由用于托管页面、浏览器和原生 SDK 以及 AI agent。它们不属于服务器约定的一部分，服务器 SDK 也不会调用它们：


    - 登录步骤：`POST /authorize/`、`POST
    /password/`、`/email/*`、`/phone/*`、`/password-reset/*`、`/passkeys/*`、`/mfa/*`、`/social/*`
    和 `/protection/complete/`。

    -
    托管页面和回调：`/sign-in`、`/magic`、`/invite`、`/reset`、`/verify/`、`/sso/*`、`/wallet/callback/`、`/logo/`
    以及 `/.well-known/` 下的原生应用关联文件。

    - Agent 访问：`/agents/*` 和 `/grants/`，包括用于实时检查 AI agent 的 access token 的 `GET
    /agents/session/`。Agent 访问不属于当前版本的约定范围。

    - Authentication 的控制台管理 API（该 API 接收控制台会话）。


    Authentication 事件目前尚未作为 Webhook 投递，因此本文档未定义任何 Webhook。
servers:
  - url: https://{issuer}
    description: 您的应用的签发方：其登录来源。
    variables:
      issuer:
        default: your-app.auth.didit.me
        description: >-
          签发方主机：`<name>.auth.didit.me`（生产环境）、`<name>.sandbox.auth.didit.me`（沙盒）或您当前生效的自定义网域。
security: []
tags:
  - name: Issuer
    description: 发现和签名密钥，用于核验 token。
  - name: Tokens
    description: 授权码授予和刷新授予。
  - name: Sessions
    description: 检查并结束访问 token 背后的会话。
  - name: Step-ups
    description: 敏感操作前的验证。
  - name: Monitoring
    description: 用于交易监控事件的设备凭据。
paths:
  /token/:
    post:
      tags:
        - Tokens
      summary: 交换代码或刷新 token
      description: >-
        用于两种授权方式的 token endpoint。


        - `authorization_code` 将您的 `redirect_uri` 收到的 code 兑换为 access token、ID
        token 和 refresh token。该 code 仅可使用一次，有效期为 60 秒，并需配合 PKCE 验证器及其登录时完全一致的
        `redirect_uri`。当应用配置为每人仅允许一个会话时，该兑换操作将终止该用户的其他会话。

        - `refresh_token` 返回新的 access token、ID token 和 refresh token，并且您发送的
        refresh token 将失效。**再次发送已使用的 refresh token 会撤回该会话**并返回
        `invalid_grant`，因此请为每个会话单次执行刷新，并同时替换所有三个 token。在刷新以 `400`
        失败或未收到响应后，请重新发起登录而不是重试：在已成功的刷新之后进行重试属于重复使用。`429` 会在刷新执行前响应，因此在
        `Retry-After` 之后重试是安全的。`503` 表示刷新未发生且该 refresh token 仍然有效：请退避重试。


        请求体可以是 JSON 或表单编码格式。
      operationId: authentication_token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRequest'
            examples:
              AuthorizationCode:
                summary: 交换授权码
                value:
                  grant_type: authorization_code
                  code: Vb0bVt4CLlr8ZL0kE5mAxv8gS6R0rW3hYqC1pN7dJ2s
                  code_verifier: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
                  redirect_uri: https://app.example.com/auth/callback
              RefreshToken:
                summary: 刷新 token
                value:
                  grant_type: refresh_token
                  refresh_token: >-
                    N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/TokenRequest'
      responses:
        '200':
          description: 会话的新 token。
          headers:
            Cache-Control:
              description: 始终为 `no-store`：切勿缓存此响应。
              schema:
                type: string
                example: no-store
            Pragma:
              description: 始终为 `no-cache`。
              schema:
                type: string
                example: no-cache
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
              examples:
                Tokens:
                  summary: 新会话的 token
                  value:
                    access_token: >-
                      eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6ImF0K2p3dCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJSZ3F1NzE3OWUtbGhKMGJ2VmVELVlnIiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJjbGllbnRfaWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQifQ.AxrqN8Pm-Op-3OayZk8HcvlFQZhozRiOvQ0SgQsGgrz4UNeJlUEeSlq9B8WcYUuM8naRaY5kQt_TUofnsYcyYQ
                    id_token: >-
                      eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJNTHRPNHM4SHItQnJrcDd2NTlPV0Z3IiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJhdXRoX3RpbWUiOjE3OTIyMzM2NzQsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJlbWFpbCI6InBlcnNvbkBleGFtcGxlLmNvbSIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.avhfDFp_aUV5_1ssYzwDcIA7H6Waf19P0bn5GCg25FhLIZBGIlwc8HiUPGj1j26r21OkP4FQnS_jM1Ho3dHtKQ
                    refresh_token: >-
                      N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
                    token_type: Bearer
                    expires_in: 299
                    session_id: c9abf6cc-2d64-4bf2-b31e-a4d09a070ab1
        '400':
          description: >-
            授权失败。`invalid_grant`：code 或 refresh token 未知、已使用或已过期；PKCE 验证器或
            `redirect_uri` 不匹配，或 `redirect_uri`
            已不再注册；该用户已被屏蔽或其邮箱不再处于已验证状态；会话已结束或不再符合您的 MFA 策略；或者对于 code
            而言，您关闭了其来源的登录方式。关闭某种方式不会终止其已启动会话的刷新。`invalid_client`：`client_id`
            不是您的应用 ID。`mfa_required`：登录未满足您的 MFA
            策略。字段错误会指出缺失或无效的字段。无法解析的请求体将返回包含 `detail` 的消息。
          headers:
            Cache-Control:
              description: 始终为 `no-store`：切勿缓存此响应。
              schema:
                type: string
                example: no-store
            Pragma:
              description: 始终为 `no-cache`。
              schema:
                type: string
                example: no-cache
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/OAuthError'
                  - $ref: '#/components/schemas/FieldErrors'
                  - $ref: '#/components/schemas/Detail'
              examples:
                InvalidGrant:
                  summary: 验证码或刷新 token 已被拒绝
                  value:
                    error: invalid_grant
                InvalidClient:
                  summary: 错误的 client_id
                  value:
                    error: invalid_client
                MfaRequired:
                  summary: 不符合 MFA 策略
                  value:
                    error: mfa_required
                MissingFields:
                  summary: 缺少必填字段的授权
                  value:
                    non_field_errors:
                      - invalid_request
                UnknownGrant:
                  summary: 不支持的 grant_type
                  value:
                    grant_type:
                      - '"password" is not a valid choice.'
                UnknownField:
                  summary: endpoint 不接受的字段
                  value:
                    foo: unknown_field
                MalformedBody:
                  summary: 无法解析的 JSON 请求体
                  value:
                    detail: >-
                      JSON parse error - Expecting property name enclosed in
                      double quotes: line 1 column 2 (char 1)
        '403':
          $ref: '#/components/responses/OriginNotAllowed'
        '404':
          $ref: '#/components/responses/ApplicationNotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/TemporarilyUnavailable'
      security:
        - {}
components:
  schemas:
    TokenRequest:
      description: >-
        由 `grant_type` 选择的两种授权方式之一。该 endpoint
        接受两种授权方式的字段并验证接收到的每个字段，因此若刷新请求中同时携带了格式错误的
        `code_verifier`，将会收到字段错误。请仅发送您所选授权方式对应的字段；任何其他字段都将返回 `unknown_field`。
      oneOf:
        - $ref: '#/components/schemas/AuthorizationCodeGrantRequest'
        - $ref: '#/components/schemas/RefreshTokenGrantRequest'
      discriminator:
        propertyName: grant_type
        mapping:
          authorization_code: '#/components/schemas/AuthorizationCodeGrantRequest'
          refresh_token: '#/components/schemas/RefreshTokenGrantRequest'
    TokenResponse:
      type: object
      description: 单个会话的 token。刷新后须一并替换所有三个 token。
      required:
        - access_token
        - id_token
        - refresh_token
        - token_type
        - expires_in
        - session_id
      properties:
        access_token:
          type: string
          description: >-
            ES256 JWT，请求头为 `typ: at+jwt`。请将其作为 `Authorization: Bearer
            <access_token>` 发送。声明：`AccessTokenClaims`。
          example: >-
            eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6ImF0K2p3dCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJSZ3F1NzE3OWUtbGhKMGJ2VmVELVlnIiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJjbGllbnRfaWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQifQ.AxrqN8Pm-Op-3OayZk8HcvlFQZhozRiOvQ0SgQsGgrz4UNeJlUEeSlq9B8WcYUuM8naRaY5kQt_TUofnsYcyYQ
          x-didit-jwt:
            header: '#/components/schemas/AccessTokenHeader'
            claims: '#/components/schemas/AccessTokenClaims'
        id_token:
          type: string
          description: >-
            ES256 JWT，请求头为 `typ: JWT`，供您的应用读取登录用户信息。切勿将其作为访问 token
            发送。声明：`IDTokenClaims`。
          example: >-
            eyJhbGciOiJFUzI1NiIsImtpZCI6ImtfOHNKMmRRdlgwbkxyNVRmWWIzSGtQdyIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3lvdXItYXBwLmF1dGguZGlkaXQubWUiLCJhdWQiOiI3YjBhNmMyZS0zZjQxLTRjOWEtOGQ1NS0xZTJmM2E0YjVjNmQiLCJzdWIiOiIxYzlkOGI4OC0xYmZmLTQzZDEtYWQ3YS04OGIyYmMwMDI0ZTkiLCJzaWQiOiJjOWFiZjZjYy0yZDY0LTRiZjItYjMxZS1hNGQwOWEwNzBhYjEiLCJqdGkiOiJNTHRPNHM4SHItQnJrcDd2NTlPV0Z3IiwiaWF0IjoxNzkyMjMzNjc0LCJleHAiOjE3OTIyMzM5NzQsInZlbmRvcl9kYXRhIjoidXNlci00MiIsIm9yZyI6bnVsbCwidmVyaWZpZWQiOnt9LCJhbXIiOlsicGFzc3dvcmQiXSwiYWN0IjpudWxsLCJhdXRoX3RpbWUiOjE3OTIyMzM2NzQsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJlbWFpbCI6InBlcnNvbkBleGFtcGxlLmNvbSIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.avhfDFp_aUV5_1ssYzwDcIA7H6Waf19P0bn5GCg25FhLIZBGIlwc8HiUPGj1j26r21OkP4FQnS_jM1Ho3dHtKQ
          x-didit-jwt:
            header: '#/components/schemas/IDTokenHeader'
            claims: '#/components/schemas/IDTokenClaims'
        refresh_token:
          type: string
          description: 单次使用。在会话结束前有效，每次刷新都会返回一个新的 token。
          example: N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
        token_type:
          type: string
          enum:
            - Bearer
        expires_in:
          type: integer
          minimum: 0
          maximum: 300
          description: 距离访问 token 过期的秒数：最多 300 秒，若会话提前结束则更短。
          example: 299
        session_id:
          type: string
          format: uuid
          description: 这些 token 所属的会话，与 `sid` 声明的值相同。
          example: c9abf6cc-2d64-4bf2-b31e-a4d09a070ab1
    OAuthError:
      title: OAuth error
      type: object
      description: OAuth 错误（RFC 6749 第 5.2 节）或服务错误。
      required:
        - error
      properties:
        error:
          type: string
          example: invalid_grant
    FieldErrors:
      title: Field errors
      type: object
      description: >-
        按字段划分的验证错误。值为消息列表或单个代码（`unknown_field`、`invalid_redirect_uri`）。`non_field_errors`
        包含跨字段规则错误。
      additionalProperties:
        oneOf:
          - type: string
          - type: array
            items:
              type: string
    Detail:
      type: object
      required:
        - detail
      properties:
        detail:
          type: string
          example: invalid_token
    AuthorizationCodeGrantRequest:
      title: Authorization code grant
      type: object
      description: 使用重定向中的授权码换取 token。该授权码只能使用一次，有效期 60 秒。
      required:
        - grant_type
        - code
        - code_verifier
        - redirect_uri
      properties:
        grant_type:
          type: string
          enum:
            - authorization_code
        code:
          type: string
          maxLength: 128
          description: 您的 `redirect_uri` 收到的 `code` 查询参数。在进行兑换之前，请先核对回调的 `state`。
          example: Vb0bVt4CLlr8ZL0kE5mAxv8gS6R0rW3hYqC1pN7dJ2s
        code_verifier:
          type: string
          minLength: 43
          maxLength: 128
          pattern: ^[A-Za-z0-9._~-]{43,128}$
          description: 其 S256 哈希值作为登录 `code_challenge` 的 PKCE 验证器。
          example: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
        redirect_uri:
          type: string
          maxLength: 2048
          description: 发起登录时使用的完全一致的 `redirect_uri`。它必须仍然是该应用的重定向 URI 之一。
          example: https://app.example.com/auth/callback
        client_id:
          type: string
          maxLength: 64
          description: 可选。发送时，必须等于您的应用 ID，否则请求将失败并返回 `invalid_client`。
          example: 7b0a6c2e-3f41-4c9a-8d55-1e2f3a4b5c6d
        scope:
          type: string
          maxLength: 512
          description: 可选，并被标准 OpenID Connect 客户端接受。它绝不会扩大登录所授予的权限范围。
          example: openid email
    RefreshTokenGrantRequest:
      title: Refresh token grant
      type: object
      description: >-
        使用 refresh token 换取新的 access token、ID token 和 refresh token。该 refresh
        token 仅可使用一次：再次发送会导致该会话被撤销，因此每个会话一次只能执行一个刷新操作。
      required:
        - grant_type
        - refresh_token
      properties:
        grant_type:
          type: string
          enum:
            - refresh_token
        refresh_token:
          type: string
          maxLength: 128
          description: 来自上一次 token 响应的 `refresh_token`。
          example: N4yZTl6y24YKYq77X_SOrBn1q2Lit81WBQMOay7dq_IVfTnxyY8ZGcJk-6-fcmWW
        client_id:
          type: string
          maxLength: 64
          description: 可选。发送时，必须等于您的应用 ID，否则请求将失败并返回 `invalid_client`。
          example: 7b0a6c2e-3f41-4c9a-8d55-1e2f3a4b5c6d
        scope:
          type: string
          maxLength: 512
          description: 可选，并被标准 OpenID Connect 客户端接受。刷新操作绝不会扩大该会话所包含的内容范围。
          example: openid email
  responses:
    OriginNotAllowed:
      description: 请求携带的 `Origin` 既不是该应用自身的源，也不在其允许的源列表中。服务端请求不发送 `Origin`。
      headers:
        Cache-Control:
          description: 始终为 `no-store`：切勿缓存此响应。
          schema:
            type: string
            example: no-store
        Pragma:
          description: 始终为 `no-cache`。
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            OriginNotAllowed:
              summary: 来源（Origin）不允许
              value:
                detail: origin_not_allowed
    ApplicationNotFound:
      description: >-
        该地址属于某个应用，但该应用尚未启用 Authentication 功能；在 `apx.didit.me` 地址上，应用 ID
        未知。不属于任何应用的地址在到达 endpoint 之前即被拒绝，且该响应不是 JSON 格式。
      headers:
        Cache-Control:
          description: 始终为 `no-store`：切勿缓存此响应。
          schema:
            type: string
            example: no-store
        Pragma:
          description: 始终为 `no-cache`。
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            ApplicationNotFound:
              summary: 未知应用
              value:
                detail: No Configuration matches the given query.
    RateLimited:
      description: >-
        此地址针对该 endpoint 的请求过多，或者在 `POST` 或 `DELETE` 上机器人防护令牌桶已耗尽。未执行任何操作：请等待
        `Retry-After` 秒后重试。
      headers:
        Retry-After:
          description: 重试前需要等待的秒数。
          schema:
            type: integer
            example: 60
        Cache-Control:
          description: 始终为 `no-store`：切勿缓存此响应。
          schema:
            type: string
            example: no-store
        Pragma:
          description: 始终为 `no-cache`。
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Detail'
          examples:
            RateLimited:
              summary: endpoint 限制
              value:
                detail: >-
                  Too many requests from this IP address. Please wait before
                  trying again. Expected available in 60 seconds.
            BotProtection:
              summary: 机器人防护限制（POST 和 DELETE）
              value:
                detail: rate_limited Expected available in 2 seconds.
    TemporarilyUnavailable:
      description: 请求所依赖的服务暂时不可用。请采用退避策略重试。
      headers:
        Cache-Control:
          description: 始终为 `no-store`：切勿缓存此响应。
          schema:
            type: string
            example: no-store
        Pragma:
          description: 始终为 `no-cache`。
          schema:
            type: string
            example: no-cache
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/OAuthError'
          examples:
            TemporarilyUnavailable:
              summary: 稍后重试
              value:
                error: temporarily_unavailable

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.