{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "SAI Identity OIDC 接入接口",
    "version": "1.0.0",
    "description": "仅描述SAI公开Identity协议接入范围。Discovery是运行时端点权威，已登记客户端政策决定实际授权。本文件不包含管理、业务或内部API，不代表匿名注册已开放。请勿在文档网页提交真实授权码、令牌或密钥。"
  },
  "servers": [
    {
      "url": "https://id.open-sai.com/realms/sai",
      "description": "SAI OIDC issuer；/realms/sai为其固定组成部分。"
    }
  ],
  "externalDocs": {
    "description": "SAI 账号接入文档",
    "url": "https://www.open-sai.com/docs/identity/"
  },
  "tags": [
    {
      "name": "Discovery",
      "description": "公开元数据与签名公钥"
    },
    {
      "name": "Login",
      "description": "经登记的授权码登录"
    },
    {
      "name": "Tokens",
      "description": "客户端认证、在线续期和另行批准的内省"
    },
    {
      "name": "Profile",
      "description": "按scope读取当前用户资料"
    },
    {
      "name": "Sessions",
      "description": "撤销授权与退出登录"
    }
  ],
  "x-documentation-interactive-requests": false,
  "x-supported-partner-profile": {
    "response_type": "code",
    "pkce": "S256",
    "registration": "reviewed manual native registration",
    "scopes": {
      "openid": "OIDC identity",
      "profile": "nickname",
      "email": "email, email_verified",
      "sai.profile": "institution_name, research_area, profile_revision; separate approval",
      "sai.assurance": "Current verification status only; separate approval and user consent",
      "sai.profile.details": "bio, website, location; separate approval and user consent"
    },
    "not_open": [
      "anonymous dynamic registration"
    ],
    "separate_approval": [
      "public client",
      "offline_access",
      "client_credentials",
      "device authorization",
      "token introspection"
    ],
    "forbidden": [
      "implicit",
      "password grant",
      "PKCE plain",
      "wildcard redirects"
    ]
  },
  "paths": {
    "/.well-known/openid-configuration": {
      "get": {
        "operationId": "getOpenIDConfiguration",
        "tags": [
          "Discovery"
        ],
        "summary": "读取 OIDC Discovery",
        "security": [],
        "responses": {
          "200": {
            "description": "当前公开端点及引擎元数据。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Discovery"
                }
              }
            }
          }
        }
      }
    },
    "/protocol/openid-connect/certs": {
      "get": {
        "operationId": "getJWKS",
        "tags": [
          "Discovery"
        ],
        "summary": "读取签名公钥",
        "description": "实际 URL 以 Discovery 的 jwks_uri 为准，客户端应支持公钥轮换。",
        "security": [],
        "responses": {
          "200": {
            "description": "JSON Web Key Set。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JWKS"
                }
              }
            }
          }
        }
      }
    },
    "/protocol/openid-connect/auth": {
      "get": {
        "operationId": "authorize",
        "tags": [
          "Login"
        ],
        "summary": "浏览器发起授权码登录",
        "description": "仅 response_type=code、PKCE S256。state/nonce/verifier 由应用生成并绑定一次性事务。普通登录不强制 prompt=login，复用有效 SSO。",
        "security": [],
        "parameters": [
          {
            "name": "response_type",
            "in": "query",
            "description": "固定为 code。",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "code"
              ]
            }
          },
          {
            "name": "client_id",
            "in": "query",
            "description": "准确登记的客户端 ID。",
            "required": true,
            "schema": {
              "type": "string",
              "example": "example-web"
            }
          },
          {
            "name": "redirect_uri",
            "in": "query",
            "description": "准确登记的 HTTPS 回调，和换令牌时相同。",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri",
              "example": "https://app.example.org/oidc/callback"
            }
          },
          {
            "name": "scope",
            "in": "query",
            "description": "只请求获批 scope，必须包含 openid。",
            "required": true,
            "schema": {
              "type": "string",
              "example": "openid profile email"
            }
          },
          {
            "name": "state",
            "in": "query",
            "description": "一次性随机值，与浏览器事务绑定；回调成功或失败均校验。",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "nonce",
            "in": "query",
            "description": "绑定 ID token 的一次性随机值。",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "code_challenge",
            "in": "query",
            "description": "BASE64URL(SHA256(code_verifier))，无填充。",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 43,
              "maxLength": 43,
              "pattern": "^[A-Za-z0-9_-]+$"
            }
          },
          {
            "name": "code_challenge_method",
            "in": "query",
            "description": "只允许 S256。",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "S256"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SAI 登录或授权确认页面。",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "302": {
            "description": "重定向至SAI 登录验证页面或准确登记的客户端回调。回调含 code/state 或 error/state。",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "303": {
            "description": "跳转到下一步登录页面。",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "400": {
            "description": "请求无效；未验证的回调不会用于错误重定向。"
          },
          "default": {
            "description": "协议或服务错误，可能为 HTML。"
          }
        }
      }
    },
    "/protocol/openid-connect/token": {
      "post": {
        "operationId": "exchangeOrRefreshToken",
        "tags": [
          "Tokens"
        ],
        "summary": "兑换授权码或续期在线令牌",
        "description": "只可使用登记的认证方式。机密客户端优先 private_key_jwt，或获批的 HTTP Basic。security 中的空对象仅为表单中的 JWT assertion 或明确登记的 public client 留出表达空间，不表示未登记的应用可以匿名调用。JWT assertion 使用登记认可的 token endpoint audience。 本文只支持 authorization_code 与获批的 refresh_token，Discovery 列出 password/implicit 等流程，不代表当前应用可以使用。",
        "security": [
          {
            "clientSecretBasic": []
          },
          {}
        ],
        "x-client-authentication-methods": [
          "private_key_jwt",
          "client_secret_basic",
          "none (registered public clients only)"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/AuthorizationCodeGrant"
                  },
                  {
                    "$ref": "#/components/schemas/RefreshGrant"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "令牌响应；按 expires_in 续期并原子替换轮换凭据。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                }
              }
            }
          },
          "400": {
            "description": "请求或授权凭据无效，按 error 分类。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "401": {
            "description": "客户端认证未通过；不能据此认定用户会话已结束。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "429": {
            "description": "请求过于频繁，遵守 Retry-After。",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "503": {
            "description": "服务暂不可用，保留可恢复状态，不扩大权限。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "default": {
            "description": "其他协议或网关错误；正文可能不是 JSON，不能直接显示上游内容。"
          }
        }
      }
    },
    "/protocol/openid-connect/userinfo": {
      "get": {
        "operationId": "getUserInfo",
        "tags": [
          "Profile"
        ],
        "summary": "读取当前授权用户资料",
        "description": "访问令牌只放Authorization头，不放URL。sub须和当前已验证ID token一致；资料不授予业务权限。",
        "security": [
          {
            "userAccessToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "本次授权允许的当前资料。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserInfo"
                }
              }
            }
          },
          "401": {
            "description": "访问令牌无效，根据令牌有效期和刷新结果处理。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "403": {
            "description": "授权范围不足。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "503": {
            "description": "当前来源暂不可用；不造失效状态或旧资料成功。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "default": {
            "description": "协议或网关错误。"
          }
        }
      },
      "post": {
        "operationId": "postUserInfo",
        "tags": [
          "Profile"
        ],
        "summary": "读取当前授权用户资料",
        "description": "访问令牌只放Authorization头，不放URL。sub须和当前已验证ID token一致；资料不授予业务权限。",
        "security": [
          {
            "userAccessToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "本次授权允许的当前资料。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserInfo"
                }
              }
            }
          },
          "401": {
            "description": "访问令牌无效，根据令牌有效期和刷新结果处理。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "403": {
            "description": "授权范围不足。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "503": {
            "description": "当前来源暂不可用；不造失效状态或旧资料成功。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "default": {
            "description": "协议或网关错误。"
          }
        }
      }
    },
    "/protocol/openid-connect/revoke": {
      "post": {
        "operationId": "revokeToken",
        "tags": [
          "Sessions"
        ],
        "summary": "撤销令牌",
        "description": "只可使用登记的认证方式。机密客户端优先 private_key_jwt，或获批的 HTTP Basic。security 中的空对象仅为表单中的 JWT assertion 或明确登记的 public client 留出表达空间，不表示未登记的应用可以匿名调用。JWT assertion 使用登记认可的 token endpoint audience。 成功不代表所有已签发的访问令牌及资源缓存立即失效。未知令牌也可能成功。",
        "security": [
          {
            "clientSecretBasic": []
          },
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/RevocationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "撤销已处理，通常无正文；未知或已失效令牌也可返回此状态。"
          },
          "400": {
            "description": "请求或授权凭据无效，按 error 分类。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "401": {
            "description": "客户端认证未通过；不能据此认定用户会话已结束。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "429": {
            "description": "请求过于频繁，遵守 Retry-After。",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "503": {
            "description": "服务暂不可用，保留可恢复状态，不扩大权限。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "default": {
            "description": "其他协议或网关错误；正文可能不是 JSON，不能直接显示上游内容。"
          }
        }
      }
    },
    "/protocol/openid-connect/token/introspect": {
      "post": {
        "operationId": "introspectToken",
        "tags": [
          "Tokens"
        ],
        "summary": "受控资源服务查询令牌状态",
        "description": "仅限另行批准的机密资源服务。端点公开不授予任意令牌查询权。使用登记的 private_key_jwt 或 client_secret_basic；security 中的空对象仅表示通过表单进行 JWT 客户端认证，不允许 public client 或匿名调用。JWT assertion 的 audience 按登记的客户端认证配置使用 token endpoint。",
        "security": [
          {
            "clientSecretBasic": []
          },
          {}
        ],
        "x-client-authentication-methods": [
          "private_key_jwt",
          "client_secret_basic"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/IntrospectionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "令牌状态；无效或不可用令牌可返回 active=false。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntrospectionResponse"
                }
              }
            }
          },
          "400": {
            "description": "请求或授权凭据无效，按 error 分类。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "401": {
            "description": "客户端认证未通过；不能据此认定用户会话已结束。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "429": {
            "description": "请求过于频繁，遵守 Retry-After。",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "503": {
            "description": "服务暂不可用，保留可恢复状态，不扩大权限。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "default": {
            "description": "其他协议或网关错误；正文可能不是 JSON，不能直接显示上游内容。"
          }
        }
      }
    },
    "/protocol/openid-connect/logout": {
      "get": {
        "operationId": "getEndSession",
        "tags": [
          "Sessions"
        ],
        "summary": "结束 SAI SSO 会话",
        "description": "以Discovery end_session_endpoint为准。建议POST避免ID token hint进入URL；未提供hint时可能需用户确认。结束SSO不等于删除数据或撤销全部离线授权。",
        "security": [],
        "responses": {
          "200": {
            "description": "退出确认或结果页面。",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "302": {
            "description": "重定向到准确登记的退出返回地址或退出确认页面。",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "303": {
            "description": "退出流程重定向。",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "400": {
            "description": "退出请求、hint或返回地址不合法。"
          },
          "default": {
            "description": "服务不可用或协议失败；不伪造退出成功。"
          }
        },
        "parameters": [
          {
            "name": "client_id",
            "in": "query",
            "description": "登记客户端 ID。",
            "required": false,
            "schema": {
              "type": "string",
              "description": "登记客户端 ID。",
              "example": "example-web"
            }
          },
          {
            "name": "id_token_hint",
            "in": "query",
            "description": "对应当前退出会话的 ID token，建议经 POST 提交。",
            "required": false,
            "schema": {
              "type": "string",
              "description": "对应当前退出会话的 ID token，建议经 POST 提交。",
              "writeOnly": true
            }
          },
          {
            "name": "post_logout_redirect_uri",
            "in": "query",
            "description": "准确登记的退出返回 URL。",
            "required": false,
            "schema": {
              "type": "string",
              "description": "准确登记的退出返回 URL。",
              "format": "uri",
              "example": "https://app.example.org/signed-out"
            }
          },
          {
            "name": "state",
            "in": "query",
            "description": "客户端保存并在退出返回时验证的随机状态。",
            "required": false,
            "schema": {
              "type": "string",
              "description": "客户端保存并在退出返回时验证的随机状态。"
            }
          }
        ]
      },
      "post": {
        "operationId": "postEndSession",
        "tags": [
          "Sessions"
        ],
        "summary": "结束 SAI SSO 会话",
        "description": "以Discovery end_session_endpoint为准。建议POST避免ID token hint进入URL；未提供hint时可能需用户确认。结束SSO不等于删除数据或撤销全部离线授权。",
        "security": [],
        "responses": {
          "200": {
            "description": "退出确认或结果页面。",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "302": {
            "description": "重定向到准确登记的退出返回地址或退出确认页面。",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "303": {
            "description": "退出流程重定向。",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "400": {
            "description": "退出请求、hint或返回地址不合法。"
          },
          "default": {
            "description": "服务不可用或协议失败；不伪造退出成功。"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/LogoutRequest"
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "backchannelLogout": {
      "post": {
        "operationId": "receiveBackchannelLogout",
        "tags": [
          "Sessions"
        ],
        "summary": "应用接收签名后通道退出通知",
        "description": "这是应用自有的已登记HTTPS接收端，不位于SAI issuer。使用成熟OIDC库验证logout_token；不依赖浏览器Cookie/CSRF。仅失效验证目标，重放应幂等。",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/BackchannelLogoutRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "匹配会话已失效或重复通知已幂等处理。"
          },
          "400": {
            "description": "退出令牌无效或无法验证。"
          },
          "503": {
            "description": "暂时无法完成退出处理。"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "clientSecretBasic": {
        "type": "http",
        "scheme": "basic",
        "description": "仅获批client_secret_basic的机密客户端；不要在文档页面输入真实密钥。"
      },
      "userAccessToken": {
        "type": "http",
        "scheme": "bearer",
        "description": "当前用户的访问令牌，仅在Authorization头中使用。"
      },
      "oidc": {
        "type": "openIdConnect",
        "openIdConnectUrl": "https://id.open-sai.com/realms/sai/.well-known/openid-configuration",
        "description": "通过登记的Authorization Code + PKCE S256获取令牌。"
      }
    },
    "schemas": {
      "Discovery": {
        "type": "object",
        "required": [
          "issuer",
          "authorization_endpoint",
          "token_endpoint",
          "userinfo_endpoint",
          "jwks_uri"
        ],
        "description": "运行时端点和引擎能力的权威来源。Discovery 列出的能力不等于应用获准使用的权限；第三方仅可使用登记时批准的授权码与 PKCE S256 等流程。",
        "properties": {
          "issuer": {
            "type": "string",
            "format": "uri"
          },
          "authorization_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "token_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "userinfo_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "jwks_uri": {
            "type": "string",
            "format": "uri"
          },
          "revocation_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "introspection_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "end_session_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "grant_types_supported": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "引擎能力，不是 partner 授权清单。"
          },
          "response_types_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "token_endpoint_auth_methods_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "code_challenge_methods_supported": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "仅支持 S256。"
          },
          "backchannel_logout_supported": {
            "type": "boolean"
          },
          "backchannel_logout_session_supported": {
            "type": "boolean"
          }
        },
        "additionalProperties": true
      },
      "JWKS": {
        "type": "object",
        "required": [
          "keys"
        ],
        "properties": {
          "keys": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/JWK"
            }
          }
        },
        "additionalProperties": true
      },
      "JWK": {
        "type": "object",
        "required": [
          "kty"
        ],
        "properties": {
          "kty": {
            "type": "string"
          },
          "kid": {
            "type": "string"
          },
          "use": {
            "type": "string"
          },
          "alg": {
            "type": "string"
          },
          "n": {
            "type": "string"
          },
          "e": {
            "type": "string"
          },
          "crv": {
            "type": "string"
          },
          "x": {
            "type": "string"
          },
          "y": {
            "type": "string"
          }
        },
        "description": "JSON Web Key 公钥。只接受登记允许的签名算法，由成熟 OIDC 库完成校验。",
        "additionalProperties": true
      },
      "OAuthError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "标准 OAuth 错误码；不要把 invalid_client、unauthorized_client 或 5xx 当作用户注销。"
          },
          "error_description": {
            "type": "string",
            "description": "仅供受控诊断，不直接显示或原样记录。"
          },
          "error_uri": {
            "type": "string",
            "format": "uri"
          },
          "state": {
            "type": "string",
            "description": "授权响应中可能返回原 state，必须校验。"
          }
        },
        "additionalProperties": true
      },
      "AuthorizationCodeGrant": {
        "type": "object",
        "required": [
          "grant_type",
          "code",
          "redirect_uri",
          "code_verifier"
        ],
        "properties": {
          "client_id": {
            "type": "string",
            "description": "登记的客户端 ID，示例不是可用凭据。",
            "example": "example-web"
          },
          "client_assertion_type": {
            "type": "string",
            "const": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
          },
          "client_assertion": {
            "type": "string",
            "description": "短时、一次性的签名客户端 assertion，按登记认证；不得记录或提交到文档站。",
            "minLength": 1,
            "writeOnly": true
          },
          "grant_type": {
            "type": "string",
            "const": "authorization_code"
          },
          "code": {
            "type": "string",
            "description": "一次性授权码，仅能在发起该登录的应用中使用一次。",
            "minLength": 1,
            "writeOnly": true
          },
          "redirect_uri": {
            "type": "string",
            "description": "与授权请求相同且已准确登记的 HTTPS 回调。",
            "format": "uri",
            "example": "https://app.example.org/oidc/callback"
          },
          "code_verifier": {
            "type": "string",
            "description": "原事务的 PKCE verifier。",
            "minLength": 43,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9._~-]+$",
            "writeOnly": true
          }
        },
        "dependentRequired": {
          "client_assertion": [
            "client_id",
            "client_assertion_type"
          ],
          "client_assertion_type": [
            "client_id",
            "client_assertion"
          ]
        },
        "description": "只可使用登记的认证方式。机密客户端优先 private_key_jwt，或获批的 HTTP Basic。security 中的空对象仅为表单中的 JWT assertion 或明确登记的 public client 留出表达空间，不表示未登记的应用可以匿名调用。JWT assertion 使用登记认可的 token endpoint audience。",
        "additionalProperties": false
      },
      "RefreshGrant": {
        "type": "object",
        "required": [
          "grant_type",
          "refresh_token"
        ],
        "properties": {
          "client_id": {
            "type": "string",
            "description": "登记的客户端 ID，示例不是可用凭据。",
            "example": "example-web"
          },
          "client_assertion_type": {
            "type": "string",
            "const": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
          },
          "client_assertion": {
            "type": "string",
            "description": "短时、一次性的签名客户端 assertion，按登记认证；不得记录或提交到文档站。",
            "minLength": 1,
            "writeOnly": true
          },
          "grant_type": {
            "type": "string",
            "const": "refresh_token"
          },
          "refresh_token": {
            "type": "string",
            "description": "当前有效的刷新令牌；轮换后原子替换，不并发重放。",
            "minLength": 1,
            "writeOnly": true
          }
        },
        "dependentRequired": {
          "client_assertion": [
            "client_id",
            "client_assertion_type"
          ],
          "client_assertion_type": [
            "client_id",
            "client_assertion"
          ]
        },
        "description": "只可使用登记的认证方式。机密客户端优先 private_key_jwt，或获批的 HTTP Basic。security 中的空对象仅为表单中的 JWT assertion 或明确登记的 public client 留出表达空间，不表示未登记的应用可以匿名调用。JWT assertion 使用登记认可的 token endpoint audience。",
        "additionalProperties": false
      },
      "TokenResponse": {
        "type": "object",
        "required": [
          "access_token",
          "token_type",
          "expires_in"
        ],
        "properties": {
          "access_token": {
            "type": "string",
            "description": "敏感访问令牌，仅用于获批 audience 与 scope。",
            "minLength": 1
          },
          "token_type": {
            "type": "string",
            "example": "Bearer"
          },
          "expires_in": {
            "type": "integer",
            "minimum": 0,
            "description": "访问令牌剩余秒数，应用据此提前续期；不等同应用 Cookie 有效期。"
          },
          "refresh_token": {
            "type": "string",
            "description": "可能返回；新值应原子替换旧值。",
            "minLength": 1
          },
          "refresh_expires_in": {
            "type": "integer",
            "minimum": 0,
            "description": "引擎扩展。缺失或为 0 不承诺永久有效。"
          },
          "id_token": {
            "type": "string",
            "description": "经过签名的 ID token，验证后才建立登录；不是业务访问令牌。",
            "minLength": 1
          },
          "scope": {
            "type": "string",
            "description": "本次实际获准的空格分隔 scope。",
            "example": "openid profile email"
          },
          "session_state": {
            "type": "string",
            "description": "引擎扩展，不替代经过验证的 ID token sid。"
          }
        },
        "additionalProperties": true
      },
      "IDTokenClaims": {
        "type": "object",
        "required": [
          "iss",
          "sub",
          "aud",
          "exp",
          "iat"
        ],
        "description": "签名 JWT 解码后的说明模型，不是独立接口。必须验签、issuer/audience/azp、时间和本次 nonce，不能只按此 JSON schema 判断登录有效。",
        "properties": {
          "iss": {
            "type": "string",
            "const": "https://id.open-sai.com/realms/sai"
          },
          "sub": {
            "type": "string",
            "description": "不透明且区分大小写的主体，与 iss 一起关联应用账号。",
            "minLength": 1
          },
          "aud": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "minItems": 1
              }
            ]
          },
          "exp": {
            "type": "integer"
          },
          "iat": {
            "type": "integer"
          },
          "auth_time": {
            "type": "integer"
          },
          "nonce": {
            "type": "string",
            "description": "须与本次授权请求保存的 nonce 一致。"
          },
          "azp": {
            "type": "string",
            "description": "按 OIDC 规则验证授权方，尤其是多 audience 令牌。"
          },
          "sid": {
            "type": "string",
            "description": "会话标识，用于签名后通道退出关联。"
          },
          "acr": {
            "type": "string",
            "description": "本次认证上下文，不是长期认证资格。"
          },
          "amr": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "本次认证方法，不是应用角色或业务权限。"
          }
        },
        "additionalProperties": true
      },
      "UserInfo": {
        "type": "object",
        "required": [
          "sub"
        ],
        "description": "仅列当前已实现字段；未授权或未填写的字段可能不返回。不能根据缺失或未知字段授予权限。sub 须与本次有效 ID token 一致。",
        "properties": {
          "sub": {
            "type": "string",
            "description": "pairwise 主体，配合固定 issuer 使用。",
            "minLength": 1
          },
          "nickname": {
            "type": "string",
            "description": "profile；可变且不唯一的昵称。",
            "maxLength": 30
          },
          "email": {
            "type": "string",
            "description": "email；当前主邮箱，不是公开资料。",
            "format": "email"
          },
          "email_verified": {
            "type": "boolean",
            "description": "email；只表示返回邮箱的验证状态。"
          },
          "institution_name": {
            "type": "string",
            "description": "sai.profile；单独获批后提供的用户自述。",
            "maxLength": 255
          },
          "research_area": {
            "type": "string",
            "description": "sai.profile；单独获批后提供的用户自述。",
            "maxLength": 255
          },
          "profile_revision": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "sai.profile；共享资料版本，不是权限版本。"
          },
          "sai.assurance": {
            "$ref": "#/components/schemas/Assurance"
          },
          "bio": {
            "type": "string",
            "maxLength": 1000,
            "description": "sai.profile.details；纯文本个人简介，可含换行，需单独获批并取得用户同意。"
          },
          "website": {
            "type": "string",
            "maxLength": 2048,
            "description": "sai.profile.details；HTTP(S) 个人主页，不含用户名和密码，需单独获批并取得用户同意。"
          },
          "location": {
            "type": "string",
            "maxLength": 255,
            "description": "sai.profile.details；用户自填所在地，不代表验证位置，需单独获批并取得用户同意。"
          }
        },
        "additionalProperties": true,
        "example": {
          "sub": "opaque-example-subject",
          "nickname": "科研用户",
          "email": "researcher@example.org",
          "email_verified": true
        }
      },
      "RevocationRequest": {
        "type": "object",
        "required": [
          "token"
        ],
        "properties": {
          "client_id": {
            "type": "string",
            "description": "登记的客户端 ID，示例不是可用凭据。",
            "example": "example-web"
          },
          "client_assertion_type": {
            "type": "string",
            "const": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
          },
          "client_assertion": {
            "type": "string",
            "description": "短时、一次性的签名客户端 assertion，按登记认证；不得记录或提交到文档站。",
            "minLength": 1,
            "writeOnly": true
          },
          "token": {
            "type": "string",
            "description": "待撤销的令牌。",
            "minLength": 1,
            "writeOnly": true
          },
          "token_type_hint": {
            "type": "string",
            "enum": [
              "refresh_token",
              "access_token"
            ]
          }
        },
        "dependentRequired": {
          "client_assertion": [
            "client_id",
            "client_assertion_type"
          ],
          "client_assertion_type": [
            "client_id",
            "client_assertion"
          ]
        },
        "description": "只可使用登记的认证方式。机密客户端优先 private_key_jwt，或获批的 HTTP Basic。security 中的空对象仅为表单中的 JWT assertion 或明确登记的 public client 留出表达空间，不表示未登记的应用可以匿名调用。JWT assertion 使用登记认可的 token endpoint audience。",
        "additionalProperties": false
      },
      "IntrospectionRequest": {
        "type": "object",
        "required": [
          "token"
        ],
        "properties": {
          "client_id": {
            "type": "string",
            "description": "登记的客户端 ID，示例不是可用凭据。",
            "example": "example-web"
          },
          "client_assertion_type": {
            "type": "string",
            "const": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
          },
          "client_assertion": {
            "type": "string",
            "description": "短时、一次性的签名客户端 assertion，按登记认证；不得记录或提交到文档站。",
            "minLength": 1,
            "writeOnly": true
          },
          "token": {
            "type": "string",
            "description": "仅供另外获批的机密资源服务查询。",
            "minLength": 1,
            "writeOnly": true
          },
          "token_type_hint": {
            "type": "string",
            "enum": [
              "access_token",
              "refresh_token"
            ]
          }
        },
        "dependentRequired": {
          "client_assertion": [
            "client_id",
            "client_assertion_type"
          ],
          "client_assertion_type": [
            "client_id",
            "client_assertion"
          ]
        },
        "description": "仅限另行批准的机密资源服务。端点公开不授予任意令牌查询权。使用登记的 private_key_jwt 或 client_secret_basic；security 中的空对象仅表示通过表单进行 JWT 客户端认证，不允许 public client 或匿名调用。JWT assertion 的 audience 按登记的客户端认证配置使用 token endpoint。",
        "additionalProperties": false
      },
      "IntrospectionResponse": {
        "type": "object",
        "required": [
          "active"
        ],
        "properties": {
          "active": {
            "type": "boolean"
          },
          "scope": {
            "type": "string"
          },
          "client_id": {
            "type": "string"
          },
          "token_type": {
            "type": "string"
          },
          "exp": {
            "type": "integer"
          },
          "iat": {
            "type": "integer"
          },
          "sub": {
            "type": "string"
          },
          "iss": {
            "type": "string",
            "format": "uri"
          },
          "aud": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ]
          }
        },
        "additionalProperties": true,
        "example": {
          "active": false
        }
      },
      "LogoutRequest": {
        "type": "object",
        "properties": {
          "client_id": {
            "type": "string",
            "description": "登记客户端 ID。",
            "example": "example-web"
          },
          "id_token_hint": {
            "type": "string",
            "description": "对应当前退出会话的 ID token，建议经 POST 提交。",
            "writeOnly": true
          },
          "post_logout_redirect_uri": {
            "type": "string",
            "description": "准确登记的退出返回 URL。",
            "format": "uri",
            "example": "https://app.example.org/signed-out"
          },
          "state": {
            "type": "string",
            "description": "客户端保存并在退出返回时验证的随机状态。"
          }
        },
        "additionalProperties": false
      },
      "BackchannelLogoutRequest": {
        "type": "object",
        "required": [
          "logout_token"
        ],
        "properties": {
          "logout_token": {
            "type": "string",
            "description": "签名 logout token。校验签名、iss/aud/iat/jti、logout event、sid或sub，且不得含nonce；按验证目标幂等失效会话。",
            "minLength": 1,
            "writeOnly": true
          }
        },
        "additionalProperties": false
      },
      "Assurance": {
        "type": "object",
        "description": "仅经批准并由用户同意的sai.assurance授权提供。仅出现在UserInfo，缺失不代表未认证；503表示暂时无法读取。无实名、证件、认证证据或应用权限。",
        "required": [
          "schema_version",
          "researcher_current",
          "government_identity_current",
          "institutional_email_current",
          "event_eligibility_current",
          "assurance_revision",
          "evaluated_at",
          "next_evaluation_at"
        ],
        "properties": {
          "schema_version": {
            "type": "integer",
            "const": 1
          },
          "researcher_current": {
            "type": "boolean",
            "description": "当前满足科研用户认证条件。"
          },
          "government_identity_current": {
            "type": "boolean",
            "description": "当前实名认证有效。"
          },
          "institutional_email_current": {
            "type": "boolean",
            "description": "当前机构邮箱认证有效。"
          },
          "event_eligibility_current": {
            "type": "boolean",
            "description": "当前活动资格有效。"
          },
          "assurance_revision": {
            "type": "integer",
            "minimum": 1,
            "description": "相关记录或认证结果变化时递增；不能作为永久授权依据。"
          },
          "evaluated_at": {
            "type": "string",
            "format": "date-time",
            "description": "本次检查时间，使用UTC。"
          },
          "next_evaluation_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "已知的下一次可能变化时间，使用UTC；没有预定变化时为null。"
          }
        },
        "additionalProperties": false
      }
    }
  }
}
