API 参考:认证与用户
路由注册:internal/router/routes_auth_tenant.go 的 RegisterAuthRoutes、RegisterMyInvitationRoutes 与 RegisterUserFavoriteRoutes(个人收藏)。Handler:internal/handler/auth.go、internal/handler/auth_register_by_invite.go、internal/handler/tenant_invitation.go、internal/handler/user_resource_favorite.go。
除特别标注外,本组接口在认证中间件之后仅要求“已登录”(无角色下限),且只接受 JWT:API key 能调用的只有 GET /auth/me。免认证接口见各条目。
认证(/api/v1/auth)
POST /api/v1/auth/register
用途:注册新用户。免认证,每 IP 10 次/分钟。Handler: internal/handler/auth.go
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
username | string | 是(binding:"required,min=2,max=50") | 用户名,2-50 字符 |
email | string | 是(binding:"required,email") | 邮箱 |
password | string | 是(binding:"required,min=6") | 密码(≥6 位) |
workspace_name | string | 否(binding:"omitempty,max=128") | 只在部署的第一次注册(/auth/config 的 first_user 为 true)时生效:默认工作区的名称,空则为 Default Workspace。其他时候被忽略 |
注册只创建账号,不创建工作区;新账号通过邀请或由系统管理员加入工作区。唯一例外是部署的第一个账号(bootstrap):同一事务里创建默认工作区、把该用户设为系统管理员并写入 Owner 成员关系。注册未开放(invite_only,或 auto 模式下已有用户)时返回 403;两个人抢第一个账号时输的一方也得到 403。
注册模式按「系统设置 auth.registration_mode > 配置文件 > 默认 auto」解析。auto 只在系统还没有任何用户时开放,首个注册者成为系统管理员。环境变量 DISABLE_REGISTRATION 在启动时折算进配置:true 等于 invite_only(一直关闭),false 等于 self_serve(一直开放),不设则保持 auto。/auth/register 与 /auth/config 用同一套解析,界面与接口的判断总是一致。
响应:201 {"success":true,"message":"Registration successful","user":{User}}
curl -X POST $BASE/api/v1/auth/register -H 'Content-Type: application/json' \
-d '{"username":"alice","email":"a@ex.com","password":"secret123"}'POST /api/v1/auth/register-by-invite
用途:通过邀请/分享链接 token 注册并加入工作区。免认证,IP 限流 30 次/分钟。Handler: internal/handler/auth_register_by_invite.go
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
token | string | 是(binding:"required") | 邀请 token |
email | string | 是(binding:"required,email") | 邮箱 |
username | string | 是(binding:"required") | 用户名 |
password | string | 是(binding:"required,min=6") | 密码(≥6 位) |
响应:201,同 Login(user/active_tenant/memberships/token/refresh_token)。
curl -X POST $BASE/api/v1/auth/register-by-invite -H 'Content-Type: application/json' \
-d '{"token":"<invite_token>","email":"a@ex.com","username":"alice","password":"secret123"}'POST /api/v1/auth/invitations/lookup
用途:匿名查询邀请 token 对应的工作区信息(注册前预览)。免认证,IP 限流。Handler: internal/handler/auth_register_by_invite.go
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
token | string | 是(binding:"required") | 邀请 token |
响应:200 {"success":true,"data":{"tenant_id","tenant_name","role","expires_at"}}
curl -X POST $BASE/api/v1/auth/invitations/lookup -H 'Content-Type: application/json' -d '{"token":"<invite_token>"}'POST /api/v1/auth/login
用途:邮箱密码登录。免认证,每 IP 30 次/分钟,另有按账号的连续失败锁定。Handler: internal/handler/auth.go
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
email | string | 是(binding:"required,email") | 邮箱 |
password | string | 是(binding:"required,min=6") | 密码 |
响应:200 {"success":true,"user":{...},"active_tenant":{...},"memberships":[...],"token":"...","refresh_token":"..."}
邮箱或密码错误返回 401。同一账号 15 分钟内连续失败 5 次后锁定 15 分钟,期间返回 429(code 1006);有 Redis 时失败计数在实例间共享。
curl -X POST $BASE/api/v1/auth/login -H 'Content-Type: application/json' -d '{"email":"a@ex.com","password":"secret123"}'GET /api/v1/auth/config
用途:查询注册模式等认证配置。免认证。Handler: internal/handler/auth.go
响应:200 {"success":true,"registration_mode":"self_serve|invite_only","configured_registration_mode":"auto|self_serve|invite_only","registration_open":true,"first_user":false,"default_workspace_name":"Default Workspace"}。registration_mode 是当前生效的状态(auto 且已有用户时读作 invite_only);first_user 为 true 表示这是全新部署,下一个注册者将成为系统管理员并创建默认工作区,注册页此时显示「工作区名称」字段,default_workspace_name 是它的占位默认值
curl $BASE/api/v1/auth/configPOST /api/v1/auth/switch-tenant
用途:切换当前活跃工作区并换发 token。需登录(无工作区也可调用),每 IP 60 次/分钟。Handler: internal/handler/auth.go
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
tenant_id | uint64 | 是(binding:"required") | 目标工作区 ID |
refresh_token | string | 否 | 用于换发新 token |
响应:200,同 Login。
curl -X POST $BASE/api/v1/auth/switch-tenant -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"tenant_id":2}'GET /api/v1/auth/oidc/config
用途:查询 OIDC 是否启用及显示名。免认证。Handler: internal/handler/auth.go
响应:200 {"success":true,"enabled":bool,"provider_display_name":"..."}
curl $BASE/api/v1/auth/oidc/configGET /api/v1/auth/oidc/url
用途:获取 OIDC 授权跳转 URL。免认证。Handler: internal/handler/auth.go
| 查询参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
redirect_uri | string | 是 | 回调地址 |
响应:200 {"success":true,"provider_display_name":"...","authorization_url":"...","state":"..."}。nonce 不出现在响应里,而是写入 HttpOnly cookie,回调时校验。
curl "$BASE/api/v1/auth/oidc/url?redirect_uri=https://app.example.com/callback"GET /api/v1/auth/oidc/callback
用途:OIDC 授权回调(浏览器重定向进入)。免认证。Handler: internal/handler/auth.go
查询参数:code、state、error、error_description(均由 OIDC 提供方带回)。
响应:总是 302 重定向到前端 /,结果放在 URL hash 里:
- 成功:
#oidc_result=<base64url(JSON)>,JSON 与登录响应同构(success、user、active_tenant、memberships、token、refresh_token,外加is_new_user)。 - 失败:
#oidc_error=<原因>[&oidc_error_description=<说明>],原因为 IdP 返回的error,或invalid_state、missing_code、login_failed、payload_encode_failed。
curl -i "$BASE/api/v1/auth/oidc/callback?code=xxx&state=yyy"GET /api/v1/auth/oidc/start
用途:直接 302 跳转到 OIDC 提供方的授权页,不需要前端 JS 先取 URL。适合企业门户等外部平台放一个链接即可发起登录,借助 IdP 已有的 SSO 会话免再次输入密码。回调地址由请求自身的 scheme 与 host 推出(/api/v1/auth/oidc/callback)。免认证。Handler: internal/handler/auth.go
响应:302 重定向到授权 URL。
curl -i $BASE/api/v1/auth/oidc/startPOST /api/v1/auth/refresh
用途:用 refresh token 换发新 token。免认证。Handler: internal/handler/auth.go
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
refreshToken | string | 是(binding:"required") | refresh token |
响应:200 {"success":true,"message":"Token refreshed successfully","access_token":"...","refresh_token":"..."}。注意请求字段是驼峰 refreshToken,响应字段是下划线。
curl -X POST $BASE/api/v1/auth/refresh -H 'Content-Type: application/json' -d '{"refreshToken":"<rt>"}'GET /api/v1/auth/validate
用途:校验当前 token 是否有效。需登录(无工作区可调用)。Handler: internal/handler/auth.go
响应:200 {"success":true,"message":"Token is valid","user":{UserInfo}}
curl $BASE/api/v1/auth/validate -H "Authorization: Bearer $TOKEN"POST /api/v1/auth/logout
用途:登出(失效当前 token)。需登录。无请求体。Handler: internal/handler/auth.go
响应:200 {"success":true,"message":"Logout successful"}
curl -X POST $BASE/api/v1/auth/logout -H "Authorization: Bearer $TOKEN"GET /api/v1/auth/me
用途:查询当前调用者身份(用户/工作区/成员关系/能力)。需登录;API key 亦可(策略 apiKeyAny(),任何有效 key)。Handler: internal/handler/auth.go
响应:200 {"success":true,"data":{"user":{UserInfo},"tenant":{TenantResponse},"memberships":[...],"tenant_required":bool,"capabilities":{"can_create_tenant":bool,"auto_accept_invitation":bool}}}。user 不带工作区字段——当前工作区是旁边的 tenant,按「X-Tenant-ID → JWT → 偏好 → 最早的成员关系」解析;tenant_required 为 true 表示调用者还没有任何可用空间。can_create_tenant 与 POST /tenants 的守卫同源:系统管理员、生效中的跨工作区超管、平台 key 为 true。
curl $BASE/api/v1/auth/me -H "X-API-Key: $API_KEY"PUT /api/v1/auth/me/preferences
用途:更新个人偏好(当前工作区)。需登录。Handler: internal/handler/auth.go
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
last_active_tenant_id | *uint64 | 否 | 当前工作区 ID,0 清除。这是用户指向工作区的唯一指针(没有「主工作区」);是否可用在下次登录按成员关系校验,不在这里校验 |
响应:200 {"success":true,"data":{UserPreferences}}
curl -X PUT $BASE/api/v1/auth/me/preferences -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"last_active_tenant_id":2}'POST /api/v1/auth/change-password
用途:修改密码。需登录。Handler: internal/handler/auth.go
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
old_password | string | 是(binding:"required") | 旧密码 |
new_password | string | 是(binding:"required") | 新密码:8-32 个字符,至少含一个字母和一个数字,且不能与旧密码相同 |
响应:200 {"success":true,"message":"Password changed successfully"}
成功后该用户的全部会话(token)被吊销,需要用新密码重新登录。违反密码策略或新旧相同返回 1010(details 分别为 password_policy / same_password),旧密码错误返回 1000。
curl -X POST $BASE/api/v1/auth/change-password -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"old_password":"old","new_password":"newpass1"}'我的邀请(/api/v1/me/invitations)
服务层保证“仅被邀请人可接受/拒绝”;无角色下限(无工作区的新用户也可用)。Handler: internal/handler/tenant_invitation.go
GET /api/v1/me/invitations
用途:列出发给我的邀请。
| 查询参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
include_terminal | bool | 否 | true 时包含已完结的邀请 |
响应:200 {"success":true,"data":{"invitations":[TenantInvitationResponse],"total":N}}
curl $BASE/api/v1/me/invitations -H "Authorization: Bearer $TOKEN"GET /api/v1/me/invitations/pending-count
用途:待处理邀请计数(轻量轮询)。
响应:200 {"success":true,"data":{"pending_count":N}}
curl $BASE/api/v1/me/invitations/pending-count -H "Authorization: Bearer $TOKEN"POST /api/v1/me/invitations/:inv_id/accept
用途:接受邀请,写入成员关系。路径参数:inv_id 邀请 ID。无请求体。
响应:200 {"success":true,"data":{"membership":{"tenant_id","role","status","joined_at"}}}
curl -X POST $BASE/api/v1/me/invitations/12/accept -H "Authorization: Bearer $TOKEN"POST /api/v1/me/invitations/:inv_id/decline
用途:拒绝邀请。路径参数:inv_id。无请求体。
响应:200 {"success":true}
curl -X POST $BASE/api/v1/me/invitations/12/decline -H "Authorization: Bearer $TOKEN"POST /api/v1/me/invitations/accept-by-token
用途:已登录用户用共享邀请链接的 token 加入工作区(与 register-by-invite 相对,不创建新账号);对已是成员的用户幂等。调用者之前没有任何工作区时,加入的工作区会被记为其当前工作区(preferences.last_active_tenant_id)。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
token | string | 是(binding:"required") | 邀请链接 token |
响应:200 {"success":true,"data":{"membership":{"tenant_id","role","status","joined_at"},"tenant_name":"..."}};链接无效、过期或已撤销返回 410。
curl -X POST $BASE/api/v1/me/invitations/accept-by-token -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"token":"<invite_token>"}'个人收藏(/api/v1/user/favorites)
收藏是个人导航用的书签,只作用于调用者自己在当前工作区里的收藏,不能查看或修改别人的收藏。Viewer+,仅 JWT(未对 API key 声明)。资源类型:kb(知识库)、doc_space(在线文档空间)、doc_page(在线文档页面)。Handler: internal/handler/user_resource_favorite.go
| 方法与路径 | 说明 |
|---|---|
GET /user/favorites?type=kb | 列出当前工作区里收藏的该类资源,type 必填,非法值返回 400;每项含 resource_type、resource_id、created_at |
POST /user/favorites | 收藏,请求体 {"type":"kb","id":"<kb_id>"} |
DELETE /user/favorites/:type/:id | 取消收藏 |
响应:列表为 {"success":true,"data":[...]},增删为 {"success":true}。
curl -X POST $BASE/api/v1/user/favorites -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"type":"kb","id":"<kb_id>"}'