template-monorepo/generate/api/auth.api

621 lines
22 KiB
Plaintext
Raw Normal View History

syntax = "v1"
type (
RegisterReq {
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug小寫英數
InviteCode string `json:"invite_code" validate:"required"` // 邀請碼
Email string `json:"email" validate:"required,email"` // 電子郵件
Password string `json:"password" validate:"required,min=8,max=128"` // 密碼8-128 字元)
DisplayName string `json:"display_name,optional"` // 顯示名稱(可選)
Language string `json:"language,optional"` // 語系代碼,如 zh-TW / en可選
AcceptTermsVersion string `json:"accept_terms_version" validate:"required"` // 使用者接受的服務條款版本
MarketingOptIn bool `json:"marketing_opt_in,optional"` // 是否同意接收行銷訊息
}
RegisterData {
ChallengeID string `json:"challenge_id"`
ExpiresIn int `json:"expires_in"`
UID string `json:"uid"`
}
RegisterConfirmReq {
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
ChallengeID string `json:"challenge_id" validate:"required"` // 註冊流程的 OTP challenge ID由 /register 回傳)
Code string `json:"code" validate:"required,len=6"` // 6 位數 OTP 驗證碼
}
RegisterResendReq {
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
ChallengeID string `json:"challenge_id" validate:"required"` // 註冊流程的 OTP challenge ID
}
RegisterResumeReq {
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
Email string `json:"email" validate:"required,email"` // 註冊 Email
}
PasswordForgotReq {
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
Email string `json:"email" validate:"required,email"` // 登入 Email
}
PasswordForgotData {
ChallengeID string `json:"challenge_id"`
ExpiresIn int `json:"expires_in"`
}
PasswordResetReq {
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
ChallengeID string `json:"challenge_id" validate:"required"` // 忘記密碼 OTP challenge ID
Code string `json:"code" validate:"required,len=6"` // 6 位數 OTP
NewPassword string `json:"new_password" validate:"required,min=8,max=128"` // 新密碼
}
PasswordResetData {
OK bool `json:"ok"`
}
AuthTokenData {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int64 `json:"expires_in"`
UID string `json:"uid"`
TokenType string `json:"token_type"`
}
RegisterSocialStartReq {
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
InviteCode string `json:"invite_code" validate:"required"` // 邀請碼
2026-05-27 09:28:13 +00:00
Provider string `json:"provider,options=google|ldap" validate:"required,oneof=google ldap"` // 第三方登入提供者;可選值: google / ldap
AcceptTermsVersion string `json:"accept_terms_version" validate:"required"` // 使用者接受的服務條款版本
Language string `json:"language,optional"` // 語系代碼,如 zh-TW / en可選
RedirectURI string `json:"redirect_uri" validate:"required,url"` // OAuth 完成後要 redirect 回的 URI
MarketingOptIn bool `json:"marketing_opt_in,optional"` // 是否同意接收行銷訊息
}
RegisterSocialStartData {
OauthURL string `json:"oauth_url"`
SessionID string `json:"session_id"`
ExpiresIn int `json:"expires_in"`
}
RegisterSocialCallbackReq {
Code string `form:"code" validate:"required"` // IdP 回傳的 OAuth authorization code
State string `form:"state" validate:"required"` // IdP 回傳的 OAuth state對應 session
}
LoginReq {
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
Email string `json:"email" validate:"required,email"` // 電子郵件
Password string `json:"password" validate:"required,min=8,max=128"` // 密碼8-128 字元)
}
LoginData {
AccessToken string `json:"access_token,optional"`
RefreshToken string `json:"refresh_token,optional"`
ExpiresIn int64 `json:"expires_in,optional"`
UID string `json:"uid,optional"`
TokenType string `json:"token_type,optional"`
MFARequired bool `json:"mfa_required,optional"`
MFAChallengeID string `json:"mfa_challenge_id,optional"`
MFAExpiresIn int `json:"mfa_expires_in,optional"`
}
LoginMFAConfirmReq {
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
ChallengeID string `json:"challenge_id" validate:"required"` // 密碼登入後回傳的 MFA challenge ID
2026-05-27 09:28:13 +00:00
Code string `json:"code" validate:"required,min=6,max=32"` // TOTP 6 碼或備援碼
}
TokenRefreshReq {
RefreshToken string `json:"refresh_token" validate:"required"` // 先前核發的 refresh token
}
TokenExchangeReq {
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
IDToken string `json:"id_token" validate:"required"` // ZITADEL 發行的 id_token
}
LoginSocialStartReq {
TenantSlug string `json:"tenant_slug" validate:"required"` // 租戶 slug
2026-05-27 09:28:13 +00:00
Provider string `json:"provider,options=google|ldap" validate:"required,oneof=google ldap"` // 第三方登入提供者;可選值: google / ldap
RedirectURI string `json:"redirect_uri" validate:"required,url"` // OAuth 完成後要 redirect 回的 URI
}
LoginSocialStartData {
OauthURL string `json:"oauth_url"`
SessionID string `json:"session_id"`
ExpiresIn int `json:"expires_in"`
}
LoginSocialCallbackReq {
Code string `form:"code" validate:"required"` // IdP 回傳的 OAuth authorization code
State string `form:"state" validate:"required"` // IdP 回傳的 OAuth state對應 session
}
LogoutData {
OK bool `json:"ok"`
}
// 文件用:成功回應 envelopeHTTP 200, code=102000, message=SUCCESS
RegisterOKStatus {
Code int64 `json:"code"`
Message string `json:"message"`
Data RegisterData `json:"data"`
}
AuthTokenOKStatus {
Code int64 `json:"code"`
Message string `json:"message"`
Data AuthTokenData `json:"data"`
}
LoginOKStatus {
Code int64 `json:"code"`
Message string `json:"message"`
Data LoginData `json:"data"`
}
RegisterSocialStartOKStatus {
Code int64 `json:"code"`
Message string `json:"message"`
Data RegisterSocialStartData `json:"data"`
}
LoginSocialStartOKStatus {
Code int64 `json:"code"`
Message string `json:"message"`
Data LoginSocialStartData `json:"data"`
}
LogoutOKStatus {
Code int64 `json:"code"`
Message string `json:"message"`
Data LogoutData `json:"data"`
}
PasswordForgotOKStatus {
Code int64 `json:"code"`
Message string `json:"message"`
Data PasswordForgotData `json:"data"`
}
PasswordResetOKStatus {
Code int64 `json:"code"`
Message string `json:"message"`
Data PasswordResetData `json:"data"`
}
)
@server(
group: auth
prefix: /api/v1/auth
refactor(middleware): wire AuthJWT + CasbinRBAC via .api middleware directive Stop relying on a global server.Use(CloudEPJWT) that was invisible from the .api source. Protected routes now declare middleware explicitly in each @server block and goctl chains them into routes.go — the .api file is the single source of truth for "who needs Bearer / who needs RBAC". Concretely: - Rewrite middleware to go-zero's standard struct + Handle() pattern. AuthJWT becomes strict: missing/invalid Bearer returns 28501000 (was soft passthrough). CasbinRBAC stays nil-tolerant so dev/test boots without a policy. - Files renamed to goctl's stringx convention (authjwt_middleware.go, casbinrbac_middleware.go) so future `make gen-api` runs see them as already-generated and skip the empty stub. - Move actor context helpers (Actor, WithActor, ActorFromContext) into internal/library/actor so middleware and BOTH logic packages share one context key. Previously each logic package had its own private actorKey struct{}, so an actor injected for member was invisible to permission — the permission RBAC chain would always see "missing actor". member/permission actor.go are now thin type-alias shims. - .api files declare middleware per group: auth.api (public) → no middleware (register/login/token/...) auth.api (logout) → middleware: AuthJWT member.api → middleware: AuthJWT permission.api (catalog,me) → middleware: AuthJWT permission.api (admin ops) → middleware: AuthJWT,CasbinRBAC normal.api (/health) → no middleware - ServiceContext exposes AuthJWT / CasbinRBAC as rest.Middleware; the global server.Use(...) in gateway.go is removed. - Document the pattern in AGENTS.md (cross-agent rules) and generate/api/README.md (detailed examples + filename rules) so any future AI agent or human follows the same convention. make gen-api / gen-doc / lint / build all pass. Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 09:30:50 +00:00
tags: "Auth - 認證(公開)"
summary: "註冊 / 登入 / Token 交換(不需 Bearer"
)
service gateway {
@doc "Email 註冊(建立 ZITADEL + member寄 registration OTP"
/*
@respdoc-200 (RegisterOKStatus) // 成功code=102000
@respdoc-400 (
10101000: (APIErrorStatus) 參數格式錯誤
10104000: (APIErrorStatus) 缺少必填欄位
) // 參數錯誤 / 驗證失敗
@respdoc-403 (
28505000: (APIErrorStatus) tenant 不允許註冊
) // 禁止存取
@respdoc-404 (
29301000: (APIErrorStatus) tenant 不存在Member scope
) // 資源不存在
@respdoc-409 (
28303000: (APIErrorStatus) email 已註冊Auth scope
) // 資源衝突
@respdoc-423 (
28313000: (APIErrorStatus) invite 消耗鎖定中
) // 資源鎖定
@respdoc-429 (
28604000: (APIErrorStatus) OTP 重送冷卻
28310000: (APIErrorStatus) invite 次數用盡
) // 請求過於頻繁
@respdoc-500 (
28201000: (APIErrorStatus) 資料庫錯誤
28601000: (APIErrorStatus) 系統內部錯誤
) // 內部錯誤
@respdoc-501 (
28605000: (APIErrorStatus) 功能未配置
) // 未實作
@respdoc-502 (
28802000: (APIErrorStatus) ZITADEL 第三方錯誤
) // 第三方服務錯誤
*/
@handler register
post /register (RegisterReq) returns (RegisterData)
@doc "確認 registration OTP 並核發 JWT"
/*
@respdoc-200 (AuthTokenOKStatus) // 成功code=102000
@respdoc-400 (
10101000: (APIErrorStatus) 參數格式錯誤
10104000: (APIErrorStatus) 缺少必填欄位
) // 參數錯誤 / 驗證失敗
@respdoc-403 (
28505000: (APIErrorStatus) challenge tenant 或 purpose 不符Auth scope
29505000: (APIErrorStatus) OTP 無效Member scope
) // 禁止存取
@respdoc-404 (
29301000: (APIErrorStatus) tenant / OTP challenge 不存在Member scope
) // 資源不存在
@respdoc-409 (
28309000: (APIErrorStatus) registration challenge 狀態無效Auth scope
29309000: (APIErrorStatus) OTP challenge 鎖定Member scope
) // 資源狀態衝突
@respdoc-500 (
28201000: (APIErrorStatus) 資料庫錯誤
28601000: (APIErrorStatus) 系統內部錯誤
) // 內部錯誤
@respdoc-501 (
28605000: (APIErrorStatus) 功能未配置
) // 未實作
*/
@handler registerConfirm
post /register/confirm (RegisterConfirmReq) returns (AuthTokenData)
@doc "重寄 registration OTP"
/*
@respdoc-200 (RegisterOKStatus) // 成功code=102000
@respdoc-400 (
10101000: (APIErrorStatus) 參數格式錯誤
10104000: (APIErrorStatus) 缺少必填欄位
) // 參數錯誤
@respdoc-403 (
28505000: (APIErrorStatus) challenge tenant 或 purpose 不符
) // 禁止存取
@respdoc-404 (
29301000: (APIErrorStatus) tenant / OTP challenge 不存在Member scope
) // 資源不存在
@respdoc-409 (
28309000: (APIErrorStatus) registration challenge 不完整Auth scope
) // 資源狀態衝突
@respdoc-429 (
28604000: (APIErrorStatus) OTP 重送冷卻
) // 請求過於頻繁
@respdoc-500 (
28201000: (APIErrorStatus) 資料庫錯誤
28601000: (APIErrorStatus) 系統內部錯誤
) // 內部錯誤
@respdoc-501 (
28605000: (APIErrorStatus) 功能未配置
) // 未實作
*/
@handler registerResend
post /register/resend (RegisterResendReq) returns (RegisterData)
@doc "恢復未完成註冊(依 Email 重寄 registration OTP"
/*
@respdoc-200 (RegisterOKStatus) // 成功code=102000
@respdoc-400 (
10101000: (APIErrorStatus) 參數格式錯誤
10104000: (APIErrorStatus) 缺少必填欄位
) // 參數錯誤
@respdoc-404 (
29301000: (APIErrorStatus) tenant / 待驗證 member 不存在Member scope
) // 資源不存在
@respdoc-409 (
28309000: (APIErrorStatus) 帳號已完成驗證Auth scope
) // 資源狀態衝突
@respdoc-429 (
28604000: (APIErrorStatus) OTP 重送冷卻
) // 請求過於頻繁
@respdoc-500 (
28201000: (APIErrorStatus) 資料庫錯誤
28601000: (APIErrorStatus) 系統內部錯誤
) // 內部錯誤
@respdoc-501 (
28605000: (APIErrorStatus) 功能未配置
) // 未實作
*/
@handler registerResume
post /register/resume (RegisterResumeReq) returns (RegisterData)
@doc "忘記密碼:寄送重設 OTP僅 platform_native 平台帳號)"
/*
@respdoc-200 (PasswordForgotOKStatus) // 成功code=102000
@respdoc-400 (
10101000: (APIErrorStatus) 參數格式錯誤
10104000: (APIErrorStatus) 缺少必填欄位
) // 參數錯誤
@respdoc-403 (
28505000: (APIErrorStatus) 外部身份帳號不可重設密碼Auth scope
) // 禁止存取
@respdoc-404 (
29301000: (APIErrorStatus) tenant / member 不存在Member scope
) // 資源不存在
@respdoc-429 (
29604000: (APIErrorStatus) OTP 重送冷卻
) // 請求過於頻繁
@respdoc-501 (
28605000: (APIErrorStatus) 功能未配置
) // 未實作
*/
@handler passwordForgot
post /password/forgot (PasswordForgotReq) returns (PasswordForgotData)
@doc "忘記密碼:驗證 OTP 並重設密碼(僅 platform_native"
/*
@respdoc-200 (PasswordResetOKStatus) // 成功code=102000
@respdoc-400 (
10101000: (APIErrorStatus) 參數格式錯誤
10104000: (APIErrorStatus) 缺少必填欄位
) // 參數錯誤
@respdoc-403 (
28505000: (APIErrorStatus) OTP 無效Auth scope
29505000: (APIErrorStatus) OTP 無效Member scope
) // 禁止存取
@respdoc-404 (
29301000: (APIErrorStatus) tenant / OTP challenge 不存在Member scope
) // 資源不存在
@respdoc-502 (
28802000: (APIErrorStatus) ZITADEL 第三方錯誤
) // 第三方服務錯誤
*/
@handler passwordReset
post /password/reset (PasswordResetReq) returns (PasswordResetData)
@doc "Social 註冊:建立 session 並回傳 OAuth URL"
/*
@respdoc-200 (RegisterSocialStartOKStatus) // 成功code=102000
@respdoc-400 (
10101000: (APIErrorStatus) 參數格式錯誤
10104000: (APIErrorStatus) 缺少必填欄位
28101000: (APIErrorStatus) invite 已過期Auth scope
) // 參數錯誤
@respdoc-403 (
28505000: (APIErrorStatus) tenant 不允許註冊
) // 禁止存取
@respdoc-404 (
29301000: (APIErrorStatus) tenant 不存在Member scope
28301000: (APIErrorStatus) invite 不存在Auth scope
) // 資源不存在
@respdoc-429 (
28310000: (APIErrorStatus) invite 次數用盡
) // 配額不足
@respdoc-500 (
28201000: (APIErrorStatus) 資料庫錯誤
28601000: (APIErrorStatus) 系統內部錯誤
) // 內部錯誤
@respdoc-501 (
28605000: (APIErrorStatus) 功能未配置
) // 未實作
@respdoc-502 (
28802000: (APIErrorStatus) ZITADEL 第三方錯誤
) // 第三方服務錯誤
*/
@handler registerSocialStart
post /register/social/start (RegisterSocialStartReq) returns (RegisterSocialStartData)
@doc "Social 註冊 OAuth callback"
/*
@respdoc-200 (AuthTokenOKStatus) // 成功code=102000
@respdoc-400 (
10101000: (APIErrorStatus) 參數格式錯誤
10104000: (APIErrorStatus) 缺少必填欄位
28101000: (APIErrorStatus) oauth state 無效Auth scope
) // 參數錯誤
@respdoc-403 (
28505000: (APIErrorStatus) social email 未驗證
) // 禁止存取
@respdoc-404 (
28301000: (APIErrorStatus) registration session 不存在Auth scope
29301000: (APIErrorStatus) tenant 不存在Member scope
) // 資源不存在
@respdoc-409 (
28303000: (APIErrorStatus) 帳號已存在(引導 login
) // 資源衝突
@respdoc-423 (
28313000: (APIErrorStatus) invite 消耗鎖定中
) // 資源鎖定
@respdoc-429 (
28310000: (APIErrorStatus) invite 次數用盡
) // 配額不足
@respdoc-500 (
28201000: (APIErrorStatus) 資料庫錯誤
28601000: (APIErrorStatus) 系統內部錯誤
) // 內部錯誤
@respdoc-501 (
28605000: (APIErrorStatus) 功能未配置
) // 未實作
@respdoc-502 (
28802000: (APIErrorStatus) ZITADEL 第三方錯誤
) // 第三方服務錯誤
*/
@handler registerSocialCallback
get /register/social/callback (RegisterSocialCallbackReq) returns (AuthTokenData)
@doc "Email + 密碼登入ZITADEL ROPG → CloudEP JWT若已啟用 TOTP 則回傳 MFA challenge"
/*
@respdoc-200 (LoginOKStatus) // 成功code=102000mfa_required=true 時僅含 challenge
@respdoc-400 (
10101000: (APIErrorStatus) 參數格式錯誤
10104000: (APIErrorStatus) 缺少必填欄位
) // 參數錯誤
@respdoc-401 (
28501000: (APIErrorStatus) 帳密錯誤
) // 未授權
@respdoc-403 (
28505000: (APIErrorStatus) 帳號未驗證 / 停權 / 不允許登入
) // 禁止存取
@respdoc-404 (
29301000: (APIErrorStatus) tenant 不存在Member scope
) // 資源不存在
@respdoc-500 (
28201000: (APIErrorStatus) 資料庫錯誤
28601000: (APIErrorStatus) 系統內部錯誤
) // 內部錯誤
@respdoc-501 (
28605000: (APIErrorStatus) 功能未配置
) // 未實作
@respdoc-502 (
28802000: (APIErrorStatus) ZITADEL 第三方錯誤
) // 第三方服務錯誤
*/
@handler login
post /login (LoginReq) returns (LoginData)
@doc "確認登入 MFATOTP / 備援碼)並核發 JWT"
/*
@respdoc-200 (AuthTokenOKStatus) // 成功code=102000
@respdoc-400 (
10101000: (APIErrorStatus) 參數格式錯誤
10104000: (APIErrorStatus) 缺少必填欄位
) // 參數錯誤
@respdoc-403 (
28505000: (APIErrorStatus) TOTP 無效 / challenge tenant 不符Auth scope
29505000: (APIErrorStatus) OTP 無效Member scope
) // 禁止存取
@respdoc-404 (
29301000: (APIErrorStatus) tenant 不存在Member scope
28301000: (APIErrorStatus) login mfa challenge 不存在Auth scope
) // 資源不存在
@respdoc-500 (
28201000: (APIErrorStatus) 資料庫錯誤
28601000: (APIErrorStatus) 系統內部錯誤
) // 內部錯誤
@respdoc-501 (
28605000: (APIErrorStatus) 功能未配置
) // 未實作
*/
@handler loginMfaConfirm
post /login/mfa (LoginMFAConfirmReq) returns (AuthTokenData)
@doc "以 refresh_token 換發新的 access/refresh token"
/*
@respdoc-200 (AuthTokenOKStatus) // 成功code=102000
@respdoc-400 (
10101000: (APIErrorStatus) 參數格式錯誤
10104000: (APIErrorStatus) 缺少 refresh_token
) // 參數錯誤
@respdoc-401 (
28501000: (APIErrorStatus) refresh token 無效或已撤銷
) // 未授權
@respdoc-500 (
28601000: (APIErrorStatus) 系統內部錯誤
) // 內部錯誤
@respdoc-501 (
28605000: (APIErrorStatus) 功能未配置
) // 未實作
*/
@handler tokenRefresh
post /token/refresh (TokenRefreshReq) returns (AuthTokenData)
@doc "ZITADEL id_token 換 CloudEP JWT企業 SSO"
/*
@respdoc-200 (AuthTokenOKStatus) // 成功code=102000
@respdoc-400 (
10101000: (APIErrorStatus) 參數格式錯誤
10104000: (APIErrorStatus) 缺少必填欄位
) // 參數錯誤
@respdoc-401 (
28501000: (APIErrorStatus) id_token 無效
) // 未授權
@respdoc-403 (
28505000: (APIErrorStatus) 帳號未驗證 / 停權 / 不允許登入
) // 禁止存取
@respdoc-404 (
29301000: (APIErrorStatus) tenant / member 不存在Member scope
) // 資源不存在
@respdoc-500 (
28201000: (APIErrorStatus) 資料庫錯誤
28601000: (APIErrorStatus) 系統內部錯誤
) // 內部錯誤
@respdoc-501 (
28605000: (APIErrorStatus) 功能未配置
) // 未實作
@respdoc-502 (
28802000: (APIErrorStatus) ZITADEL 第三方錯誤
) // 第三方服務錯誤
*/
@handler tokenExchange
post /token/exchange (TokenExchangeReq) returns (AuthTokenData)
@doc "Social 登入:建立 login session 並回傳 OAuth URL不含 invite"
/*
@respdoc-200 (LoginSocialStartOKStatus) // 成功code=102000
@respdoc-400 (
10101000: (APIErrorStatus) 參數格式錯誤
10104000: (APIErrorStatus) 缺少必填欄位
) // 參數錯誤
@respdoc-403 (
28505000: (APIErrorStatus) tenant 不允許登入
) // 禁止存取
@respdoc-404 (
29301000: (APIErrorStatus) tenant 不存在Member scope
) // 資源不存在
@respdoc-500 (
28201000: (APIErrorStatus) 資料庫錯誤
28601000: (APIErrorStatus) 系統內部錯誤
) // 內部錯誤
@respdoc-501 (
28605000: (APIErrorStatus) 功能未配置
) // 未實作
@respdoc-502 (
28802000: (APIErrorStatus) ZITADEL 第三方錯誤
) // 第三方服務錯誤
*/
@handler loginSocialStart
post /login/social/start (LoginSocialStartReq) returns (LoginSocialStartData)
@doc "Social 登入 OAuth callback"
/*
2026-05-27 09:28:13 +00:00
@respdoc-200 (LoginOKStatus) // 成功code=102000若已啟用 TOTP 則僅回 MFA challenge
@respdoc-400 (
10101000: (APIErrorStatus) 參數格式錯誤
10104000: (APIErrorStatus) 缺少必填欄位
28101000: (APIErrorStatus) oauth state 無效Auth scope
) // 參數錯誤
@respdoc-403 (
28505000: (APIErrorStatus) social email 未驗證 / 帳號狀態不允許登入
) // 禁止存取
@respdoc-404 (
28301000: (APIErrorStatus) login session 不存在Auth scope
29301000: (APIErrorStatus) tenant / member 不存在Member scope
) // 資源不存在
@respdoc-500 (
28201000: (APIErrorStatus) 資料庫錯誤
28601000: (APIErrorStatus) 系統內部錯誤
) // 內部錯誤
@respdoc-501 (
28605000: (APIErrorStatus) 功能未配置
) // 未實作
@respdoc-502 (
28802000: (APIErrorStatus) ZITADEL 第三方錯誤
) // 第三方服務錯誤
*/
@handler loginSocialCallback
2026-05-27 09:28:13 +00:00
get /login/social/callback (LoginSocialCallbackReq) returns (LoginData)
refactor(middleware): wire AuthJWT + CasbinRBAC via .api middleware directive Stop relying on a global server.Use(CloudEPJWT) that was invisible from the .api source. Protected routes now declare middleware explicitly in each @server block and goctl chains them into routes.go — the .api file is the single source of truth for "who needs Bearer / who needs RBAC". Concretely: - Rewrite middleware to go-zero's standard struct + Handle() pattern. AuthJWT becomes strict: missing/invalid Bearer returns 28501000 (was soft passthrough). CasbinRBAC stays nil-tolerant so dev/test boots without a policy. - Files renamed to goctl's stringx convention (authjwt_middleware.go, casbinrbac_middleware.go) so future `make gen-api` runs see them as already-generated and skip the empty stub. - Move actor context helpers (Actor, WithActor, ActorFromContext) into internal/library/actor so middleware and BOTH logic packages share one context key. Previously each logic package had its own private actorKey struct{}, so an actor injected for member was invisible to permission — the permission RBAC chain would always see "missing actor". member/permission actor.go are now thin type-alias shims. - .api files declare middleware per group: auth.api (public) → no middleware (register/login/token/...) auth.api (logout) → middleware: AuthJWT member.api → middleware: AuthJWT permission.api (catalog,me) → middleware: AuthJWT permission.api (admin ops) → middleware: AuthJWT,CasbinRBAC normal.api (/health) → no middleware - ServiceContext exposes AuthJWT / CasbinRBAC as rest.Middleware; the global server.Use(...) in gateway.go is removed. - Document the pattern in AGENTS.md (cross-agent rules) and generate/api/README.md (detailed examples + filename rules) so any future AI agent or human follows the same convention. make gen-api / gen-doc / lint / build all pass. Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 09:30:50 +00:00
}
refactor(middleware): wire AuthJWT + CasbinRBAC via .api middleware directive Stop relying on a global server.Use(CloudEPJWT) that was invisible from the .api source. Protected routes now declare middleware explicitly in each @server block and goctl chains them into routes.go — the .api file is the single source of truth for "who needs Bearer / who needs RBAC". Concretely: - Rewrite middleware to go-zero's standard struct + Handle() pattern. AuthJWT becomes strict: missing/invalid Bearer returns 28501000 (was soft passthrough). CasbinRBAC stays nil-tolerant so dev/test boots without a policy. - Files renamed to goctl's stringx convention (authjwt_middleware.go, casbinrbac_middleware.go) so future `make gen-api` runs see them as already-generated and skip the empty stub. - Move actor context helpers (Actor, WithActor, ActorFromContext) into internal/library/actor so middleware and BOTH logic packages share one context key. Previously each logic package had its own private actorKey struct{}, so an actor injected for member was invisible to permission — the permission RBAC chain would always see "missing actor". member/permission actor.go are now thin type-alias shims. - .api files declare middleware per group: auth.api (public) → no middleware (register/login/token/...) auth.api (logout) → middleware: AuthJWT member.api → middleware: AuthJWT permission.api (catalog,me) → middleware: AuthJWT permission.api (admin ops) → middleware: AuthJWT,CasbinRBAC normal.api (/health) → no middleware - ServiceContext exposes AuthJWT / CasbinRBAC as rest.Middleware; the global server.Use(...) in gateway.go is removed. - Document the pattern in AGENTS.md (cross-agent rules) and generate/api/README.md (detailed examples + filename rules) so any future AI agent or human follows the same convention. make gen-api / gen-doc / lint / build all pass. Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-21 09:30:50 +00:00
@server(
group: auth
prefix: /api/v1/auth
middleware: AuthJWT
tags: "Auth - 認證(需 Bearer"
summary: "登出(需 access token"
)
service gateway {
@doc "登出(撤銷 access JWT 及配對 refresh JWT"
/*
@respdoc-200 (LogoutOKStatus) // 成功code=102000
@respdoc-401 (
28501000: (APIErrorStatus) 缺少或無效 access token
) // 未授權
@respdoc-500 (
28601000: (APIErrorStatus) 系統內部錯誤
) // 內部錯誤
@respdoc-501 (
28605000: (APIErrorStatus) 功能未配置
) // 未實作
*/
@handler logout
post /logout returns (LogoutData)
}