skillhub/docs/api/authentication.openapi.yaml
dongmucat 5012b31af2 test(auth): cover CLI session fallback (#605)
Signed-off-by: dongmucat <1127093059@qq.com>
2026-07-28 15:22:11 +08:00

305 lines
11 KiB
YAML

openapi: 3.0.3
info:
title: SkillHub CLI Authentication API
version: 1.0.0
description: >-
Authentication contract for CLI identity and public skill reads. A valid
Bearer credential overrides a Web Session identity. Once the Bearer scheme
is used, the credential must be valid: empty, malformed, unknown, expired,
or revoked Bearer credentials return HTTP 401 and never fall back to the
Session or anonymous access. An absent Authorization header or an
unsupported scheme such as Basic preserves a valid Web Session. Without a
Session, public reads use anonymous visibility and whoami returns HTTP 401.
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
description: Requires a valid Bearer credential or Web Session. Bearer takes priority over Session; invalid Bearer returns 401 without Session fallback. An absent or non-Bearer Authorization header preserves Session, but returns 401 when no Session exists.
security:
- bearerAuth: []
- sessionAuth: []
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: Valid Bearer overrides Web Session. Invalid Bearer returns 401 without Session fallback. An absent or non-Bearer Authorization header preserves Session; without Session, the request uses anonymous public visibility.
security:
- {}
- sessionAuth: []
- 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
description: Valid Bearer overrides Web Session. Invalid Bearer returns 401 without Session fallback. An absent or non-Bearer Authorization header preserves Session; without Session, the request uses anonymous public visibility.
security:
- {}
- sessionAuth: []
- 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
description: Valid Bearer overrides Web Session. Invalid Bearer returns 401 without Session fallback. An absent or non-Bearer Authorization header preserves Session; without Session, the request uses anonymous public visibility.
security:
- {}
- sessionAuth: []
- 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
description: Valid Bearer overrides Web Session. Invalid Bearer returns 401 without Session fallback. An absent or non-Bearer Authorization header preserves Session; without Session, the request uses anonymous public visibility.
security:
- {}
- sessionAuth: []
- 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. A valid token overrides Web Session; invalid lifecycle states all return the same 401 response without Session fallback.
sessionAuth:
type: apiKey
in: cookie
name: SESSION
description: Spring Session browser identity. It is preserved when Authorization is absent or uses a non-Bearer scheme, and is overridden by a valid Bearer token.
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: No valid supported identity is present where required, or the Bearer credential is empty, malformed, unknown, expired, revoked, or belongs to an unavailable user. Invalid Bearer never falls back to Web Session.
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, data, timestamp, requestId]
properties:
code: {type: integer, format: int32}
msg: {type: string}
data: {type: object, nullable: true}
timestamp: {type: string, format: date-time}
requestId: {type: string, example: req-123}
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, nullable: true, example: cli@example.com, description: Email address when available; the required field is null when the account has no email.}
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}