internal/serviceaccount: Service-Accounts als eigenstaendige Identitaetsklasse
(eigene Tabelle service_accounts, getrennt von internal/user — Zitadel-
Vorbild). Store.IssueToken gibt den Klartext-Token NUR einmal an den
Aufrufer zurueck, gespeichert wird ausschliesslich der SHA-256-Hash
(Akzeptanzkriterium 1). Scopes und optionale Ablaufzeit sind Teil des
Tokens selbst (Akzeptanzkriterium 2).
Store.Verify prueft Widerruf/Ablauf/Scope bei JEDEM Aufruf direkt gegen die
Datenbank — kein Cache dazwischen, ein widerrufenes Token wird ab dem
naechsten Request sofort abgewiesen (Akzeptanzkriterium 3).
Pruefungen (ausgefuehrt auf root@192.168.1.131, go build/vet/test PASS):
1. Token ausschliesslich gehasht gespeichert (Stichprobe in der Datenbank) —
TestIssueToken_StoresOnlyHash: token_hash-Spalte direkt gelesen, enthaelt
nachweislich nicht den Klartext, 32-Byte-SHA-256-Laenge bestaetigt. PASS.
2. Widerrufenes Token wird beim naechsten Request zuverlaessig abgewiesen —
TestRevoke_TakesEffectImmediately: Verify vor Widerruf erfolgreich, sofort
danach ErrTokenInvalid. PASS.
3. Scope-Verletzung wird korrekt abgewiesen —
TestVerify_RejectsInsufficientScope: Token mit scope=read wird fuer
scope=write abgewiesen (ErrScopeInsufficient), fuer scope=read akzeptiert. PASS.
Zusaetzlich: TestVerify_RejectsExpiredToken belegt die optionale zeitliche
Befristung aus Akzeptanzkriterium 2. PASS.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>