goi-web/apps/server/openapi.yaml

397 lines
11 KiB
YAML

openapi: 3.1.1
info:
title: Demo API Server
description: Demonstrates secure JWT authentication using HTTP-only cookies
version: 'v1'
servers:
- url: https://your-bff.com
description: Production server
paths:
/api/login:
post:
operationId: Login
tags:
- Authentication
summary: User Login
description: |
Authenticate user with username/password.
On success, sets `accessToken` and `refreshToken` in HTTP-only cookies.
security: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
example:
username: alice
password: secret123
responses:
'200':
description: Login successful - JWT tokens set in HTTP-only cookies
headers:
Set-Cookie:
description: |
Two HTTP-only cookies:
- `accessToken`: short-lived JWT (15 min)
- `refreshToken`: long-lived token, only sent to `/api/refresh`
schema:
type: array
items:
type: string
example:
- accessToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.x; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=900
- refreshToken=rt_abc123def456ghi789; Path=/api/refresh; HttpOnly; Secure; SameSite=Strict; Max-Age=604800
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
examples:
success:
$ref: '#/components/examples/LoginResponseSuccess'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
badRequest:
$ref: '#/components/examples/ErrorResponseBadRequest'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
unauthorized:
$ref: '#/components/examples/ErrorResponseUnauthorized'
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
notFound:
$ref: '#/components/examples/ErrorResponseNotFound'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
serverError:
$ref: '#/components/examples/ErrorResponseServerError'
/api/logout:
post:
operationId: Logout
tags:
- Authentication
summary: Logout
description: Clears JWT cookies. Optionally revokes refresh token.
security: [] # Public (anyone can logout)
responses:
'200':
description: Logged out successfully
content:
application/json:
schema:
$ref: '#/components/schemas/LogoutResponse'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
serverError:
$ref: '#/components/examples/ErrorResponseServerError'
/api/refresh:
post:
operationId: Refresh
tags:
- Authentication
summary: Refresh Access Token
description: |
Uses `refreshToken` cookie to issue a new `accessToken`.
Must include valid `refreshToken` cookie (sent automatically if Path matches).
security:
- CSRFCookieAuth: []
- CSRFHeaderAuth: []
- RefreshCookieAuth: []
responses:
'200':
description: New access token issued via cookie
headers:
Set-Cookie:
description: New short-lived access token
schema:
type: string
example: accessToken=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.y; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=900
content:
application/json:
schema:
$ref: '#/components/schemas/RefreshResponse'
examples:
success:
$ref: '#/components/examples/RefreshResponseSuccess'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
badRequest:
$ref: '#/components/examples/ErrorResponseBadRequest'
'401':
description: Unauthorized - invalid or expired refresh token
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
unauthorized:
$ref: '#/components/examples/ErrorResponseUnauthorized'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
serverError:
$ref: '#/components/examples/ErrorResponseServerError'
/api/products:
get:
operationId: GetProducts
security:
- CookieAuth: []
tags:
- Product
summary: Get Product List
description: Retrieve list of products. Requires valid `accessToken` cookie.
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/GetProductsResponse'
examples:
success:
$ref: '#/components/examples/GetProductsResponseSuccess'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
badRequest:
$ref: '#/components/examples/ErrorResponseBadRequest'
'401':
description: Unauthorized - missing or invalid access token
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
unauthorized:
$ref: '#/components/examples/ErrorResponseUnauthorized'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
serverError:
$ref: '#/components/examples/ErrorResponseServerError'
components:
securitySchemes:
CookieAuth:
type: apiKey
in: cookie
name: accessToken
description: |
**HTTP-only cookie** containing the JWT access token.
- Sent automatically on same-origin requests
- Expires in 15 minutes
- Claims: `sub`, `iat`, `exp`, `role`
- Use `/api/refresh` when expired
RefreshCookieAuth:
type: apiKey
in: cookie
name: refreshToken
description: |
**HTTP-only cookie** used only for token refresh.
- Only sent to `/api/refresh`
- Long-lived (7 days)
- Never exposed to JavaScript
CSRFHeaderAuth:
type: apiKey
in: header
name: X-CSRF-Token
CSRFCookieAuth:
type: apiKey
in: cookie
name: csrfToken
schemas:
ErrorResponse:
type: object
properties:
status:
type: string
example: error
created:
type: integer
description: UTC timestamp in milliseconds
example: 1759974176938
error:
type: string
example: INTERNAL_SERVER_ERROR
message:
type: string
example: An unexpected error occurred
required:
- status
- created
- error
- message
LoginRequest:
type: object
properties:
username:
type: string
description: Login username
example: alice
password:
type: string
description: Password
example: secret123
required:
- username
- password
LoginResponse:
type: object
properties:
status:
type: string
example: success
created:
type: integer
example: 1759974176938
required:
- status
- created
LogoutResponse:
type: object
properties:
status:
type: string
example: success
created:
type: integer
example: 1759974176938
required:
- status
- created
RefreshResponse:
type: object
properties:
status:
type: string
example: success
created:
type: integer
example: 1759974176938
required:
- status
- created
GetProductsResponse:
type: object
properties:
status:
type: string
example: success
created:
type: integer
example: 1759974176938
data:
type: array
items:
type: object
properties:
id:
type: string
example: unc0001-01
name:
type: string
example: Maple Table
price:
type: number
example: 25000
required:
- id
- name
- price
required:
- status
- created
- data
examples:
ErrorResponseBadRequest:
value:
status: error
created: 1759974176938
error: INVALID_REQUEST
message: Invalid request payload
ErrorResponseUnauthorized:
value:
status: error
created: 1759974176938
error: UNAUTHORIZED
message: Invalid or missing token
ErrorResponseNotFound:
value:
status: error
created: 1759974176938
error: NOT_FOUND
message: Resource not found
ErrorResponseServerError:
value:
status: error
created: 1759974176938
error: INTERNAL_SERVER_ERROR
message: An unexpected error occurred
LoginResponseSuccess:
value:
status: success
created: 1759974176938
RefreshResponseSuccess:
value:
status: success
created: 1759974176938
GetProductsResponseSuccess:
value:
status: success
created: 1759974176938
data:
- id: unc0001-01
name: Maple Table
price: 25000
- id: unc0002-01
name: Oak Chair
price: 12000
security: []
tags:
- name: Authentication
x-displayName: Authentication
description: Login, refresh, and auth flows
- name: Product
x-displayName: Product
description: Product catalog endpoints