604 lines
15 KiB
YAML
604 lines
15 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: Member Center API
|
|
version: 0.1.0
|
|
description: OAuth2/OIDC + Newsletter Subscription API
|
|
servers:
|
|
- url: /api
|
|
security:
|
|
- BearerAuth: []
|
|
|
|
paths:
|
|
/oauth/authorize:
|
|
get:
|
|
summary: OAuth2 Authorization Endpoint
|
|
description: Authorization Code + PKCE flow
|
|
parameters:
|
|
- in: query
|
|
name: client_id
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: redirect_uri
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: response_type
|
|
required: true
|
|
schema: { type: string, enum: [code] }
|
|
- in: query
|
|
name: scope
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: code_challenge
|
|
required: true
|
|
schema: { type: string }
|
|
- in: query
|
|
name: code_challenge_method
|
|
required: true
|
|
schema: { type: string, enum: [S256] }
|
|
- in: query
|
|
name: state
|
|
required: false
|
|
schema: { type: string }
|
|
responses:
|
|
'302':
|
|
description: Redirect to client with code
|
|
|
|
/oauth/token:
|
|
post:
|
|
summary: OAuth2 Token Endpoint
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/x-www-form-urlencoded:
|
|
schema:
|
|
type: object
|
|
required: [grant_type, code, redirect_uri, client_id, code_verifier]
|
|
properties:
|
|
grant_type: { type: string, enum: [authorization_code, refresh_token] }
|
|
code: { type: string }
|
|
redirect_uri: { type: string }
|
|
client_id: { type: string }
|
|
code_verifier: { type: string }
|
|
refresh_token: { type: string }
|
|
responses:
|
|
'200':
|
|
description: Token response
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/TokenResponse'
|
|
|
|
/.well-known/openid-configuration:
|
|
get:
|
|
summary: OIDC Discovery
|
|
responses:
|
|
'200':
|
|
description: OIDC discovery document
|
|
|
|
/.well-known/jwks.json:
|
|
get:
|
|
summary: JWKS
|
|
responses:
|
|
'200':
|
|
description: JSON Web Key Set
|
|
|
|
/auth/register:
|
|
post:
|
|
summary: Register user
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/RegisterRequest'
|
|
responses:
|
|
'200':
|
|
description: Registered
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/UserProfile'
|
|
|
|
/auth/login:
|
|
post:
|
|
summary: API login
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/LoginRequest'
|
|
responses:
|
|
'200':
|
|
description: Token response
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/TokenResponse'
|
|
|
|
/auth/refresh:
|
|
post:
|
|
summary: Refresh token
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/RefreshRequest'
|
|
responses:
|
|
'200':
|
|
description: Token response
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/TokenResponse'
|
|
|
|
/auth/logout:
|
|
post:
|
|
summary: Logout (revoke refresh token)
|
|
security:
|
|
- BearerAuth: []
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
refresh_token:
|
|
type: string
|
|
responses:
|
|
'204':
|
|
description: Logged out
|
|
|
|
/auth/password/forgot:
|
|
post:
|
|
summary: Request password reset
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ForgotPasswordRequest'
|
|
responses:
|
|
'204':
|
|
description: Email sent
|
|
|
|
/auth/password/reset:
|
|
post:
|
|
summary: Reset password
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ResetPasswordRequest'
|
|
responses:
|
|
'204':
|
|
description: Password reset
|
|
|
|
/auth/email/verify:
|
|
get:
|
|
summary: Verify email
|
|
parameters:
|
|
- in: query
|
|
name: token
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'200':
|
|
description: Email verified
|
|
|
|
/user/profile:
|
|
get:
|
|
summary: Get current user profile
|
|
security:
|
|
- BearerAuth: []
|
|
responses:
|
|
'200':
|
|
description: Profile
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/UserProfile'
|
|
|
|
/newsletter/subscribe:
|
|
post:
|
|
summary: Subscribe (unauthenticated allowed)
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/SubscribeRequest'
|
|
responses:
|
|
'200':
|
|
description: Pending subscription
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Subscription'
|
|
|
|
/newsletter/confirm:
|
|
get:
|
|
summary: Confirm subscription (double opt-in)
|
|
parameters:
|
|
- in: query
|
|
name: token
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'200':
|
|
description: Active subscription
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Subscription'
|
|
|
|
/newsletter/unsubscribe:
|
|
post:
|
|
summary: Unsubscribe single list
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [token]
|
|
properties:
|
|
token: { type: string }
|
|
responses:
|
|
'200':
|
|
description: Unsubscribed
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Subscription'
|
|
|
|
/newsletter/preferences:
|
|
get:
|
|
summary: Get preferences
|
|
parameters:
|
|
- in: query
|
|
name: subscription_id
|
|
required: false
|
|
schema: { type: string }
|
|
- in: query
|
|
name: tenant_id
|
|
required: false
|
|
schema: { type: string }
|
|
- in: query
|
|
name: email
|
|
required: false
|
|
schema: { type: string, format: email }
|
|
responses:
|
|
'200':
|
|
description: Preferences
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Subscription'
|
|
|
|
post:
|
|
summary: Update preferences
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [subscription_id, preferences]
|
|
properties:
|
|
subscription_id: { type: string }
|
|
preferences: { type: object }
|
|
responses:
|
|
'200':
|
|
description: Updated
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Subscription'
|
|
|
|
/admin/tenants:
|
|
get:
|
|
summary: List tenants
|
|
security: [{ BearerAuth: [] }]
|
|
responses:
|
|
'200':
|
|
description: List
|
|
post:
|
|
summary: Create tenant
|
|
security: [{ BearerAuth: [] }]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Tenant'
|
|
responses:
|
|
'201':
|
|
description: Created
|
|
|
|
/admin/tenants/{id}:
|
|
get:
|
|
summary: Get tenant
|
|
security: [{ BearerAuth: [] }]
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'200':
|
|
description: Tenant
|
|
put:
|
|
summary: Update tenant
|
|
security: [{ BearerAuth: [] }]
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema: { type: string }
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Tenant'
|
|
responses:
|
|
'200':
|
|
description: Updated
|
|
delete:
|
|
summary: Delete tenant
|
|
security: [{ BearerAuth: [] }]
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'204':
|
|
description: Deleted
|
|
|
|
/admin/newsletter-lists:
|
|
get:
|
|
summary: List newsletter lists
|
|
security: [{ BearerAuth: [] }]
|
|
responses:
|
|
'200':
|
|
description: List
|
|
post:
|
|
summary: Create list
|
|
security: [{ BearerAuth: [] }]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/NewsletterList'
|
|
responses:
|
|
'201':
|
|
description: Created
|
|
|
|
/admin/newsletter-lists/{id}:
|
|
get:
|
|
summary: Get list
|
|
security: [{ BearerAuth: [] }]
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'200':
|
|
description: List
|
|
put:
|
|
summary: Update list
|
|
security: [{ BearerAuth: [] }]
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema: { type: string }
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/NewsletterList'
|
|
responses:
|
|
'200':
|
|
description: Updated
|
|
delete:
|
|
summary: Delete list
|
|
security: [{ BearerAuth: [] }]
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'204':
|
|
description: Deleted
|
|
|
|
/admin/oauth-clients:
|
|
get:
|
|
summary: List OAuth clients
|
|
security: [{ BearerAuth: [] }]
|
|
responses:
|
|
'200':
|
|
description: List
|
|
post:
|
|
summary: Create OAuth client
|
|
security: [{ BearerAuth: [] }]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/OAuthClient'
|
|
responses:
|
|
'201':
|
|
description: Created
|
|
|
|
/admin/oauth-clients/{id}:
|
|
get:
|
|
summary: Get OAuth client
|
|
security: [{ BearerAuth: [] }]
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'200':
|
|
description: OAuth client
|
|
put:
|
|
summary: Update OAuth client
|
|
security: [{ BearerAuth: [] }]
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema: { type: string }
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/OAuthClient'
|
|
responses:
|
|
'200':
|
|
description: Updated
|
|
delete:
|
|
summary: Delete OAuth client
|
|
security: [{ BearerAuth: [] }]
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
'204':
|
|
description: Deleted
|
|
|
|
components:
|
|
securitySchemes:
|
|
OAuth2:
|
|
type: oauth2
|
|
flows:
|
|
authorizationCode:
|
|
authorizationUrl: /api/oauth/authorize
|
|
tokenUrl: /api/oauth/token
|
|
scopes:
|
|
openid: OpenID Connect
|
|
email: Email
|
|
profile: Basic profile
|
|
BearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
bearerFormat: JWT
|
|
|
|
schemas:
|
|
TokenResponse:
|
|
type: object
|
|
properties:
|
|
access_token: { type: string }
|
|
refresh_token: { type: string }
|
|
id_token: { type: string }
|
|
token_type: { type: string, example: Bearer }
|
|
expires_in: { type: integer }
|
|
|
|
RegisterRequest:
|
|
type: object
|
|
required: [email, password]
|
|
properties:
|
|
email: { type: string, format: email }
|
|
password: { type: string }
|
|
|
|
LoginRequest:
|
|
type: object
|
|
required: [email, password, client_id]
|
|
properties:
|
|
email: { type: string, format: email }
|
|
password: { type: string }
|
|
client_id: { type: string }
|
|
scope: { type: string }
|
|
|
|
RefreshRequest:
|
|
type: object
|
|
required: [refresh_token, client_id]
|
|
properties:
|
|
refresh_token: { type: string }
|
|
client_id: { type: string }
|
|
|
|
ForgotPasswordRequest:
|
|
type: object
|
|
required: [email]
|
|
properties:
|
|
email: { type: string, format: email }
|
|
|
|
ResetPasswordRequest:
|
|
type: object
|
|
required: [token, new_password]
|
|
properties:
|
|
token: { type: string }
|
|
new_password: { type: string }
|
|
|
|
UserProfile:
|
|
type: object
|
|
properties:
|
|
id: { type: string }
|
|
email: { type: string, format: email }
|
|
email_verified: { type: boolean }
|
|
created_at: { type: string, format: date-time }
|
|
|
|
SubscribeRequest:
|
|
type: object
|
|
required: [tenant_id, list_id, email]
|
|
properties:
|
|
tenant_id: { type: string }
|
|
list_id: { type: string }
|
|
email: { type: string, format: email }
|
|
preferences: { type: object }
|
|
source: { type: string }
|
|
|
|
Subscription:
|
|
type: object
|
|
properties:
|
|
id: { type: string }
|
|
tenant_id: { type: string }
|
|
list_id: { type: string }
|
|
email: { type: string, format: email }
|
|
status: { type: string, enum: [pending, active, unsubscribed] }
|
|
preferences: { type: object }
|
|
created_at: { type: string, format: date-time }
|
|
|
|
Tenant:
|
|
type: object
|
|
properties:
|
|
id: { type: string }
|
|
name: { type: string }
|
|
domains: { type: array, items: { type: string } }
|
|
status: { type: string }
|
|
|
|
NewsletterList:
|
|
type: object
|
|
properties:
|
|
id: { type: string }
|
|
tenant_id: { type: string }
|
|
name: { type: string }
|
|
status: { type: string }
|
|
|
|
OAuthClient:
|
|
type: object
|
|
properties:
|
|
id: { type: string }
|
|
tenant_id: { type: string }
|
|
name: { type: string }
|
|
redirect_uris: { type: array, items: { type: string } }
|
|
client_type: { type: string, enum: [public, confidential] }
|