feat(security): HttpOnly cookie sessions, token revocation, and RBAC

- Add User.role (enum Role/ADMIN) and User.tokenVersion with migration
- Login now issues short-lived access token (30m default) + 7d refresh
  token, both embedding tokenVersion and a typ discriminator
- Tokens delivered via HttpOnly SameSite cookies (ir_at, ir_rt scoped
  to /auth); refresh token never leaves the cookie
- New endpoints: POST /auth/refresh (rotation), GET /auth/me,
  POST /auth/logout (bumps tokenVersion, revoking all tokens)
- JWT strategy accepts bearer or cookie, rejects refresh tokens, and
  verifies tokenVersion + user existence on every request
- Global RolesGuard: authenticated routes require ADMIN unless widened
  via @Roles(...)
- Admin SPA: session fully cookie-based, no token in localStorage;
  router guard restores session via /auth/me; axios auto-refreshes once
  on 401; stale localStorage keys cleaned up
This commit is contained in:
yeuimu
2026-08-22 12:04:56 +08:00
parent 755b40aded
commit be0b90e68f
16 changed files with 503 additions and 85 deletions
+8 -1
View File
@@ -241,11 +241,18 @@ model GoodTag {
}
// ---------- Users (admin authentication) ----------
enum Role {
ADMIN
}
model User {
id BigInt @id @default(autoincrement())
username String @unique
passwordHash String @map("password_hash")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
role Role @default(ADMIN)
// Bumped on logout / revocation; JWTs carrying an older version are rejected.
tokenVersion Int @default(0) @map("token_version")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6)
@@map("users")