fix(auth): getUser() refresht abgelaufene ID-Tokens — Fixes #404 #410

Merged
pm-bot merged 1 commit from fix/404-getuser-refresh into main 2026-05-25 23:11:26 +02:00
Collaborator

Summary

  • AuthClient.getUser() warf Error: ID token expired direkt aus verifyIdToken(), ohne den bereits existierenden Refresh-Pfad zu nutzen — die unhandled promise rejection landete im UI als roher Stacktrace (siehe #404). Inkonsistenz innerhalb der eigenen API: getAccessToken() refresht korrekt, getUser() nicht.
  • Fix: bei ID token expired versucht getUser() jetzt einen Refresh, verifiziert den neuen ID-Token und gibt den User zurück; bei fehlendem Refresh-Token → Tokens löschen, logout-Event, null zurückgeben. Andere Verify-Fehler (Signatur, Issuer, Audience, Nonce) propagieren weiterhin — die deuten auf echte Probleme hin und dürfen nicht stillschweigend geschluckt werden.
  • Test-Infrastruktur in packages/auth neu aufgesetzt (vitest.config.ts + test-Script + vitest dev-dep). 3 Specs nach TDD: red → green → refactor.

Nicht in diesem PR

  • API-Layer-Konsistenz (Cache liefert Lists trotz abgelaufenem Token) → separat als #408 dokumentiert
  • Globaler Promise-Rejection-Handler im Web → falls nach dieser Library-Fix noch andere unhandled rejections auftauchen, dann eigenes Issue
  • ESLint v9 Flat-Config-Adoption in 7 Workspaces → bei der Arbeit entdeckt, separat in #409

Test plan

  • pnpm --filter @mrrmlab/auth test → 3/3 grün (vorher 2/3 rot, Bug bestätigt)
  • pnpm --filter @mrrmlab/auth --filter @mrrmlab/api-client typecheck → grün
  • pnpm --filter @mrrmlab/web --filter mobile typecheck → grün (Consumer brauchen keine Anpassung; Return-Type war bereits AuthUser | null)
  • Manuelle Verifizierung lt. Reproduzier-Rezept in #404 nach Merge auf dev/prod: Token künstlich invalidieren → erwartet Silent-Refresh oder Login-Redirect, kein Stacktrace
  • Smoke-Test-Checkliste v0.4.6 (siehe Kommentar in #405) vor Release-Tag

Consumer-Impact

4 Aufrufer in apps/{web,mobile} rufen auth.getUser().then(setUser) ohne .catch() — Return-Type war schon AuthUser | null, daher keine Code-Änderung am Call-Site nötig. Vorher: Crash. Jetzt: geordneter Logged-Out-State.

## Summary - `AuthClient.getUser()` warf `Error: ID token expired` direkt aus `verifyIdToken()`, ohne den bereits existierenden Refresh-Pfad zu nutzen — die unhandled promise rejection landete im UI als roher Stacktrace (siehe #404). Inkonsistenz innerhalb der eigenen API: `getAccessToken()` refresht korrekt, `getUser()` nicht. - Fix: bei `ID token expired` versucht `getUser()` jetzt einen Refresh, verifiziert den neuen ID-Token und gibt den User zurück; bei fehlendem Refresh-Token → Tokens löschen, logout-Event, `null` zurückgeben. Andere Verify-Fehler (Signatur, Issuer, Audience, Nonce) propagieren weiterhin — die deuten auf echte Probleme hin und dürfen nicht stillschweigend geschluckt werden. - Test-Infrastruktur in `packages/auth` neu aufgesetzt (`vitest.config.ts` + test-Script + vitest dev-dep). 3 Specs nach TDD: red → green → refactor. ## Nicht in diesem PR - API-Layer-Konsistenz (Cache liefert Lists trotz abgelaufenem Token) → separat als #408 dokumentiert - Globaler Promise-Rejection-Handler im Web → falls nach dieser Library-Fix noch andere unhandled rejections auftauchen, dann eigenes Issue - ESLint v9 Flat-Config-Adoption in 7 Workspaces → bei der Arbeit entdeckt, separat in #409 ## Test plan - [x] `pnpm --filter @mrrmlab/auth test` → 3/3 grün (vorher 2/3 rot, Bug bestätigt) - [x] `pnpm --filter @mrrmlab/auth --filter @mrrmlab/api-client typecheck` → grün - [x] `pnpm --filter @mrrmlab/web --filter mobile typecheck` → grün (Consumer brauchen keine Anpassung; Return-Type war bereits `AuthUser | null`) - [ ] Manuelle Verifizierung lt. Reproduzier-Rezept in #404 nach Merge auf dev/prod: Token künstlich invalidieren → erwartet Silent-Refresh oder Login-Redirect, kein Stacktrace - [ ] Smoke-Test-Checkliste v0.4.6 (siehe Kommentar in #405) vor Release-Tag ## Consumer-Impact 4 Aufrufer in `apps/{web,mobile}` rufen `auth.getUser().then(setUser)` ohne `.catch()` — Return-Type war schon `AuthUser | null`, daher keine Code-Änderung am Call-Site nötig. Vorher: Crash. Jetzt: geordneter Logged-Out-State.
fix(auth): getUser() refresht abgelaufene ID-Tokens statt zu werfen
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
85b6b7daa1
getUser() rief verifyIdToken() ohne Refresh-Pfad auf — bei abgelaufenem
ID-Token wurde "ID token expired" geworfen und als unhandled promise
rejection im UI als roher Stacktrace gerendert. Die Refresh-Infrastruktur
existiert bereits (refresh(), getValidAccessToken), wurde aber von getUser()
nicht genutzt: Inkonsistenz innerhalb der eigenen API.

Verhalten jetzt:
- abgelaufener ID-Token + Refresh-Token vorhanden -> transparenter Refresh,
  neuer User wird zurückgegeben
- abgelaufener ID-Token + kein Refresh-Token -> Tokens werden gelöscht,
  logout-Event, getUser() liefert null
- alle anderen Verify-Fehler (Signatur, Issuer, Audience, Nonce) propagieren
  weiterhin, weil sie auf echte Probleme hindeuten und nicht stillschweigend
  geschluckt werden dürfen

Test-Infra für packages/auth war noch nicht vorhanden — vitest.config.ts +
package.json-Script + drei Specs nach TDD (red-green-refactor) hinzugefügt.

Nicht in diesem PR: API-Layer-Konsistenz (Cache liefert Lists trotz
abgelaufenem Token) -> separat in #408 dokumentiert. Globaler
Promise-Rejection-Handler im Web -> falls nach dieser Library-Fix noch
andere Unhandled-Rejections auftauchen, eigenes Issue.

Fixes #404

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
pm-bot merged commit 4d361f5951 into main 2026-05-25 23:11:26 +02:00
admin-mrrm referenced this pull request from a commit 2026-05-27 05:53:09 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
admin-mrrm/mrrmlabapp!410
No description provided.