feat: revocable read-only API keys for machine access
Integrations such as the Wabenhain shop sync no longer need a service account with a password (and without MFA). An admin creates a key under Verwaltung > Automatisierung > API-Schlüssel. - Key format ack_<8>_<43>; only its SHA-256 hash is stored, the key is shown exactly once. Optional expiry, "last used" timestamp, revocation takes effect immediately; create/revoke are audit-logged without the key. - A key is not a user: JwtAuthGuard sets request.apiKey, PermissionsGuard admits it only to routes whose permission is among its scopes and never to user-only routes. Scopes come from a short allowlist (shop-sync:read). - Managing keys requires the new permission api-keys:manage. - Keys are excluded from backups on purpose; a restore means issuing a new key. Additive migration (new table). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
ede86c062a
commit
445fb3b94b
21 files changed
+848
-5
No files matched your search
@@ -111,10 +111,34 @@ model User {
|
||||
webhookSubscriptions WebhookSubscription[]
|
||||
documents Document[]
|
||||
honeyBatchRelocations HoneyBatchRelocation[]
|
||||
apiKeys ApiKey[]
|
||||
|
||||
@@index([roleId])
|
||||
}
|
||||
|
||||
// Schlüssel für Maschinen-Zugriffe (z. B. Wabenhain-Shop-Sync) statt eines
|
||||
// Service-Kontos mit Passwort. Gespeichert wird nur der SHA-256-Hash des
|
||||
// Schlüssels; der Klartext wird genau einmal bei der Erstellung angezeigt.
|
||||
// Ein Schlüssel ist kein Benutzer: er darf nur die Routen, deren
|
||||
// Berechtigung in `scopes` steht (siehe api-keys/api-key-scopes.ts).
|
||||
model ApiKey {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
// Anfang des Schlüssels ("ack_ab12cd34") - zum Wiedererkennen in der
|
||||
// Liste, ohne den Schlüssel selbst zu speichern.
|
||||
prefix String
|
||||
keyHash String @unique
|
||||
scopes String[]
|
||||
createdByUserId String?
|
||||
createdByUser User? @relation(fields: [createdByUserId], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
lastUsedAt DateTime?
|
||||
expiresAt DateTime?
|
||||
revokedAt DateTime?
|
||||
|
||||
@@index([revokedAt])
|
||||
}
|
||||
|
||||
model RefreshToken {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
|
||||
Reference in new issue
Block a user