一键导入
bedrud-api-auth
Auth endpoints — JWT flow, local/OAuth/passkey/verify/password-reset/avatar/preferences.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Auth endpoints — JWT flow, local/OAuth/passkey/verify/password-reset/avatar/preferences.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Admin endpoints — users, rooms, queue, settings, invite tokens, webhooks, overview, recordings.
Room endpoints — CRUD, join, moderation, presence, chat upload, stage stubs, recording.
Complete Bedrud API endpoint reference. Dense index of every route, middleware, status code; deep detail in leaf skills.
All DTO definitions, source file index, Swagger reference.
Auth service + middleware — JWT, passkeys, OAuth, session store, rate limiting, recordings gate.
Data layer — GORM models, repository, database init, test utilities, admin DTOs.
| name | bedrud-api-auth |
| description | Auth endpoints — JWT flow, local/OAuth/passkey/verify/password-reset/avatar/preferences. |
| license | Apache License |
Source of truth: server/internal/server/server.go (routes), handlers/auth_handler.go, handlers/auth.go, handlers/preferences_handler.go, middleware/auth.go, middleware/ratelimit.go, auth/auth.go, auth/jwt.go.
Access + refresh token pair. HMAC-SHA256 (auth.jwtSecret).
| Token | Duration | Cookie | JSON field (login body) |
|---|---|---|---|
| Access | tokenDuration hours (config/settings) | access_token HttpOnly | tokens.accessToken |
| Refresh | 7 days | refresh_token HttpOnly | tokens.refreshToken |
Path=/, HttpOnly, Secure when TLS or behindProxy, SameSite=Lax (or None when secure), optional Domain from config.POST /auth/refresh atomically replaces stored refresh; old token blocked. Concurrent reuse → 409.refresh_token; login response uses camelCase under tokens.{
"user": { "id", "email", "name", "provider", "avatarUrl", "accesses", "isActive", "emailVerifiedAt", "passwordChangedAt", "createdAt", "updatedAt" },
"tokens": { "accessToken": "...", "refreshToken": "..." }
}
(Password / stored RefreshToken never serialized.)
| Middleware | Behavior | Fail |
|---|---|---|
Protected() | JWT from Authorization: Bearer (or raw header), else cookie access_token. Banned users → 403 | 401 / 403 |
RequireAccess(level) | Hierarchical weight: superadmin(4) > admin(3) > moderator(2) > user(1). After Protected() | 403 |
RequireEmailVerified(cfg, userRepo) | When requireEmailVerification on: DB check EmailVerifiedAt. Guests exempt. After Protected() | 403 |
RequireBearerForMutations() | POST/PUT/DELETE/PATCH need Authorization header (CSRF). Defined but not mounted on current routes | 401 |
RejectGuest() | Block provider == "guest". Defined but not mounted on current auth routes | 403 |
AuthRateLimiter | Default 10 / 60s / IP (authMaxRequests / authWindowSecs; 0 = off) | 429 |
ResendRateLimiter | Default 3 / 60s / IP (authResendMaxRequests / authResendWindowSecs) | 429 |
GuestRateLimiter | Default 5 / 60s / IP — used on room guest-join, not auth guest-login | 429 |
APIRateLimiter | Default 30 / 60s / IP — rooms/uploads, not auth | 429 |
superadmin > admin > moderator > user > guest
Providers: local, passkey, guest, plus OAuth (google, github, twitter when configured).
{"error":"<message>"} (+ optional fields like requiresVerification, email, already_verified).
| Order | Middleware | Purpose |
|---|---|---|
| 1 | recover.New() | Panic recovery |
| 2 | helmet.New() | XSS, nosniff, X-Frame DENY, referrer |
| 3 | cors.New() | Config origins/headers; credentials require explicit origins |
| 4 | Body limit 2MB | Fiber BodyLimit |
API group prefix: /api.
| Method | Path | Auth | Res | Status |
|---|---|---|---|---|
| GET | /api/health | none | {"status":"healthy","time":<unix>} | 200 |
| GET | /api/ready | none | {"status":"ready","time":...} or DB fail | 200 / 503 |
| GET | /health, /ready | none | redirect → /api/... | 307 |
| GET | /api/cert | none | PEM cert download | 200 / 404 |
| GET | /uploads/avatars/* | none | avatar file | 200 / 400 |
| Method | Path | Auth / Limit | Req | Res | Status |
|---|---|---|---|---|---|
| POST | /api/auth/register | AuthRate | {email, password, name, inviteToken?} | LoginResponse or verification gate | 200 / 400 / 403 / 409 / 500 |
| POST | /api/auth/login | AuthRate | {email, password} | LoginResponse | 200 / 400 / 401 / 403 |
| POST | /api/auth/guest-login | AuthRate | {name} | LoginResponse | 200 / 400 / 403 / 500 |
| POST | /api/auth/refresh | AuthRate | {refresh_token} or cookie | {access_token, refresh_token} | 200 / 400 / 401 / 403 / 409 / 500 |
| POST | /api/auth/logout | Protected | {refresh_token?} or cookie | {"message":"Successfully logged out"} | 200 |
| GET | /api/auth/me | Protected + EmailVerified | — | models.User | 200 / 401 / 500 |
| PUT | /api/auth/me | Protected + EmailVerified | {name, email?} | profile obj (+ verification fields) | 200 / 400 |
| POST | /api/auth/me/avatar | Protected + EmailVerified | multipart avatar | profile obj | 200 / 400 / 500 |
| DELETE | /api/auth/me/avatar | Protected + EmailVerified | — | profile obj | 200 / 400 |
| PUT | /api/auth/password | Protected + EmailVerified | {currentPassword, newPassword} | {"message":"Password updated successfully"} | 200 / 400 |
{ "id", "name", "email", "provider", "accesses", "avatarUrl" }
Email change (local/passkey only): sets requiresVerification, optional tokens: {accessToken, refreshToken}, revokes old access JWT, enqueues verification (cooldown-gated).
MinPasswordLength / MaxPasswordLength).registrationEnabled, tokenRegistrationOnly + valid unused invite.requireEmailVerification: no tokens → {requiresVerification, message, email}; else cookies + LoginResponse + welcome email job.{error, requiresVerification, email}."Invalid credentials".registrationEnabled + guestLoginEnabled.guest provider/access.refresh_token cookie. Re-loads user (accesses, active, verified).access_token / refresh_token (snake_case). Sets cookies.password_changed email.| Method | Path | Limit | Req | Res | Status |
|---|---|---|---|---|---|
| POST | /api/auth/forgot-password | AuthRate | {email} | {"message":"If the account exists, a password reset email has been sent"} | 200 / 400 |
| POST | /api/auth/reset-password | AuthRate | {token, newPassword} | success message | 200 / 400 / 500 |
CooldownCache (default TTL 2m / verificationEmailCooldownMins). Email-hash key also consumed.password_reset; binds email + passwordChangedAt; frontend link /auth/reset-password?token=.| Method | Path | Auth / Limit | Req | Res | Status |
|---|---|---|---|---|---|
| POST | /api/auth/verify | none | {token} | {access_token, refresh_token, verified:true} | 200 / 400 / 401 / 404 / 409 / 500 |
| GET | /api/auth/verify/status | Protected | — | {verified, email} | 200 / 401 |
| POST | /api/auth/verify/resend | ResendRate | {email} | {"message":"If the account exists, a verification email has been sent"} | 200 / 400 |
{error, already_verified:true}.setAuthCookies (client uses JSON or re-login).VerificationEventRepository.{frontendURL}/auth/verify?token=... (email only).| Method | Path | Limit | Handler | Res |
|---|---|---|---|---|
| GET | /api/auth/:provider/login | AuthRate | BeginAuthHandler | 307 → provider (or 400 unsupported) |
| GET | /api/auth/:provider/callback | AuthRate | CallbackHandler | redirect or JSON |
Providers: only those returned by auth.ConfiguredProviders() (google, github, twitter when secrets set).
requireEmailVerification and not yet set.frontendURL set → redirect {frontendURL}/auth/callback (token not in query string — cookies only). Else JSON AuthResponse {user, token} (access only in body field token).Deactivated users → 403.
| Method | Path | Auth / Limit | Req | Res | Status |
|---|---|---|---|---|---|
| POST | /api/auth/passkey/register/begin | Protected + EmailVerified | — | {challenge, user:{id,name,displayName}, rp:{id,name}} | 200 / 500 |
| POST | /api/auth/passkey/register/finish | Protected + EmailVerified | {clientDataJSON, attestationObject} base64url | {"message":"Passkey registered successfully"} | 200 / 400 |
| POST | /api/auth/passkey/login/begin | AuthRate | — | {challenge, rpId} | 200 / 500 |
| POST | /api/auth/passkey/login/finish | AuthRate | {credentialId, clientDataJSON, authenticatorData, signature} base64url | LoginResponse | 200 / 400 / 401 / 403 |
| POST | /api/auth/passkey/signup/begin | AuthRate | {email, name, inviteToken?} | creation options | 200 / 400 / 403 / 500 |
| POST | /api/auth/passkey/signup/finish | AuthRate | {clientDataJSON, attestationObject} | LoginResponse or verification gate | 200 / 400 / 500 |
ChallengeStore + gothic session IDs for login/signup.server.domain or request hostname. RP name = same as RP ID (not hard-coded "Bedrud").auth.frontendURL or request host/X-Forwarded-Proto.{requiresVerification, message, email} (empty tokens from service).requiresVerification like password login.| Method | Path | Auth | Req | Res | Status |
|---|---|---|---|---|---|
| GET | /api/auth/preferences | Protected + EmailVerified | — | {"preferencesJson":"..."} (default {}) | 200 / 500 |
| PUT | /api/auth/preferences | Protected + EmailVerified | {preferencesJson} | {"message":"Preferences updated"} | 200 / 400 / 413 / 500 |
{...}). Upsert by user ID.| Method | Path | Auth | Handler | Res |
|---|---|---|---|---|
| GET | /api/auth/settings | none | adminHandler.GetPublicSettings | public fields only |
{
"serverName": "...",
"registrationEnabled": true,
"tokenRegistrationOnly": false,
"guestLoginEnabled": true,
"passkeysEnabled": true,
"oauthProviders": ["google", "github"],
"requireEmailVerification": false,
"chatMaxMessageCount": 10000,
"chatMessageTTLHours": 2160,
"recordingsEnabled": false
}
No secrets.
| Rule | Value |
|---|---|
| Password length | 12–128 |
| Display name (register/profile) | 2–255 |
| Guest name | 2–50 |
| Passkey signup name | 2–100 |
| Avatar | multipart field avatar, max 2 MB, served at /uploads/avatars/* |
| Preferences blob | ≤ 4 KB JSON object |
| Email verification / forgot cooldown | default 2 minutes (auth.verificationEmailCooldownMins) |
| Error body | {"error":"..."} |
| Concern | Path |
|---|---|
| Route wiring | server/internal/server/server.go |
| Local / verify / passkey / avatar / reset | server/internal/handlers/auth_handler.go |
| OAuth begin/callback | server/internal/handlers/auth.go |
| Preferences | server/internal/handlers/preferences_handler.go |
| Public settings | server/internal/handlers/admin_handler.go (GetPublicSettings) |
| Password constants | server/internal/handlers/models.go |
| JWT + revocation | server/internal/auth/jwt.go |
| Auth service / LoginResponse | server/internal/auth/auth.go |
| Middleware | server/internal/middleware/auth.go, ratelimit.go |
| User model | server/internal/models/user.go |
| Avatar storage | server/internal/storage/avatar.go |