mirror of
https://github.com/iflytek/skillhub.git
synced 2026-08-27 11:14:59 +00:00
docs(auth): document CLI token failure semantics (#605)
Signed-off-by: dongmucat <1127093059@qq.com>
This commit is contained in:
parent
06cecd4237
commit
5805e0f1d3
2 changed files with 297 additions and 4 deletions
|
|
@ -621,10 +621,15 @@ window.location.href = '/oauth2/authorization/github'
|
|||
|
||||
### 10.3 CLI API
|
||||
|
||||
| 接口 | 所需凭证 | 额外判定 |
|
||||
|------|---------|---------|
|
||||
| `GET /api/v1/whoami` | 任意有效 Bearer Token | 无 |
|
||||
| `POST /api/v1/publish` | Bearer Token + `skill:publish` | 普通用户要求目标 namespace 成员;`SUPER_ADMIN` 可绕过 |
|
||||
| 接口 | 凭证规则 | 授权与错误语义 |
|
||||
|------|---------|---------------|
|
||||
| `GET /api/cli/v1/auth/whoami` | 必须提供有效 Bearer Token | 缺失、未知、过期、撤销或 malformed token 返回 401 |
|
||||
| `GET /api/cli/v1/skills/search` | 可匿名;提供 Bearer 时必须有效 | 匿名仅返回公开可安装 skill;坏凭证返回 401,不得降级匿名 |
|
||||
| `GET /api/cli/v1/skills/{namespace}/{slug}/resolve` | 可匿名读取公开资源;提供 Bearer 时必须有效 | 坏凭证返回 401;有效身份无资源权限返回 403 |
|
||||
| `GET /api/cli/v1/skills/{namespace}/{slug}/download` | 可匿名下载公开资源;提供 Bearer 时必须有效 | 坏凭证返回 401;有效身份无资源权限返回 403 |
|
||||
| `GET /api/cli/v1/skills/{namespace}/{slug}/versions/{version}/download` | 可匿名下载公开资源;提供 Bearer 时必须有效 | 坏凭证返回 401;有效身份无资源权限返回 403 |
|
||||
|
||||
公共读接口仅在完全缺少 `Authorization` 头时允许匿名访问。请求一旦携带 Bearer 凭证,空值、格式错误、未知、过期、已撤销、用户缺失或用户禁用均由共享认证过滤器返回 401。身份已验证但 token scope 或资源可见性不足时返回 403;服务端不向客户端区分 token 不存在、过期或已撤销。
|
||||
|
||||
### 10.4 Admin API
|
||||
|
||||
|
|
|
|||
288
docs/api/authentication.openapi.yaml
Normal file
288
docs/api/authentication.openapi.yaml
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
openapi: 3.0.3
|
||||
info:
|
||||
title: SkillHub CLI Authentication API
|
||||
version: 1.0.0
|
||||
description: >-
|
||||
Authentication contract for CLI identity and public skill reads. Public read
|
||||
operations permit a request with no Authorization header, but any supplied
|
||||
Bearer credential must be valid; malformed, unknown, expired, or revoked
|
||||
credentials return HTTP 401 and never fall back to anonymous access.
|
||||
servers:
|
||||
- url: /
|
||||
tags:
|
||||
- name: CLI Authentication
|
||||
- name: CLI Skills
|
||||
paths:
|
||||
/api/cli/v1/auth/whoami:
|
||||
get:
|
||||
tags: [CLI Authentication]
|
||||
summary: Return the current CLI identity
|
||||
operationId: cliWhoAmI
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: Authenticated CLI identity
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CliWhoAmIEnvelope'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
/api/cli/v1/skills/search:
|
||||
get:
|
||||
tags: [CLI Skills]
|
||||
summary: Search CLI-installable skills
|
||||
operationId: cliSearchSkills
|
||||
description: No Authorization header uses anonymous public visibility. A supplied invalid Bearer credential returns 401.
|
||||
security:
|
||||
- {}
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- name: q
|
||||
in: query
|
||||
required: false
|
||||
schema: {type: string}
|
||||
example: pdf
|
||||
description: Optional search text.
|
||||
- name: limit
|
||||
in: query
|
||||
required: false
|
||||
schema: {type: integer, format: int32, default: 20}
|
||||
example: 20
|
||||
description: Maximum number of results.
|
||||
responses:
|
||||
'200':
|
||||
description: Search result
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CliSearchEnvelope'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
/api/cli/v1/skills/{namespace}/{slug}/resolve:
|
||||
get:
|
||||
tags: [CLI Skills]
|
||||
summary: Resolve a skill version
|
||||
operationId: cliResolveSkill
|
||||
security:
|
||||
- {}
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/Namespace'
|
||||
- $ref: '#/components/parameters/Slug'
|
||||
- name: version
|
||||
in: query
|
||||
required: false
|
||||
schema: {type: string}
|
||||
example: 1.0.0
|
||||
description: Optional exact version; omitted resolves latest.
|
||||
responses:
|
||||
'200':
|
||||
description: Resolved version
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CliResolveEnvelope'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
/api/cli/v1/skills/{namespace}/{slug}/download:
|
||||
get:
|
||||
tags: [CLI Skills]
|
||||
summary: Download the latest installable skill version
|
||||
operationId: cliDownloadLatestSkill
|
||||
security:
|
||||
- {}
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/Namespace'
|
||||
- $ref: '#/components/parameters/Slug'
|
||||
responses:
|
||||
'200':
|
||||
$ref: '#/components/responses/Download'
|
||||
'302':
|
||||
$ref: '#/components/responses/DownloadRedirect'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'503':
|
||||
$ref: '#/components/responses/StorageUnavailable'
|
||||
/api/cli/v1/skills/{namespace}/{slug}/versions/{version}/download:
|
||||
get:
|
||||
tags: [CLI Skills]
|
||||
summary: Download an exact installable skill version
|
||||
operationId: cliDownloadSkillVersion
|
||||
security:
|
||||
- {}
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/Namespace'
|
||||
- $ref: '#/components/parameters/Slug'
|
||||
- $ref: '#/components/parameters/Version'
|
||||
responses:
|
||||
'200':
|
||||
$ref: '#/components/responses/Download'
|
||||
'302':
|
||||
$ref: '#/components/responses/DownloadRedirect'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
'403':
|
||||
$ref: '#/components/responses/Forbidden'
|
||||
'503':
|
||||
$ref: '#/components/responses/StorageUnavailable'
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: SkillHub API token
|
||||
description: API token issued by SkillHub. Invalid lifecycle states all return the same 401 response.
|
||||
parameters:
|
||||
Namespace:
|
||||
name: namespace
|
||||
in: path
|
||||
required: true
|
||||
schema: {type: string}
|
||||
example: global
|
||||
description: Namespace slug.
|
||||
Slug:
|
||||
name: slug
|
||||
in: path
|
||||
required: true
|
||||
schema: {type: string}
|
||||
example: pdf-parser
|
||||
description: Skill slug.
|
||||
Version:
|
||||
name: version
|
||||
in: path
|
||||
required: true
|
||||
schema: {type: string}
|
||||
example: 1.0.0
|
||||
description: Exact semantic version.
|
||||
responses:
|
||||
Download:
|
||||
description: ZIP package stream
|
||||
headers:
|
||||
Content-Disposition:
|
||||
schema: {type: string}
|
||||
description: Attachment filename.
|
||||
content:
|
||||
application/zip:
|
||||
schema: {type: string, format: binary}
|
||||
DownloadRedirect:
|
||||
description: Redirect to a presigned object-storage URL
|
||||
headers:
|
||||
Location:
|
||||
schema: {type: string, format: uri}
|
||||
BadRequest:
|
||||
description: Namespace, skill, or version cannot be resolved.
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/ErrorEnvelope'}
|
||||
Unauthorized:
|
||||
description: Bearer credential is missing where required, malformed, unknown, expired, revoked, or belongs to an unavailable user.
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/ErrorEnvelope'}
|
||||
example:
|
||||
code: 401
|
||||
msg: Authentication required
|
||||
data: null
|
||||
timestamp: '2026-07-28T00:00:00Z'
|
||||
requestId: req-123
|
||||
Forbidden:
|
||||
description: Credential is valid but token scope or resource permission is insufficient.
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/ErrorEnvelope'}
|
||||
example:
|
||||
code: 403
|
||||
msg: Forbidden
|
||||
data: null
|
||||
timestamp: '2026-07-28T00:00:00Z'
|
||||
requestId: req-123
|
||||
StorageUnavailable:
|
||||
description: Object storage is unavailable.
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/ErrorEnvelope'}
|
||||
schemas:
|
||||
Envelope:
|
||||
type: object
|
||||
required: [code, msg, timestamp]
|
||||
properties:
|
||||
code: {type: integer, format: int32}
|
||||
msg: {type: string}
|
||||
data: {type: object, nullable: true}
|
||||
timestamp: {type: string, format: date-time}
|
||||
requestId: {type: string, nullable: true}
|
||||
ErrorEnvelope:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Envelope'
|
||||
- type: object
|
||||
properties:
|
||||
data: {type: object, nullable: true, example: null}
|
||||
CliWhoAmIEnvelope:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Envelope'
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/CliWhoAmI'
|
||||
CliWhoAmI:
|
||||
type: object
|
||||
required: [handle, displayName, email]
|
||||
properties:
|
||||
handle: {type: string, example: user-123}
|
||||
displayName: {type: string, example: CLI User}
|
||||
email: {type: string, format: email, example: cli@example.com}
|
||||
CliSearchEnvelope:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Envelope'
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/CliSearchResult'
|
||||
CliSearchResult:
|
||||
type: object
|
||||
required: [items, total, limit]
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items: {$ref: '#/components/schemas/CliSearchItem'}
|
||||
total: {type: integer, format: int64, example: 1}
|
||||
limit: {type: integer, format: int32, example: 20}
|
||||
CliSearchItem:
|
||||
type: object
|
||||
required: [namespace, slug, latestVersion]
|
||||
properties:
|
||||
namespace: {type: string, example: global}
|
||||
slug: {type: string, example: pdf-parser}
|
||||
latestVersion: {type: string, example: 1.2.0}
|
||||
summary: {type: string, nullable: true, example: Parse PDF files}
|
||||
CliResolveEnvelope:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Envelope'
|
||||
- type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/CliResolveResult'
|
||||
CliResolveResult:
|
||||
type: object
|
||||
required: [namespace, slug, version, versionId, fingerprint, downloadUrl]
|
||||
properties:
|
||||
namespace: {type: string, example: global}
|
||||
slug: {type: string, example: pdf-parser}
|
||||
version: {type: string, example: 1.2.0}
|
||||
versionId: {type: integer, format: int64, example: 42}
|
||||
fingerprint: {type: string, example: 'sha256:abc123'}
|
||||
downloadUrl: {type: string, example: /api/v1/skills/global/pdf-parser/versions/1.2.0/download}
|
||||
Loading…
Add table
Reference in a new issue