v1.2.0 — Guest-Mode + Refresh-Resilience
Native-Apps werden gegen mana-auth-Downtime gehärtet und können einen anonymen Local-First-Modus anbieten. Komplett additiv. AuthClient.Status um `.guest(id: String)` erweitert — persistente lokale UUID ohne Server-Account, gleichberechtigt mit `.signedIn` als "App ist nutzbar"-Zustand. Neue Methoden: - enterGuestMode() throws -> String — idempotent - currentGuestId() -> String? - clearGuestId() - signOut(keepGuestMode: Bool = false) — Default-Verhalten unverändert KeychainStore.Key.guestId neu. wipe() löscht nur Session-Felder (accessToken/refreshToken/email); Guest-ID überlebt. Für komplettes Vergessen: neue wipeAll(). refreshAccessToken() wipt nicht mehr blind bei jedem Nicht-200. Heuristik via AuthError.invalidatesSession: - Wipe bei invalidCredentials/unauthorized/tokenExpired/tokenInvalid/ emailNotVerified — Session ist tatsächlich tot. - Behalten bei serviceUnavailable/serverInternal/networkFailure/ rateLimited — Apps werden bei mana-auth-Downtime nicht mehr in Login geworfen. Beim Wipe fällt der Status auf .guest(id) zurück, falls eine Guest-Identität existiert; sonst auf .signedOut. Tests: - Mock-Setup auf per-test-ID-Routing migriert (analog mana-swift-ui), löst Cross-Suite-Pollution zwischen AuthClient+Account und AuthClient Guest-Mode + Resilience. - 15 neue Tests für Guest-Mode + Refresh-Resilience. - 54/54 Tests grün. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3459c78731
commit
923b5d06b5
7 changed files with 647 additions and 160 deletions
71
CHANGELOG.md
71
CHANGELOG.md
|
|
@ -4,6 +4,77 @@ Alle Änderungen werden hier dokumentiert. Format orientiert an
|
|||
[Keep a Changelog](https://keepachangelog.com), Versionierung nach
|
||||
[Semver](https://semver.org).
|
||||
|
||||
## [1.2.0] — 2026-05-13
|
||||
|
||||
Minor — Guest-Mode + Auth-Resilience. Native-Apps werden gegen mana-auth-
|
||||
Downtime gehärtet und können jetzt einen anonymen Local-First-Modus
|
||||
anbieten. Komplett additiv — keine Breaking Changes für bestehende
|
||||
Konsumenten (Memoro, Cards, Manaspur, Nutriphi).
|
||||
|
||||
### ManaCore — Guest-Identität
|
||||
|
||||
- `AuthClient.Status` um Case `.guest(id: String)` erweitert. Persistente
|
||||
lokale UUID ohne Server-Account; gleichberechtigt mit `.signedIn` als
|
||||
„App ist nutzbar"-Zustand. Apps können in diesem Modus alles Lokale
|
||||
und alle unauthenticated-Server-Endpoints anbieten, schreibende
|
||||
Endpoints poppen Auth-Sheet.
|
||||
- `AuthClient.enterGuestMode() throws -> String` — idempotent, erzeugt
|
||||
oder reuse die Guest-UUID aus Keychain. Wechselt den Status nur,
|
||||
wenn aktuell `.signedOut`/`.unknown` (eine aktive Session bleibt
|
||||
unangetastet, App kann die Guest-ID parallel lesen).
|
||||
- `AuthClient.currentGuestId() -> String?` — Lookup unabhängig vom Status.
|
||||
Genutzt z.B. um lokale Guest-Daten beim Sign-In dem neuen Server-
|
||||
Account zuzuordnen.
|
||||
- `AuthClient.clearGuestId()` — entfernt die Guest-ID, etwa nach
|
||||
erfolgreicher Migration der lokalen Daten auf einen Server-Account.
|
||||
- `AuthClient.signOut(keepGuestMode: Bool = false)` — Default-Verhalten
|
||||
unverändert (`false` löscht alles, Status `.signedOut`). Mit `true`
|
||||
bleibt die App im anonymen Modus weiter nutzbar.
|
||||
- `KeychainStore.Key.guestId` als neuer Key. `wipe()` löscht jetzt
|
||||
*nur* Session-Felder (accessToken/refreshToken/email) — die Guest-ID
|
||||
überlebt. Für komplettes Vergessen: neue `wipeAll()`.
|
||||
|
||||
### ManaCore — Refresh-Resilience
|
||||
|
||||
- `refreshAccessToken()` wipt nicht mehr blind den Keychain bei jedem
|
||||
Nicht-200. Stattdessen Heuristik via `AuthError.invalidatesSession`:
|
||||
- **Wipe** bei `.invalidCredentials`, `.unauthorized`, `.tokenExpired`,
|
||||
`.tokenInvalid`, `.emailNotVerified` — Session ist tatsächlich tot.
|
||||
- **Behalten** bei `.serviceUnavailable` (503), `.serverInternal`
|
||||
(500), `.networkFailure`, `.rateLimited`, weiteren transienten
|
||||
Fehlern. Apps werden bei mana-auth-Downtime nicht mehr in den
|
||||
Login-Screen geworfen.
|
||||
- Beim Wipe-Pfad fällt der Status auf `.guest(id)` zurück, falls eine
|
||||
Guest-Identität existiert; sonst auf `.signedOut`.
|
||||
- `AuthError.invalidatesSession: Bool` — public computed Property,
|
||||
auch von Apps direkt nutzbar (z.B. um auf Transport-Fehler zu
|
||||
reagieren).
|
||||
|
||||
### Tests
|
||||
|
||||
- 15 neue Tests: Guest-Mode (Idempotenz, Bootstrap-Priorität, Status-
|
||||
Übergänge), signOut(keepGuestMode:) in beiden Modi, Refresh-Verhalten
|
||||
bei 401/429/500/503/Network, invalidatesSession-Partitionierung.
|
||||
|
||||
### Migration für Apps
|
||||
|
||||
Bestehende Apps brauchen **keine** Änderung — Default-Verhalten ist
|
||||
identisch. Wer den anonymen Modus nutzen will:
|
||||
|
||||
```swift
|
||||
// Beim App-Start nach bootstrap():
|
||||
auth.bootstrap()
|
||||
if case .signedOut = auth.status {
|
||||
try? auth.enterGuestMode() // Statt sofort Login-Screen
|
||||
}
|
||||
|
||||
// In Aktionen, die einen Account brauchen:
|
||||
guard case .signedIn = auth.status else {
|
||||
presentLoginSheet()
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
## [1.1.1] — 2026-05-13
|
||||
|
||||
Patch — Wire-Konvention für authenticated Account-Calls geklärt.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue