Plumb a per-resource owner (Zitadel sub) through the sync contract without
enforcing isolation client-side — the server stays the authority.
- Dtos: add optional ownerId to RemoteList/RemoteTask/MirrorTask
- JwtClaims: decode the sub claim from the access token (never throws)
- OnlineSyncService: stamp ownerId on pushed lists + mirror; defensively skip
pulled tasks owned by a different user (unowned tasks still sync, so
single-user behavior is unchanged)
- docs: contract documents ownerId + multi-user readiness
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Online API now requires the "user" project role (claim
urn:zitadel:iam:org:project:roles) instead of an ALLOWED_USER_IDS allowlist.
- IOnlineAuthProvider: add GetAccessTokenAsync(forceRefresh) overload
- ZitadelAuthProvider: forceRefresh drops the cached token and re-runs the
refresh-token grant to mint a fresh, role-bearing token
- OnlineInboxApiClient: on 401, force-refresh and retry once; if still 401,
throw a clear "missing 'user' role" error
- OnlineSyncService: surface the 401 at Error level (no longer silent)
- UI: ZitadelTokenInspector decodes the access token after login and warns
early when the "user" role is absent (fail-open); shown in settings
- docs: online-inbox-api-contract reflects role-based access (no allowlist)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The API base URL is https://claudedo.kuns.dev/api — leading-slash request
paths discarded the /api segment. Use relative paths so they nest under the
base. Tests now use a /api/ base to guard the regression.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Headless refresh-token -> access-token exchange via OIDC discovery + token
endpoint. Cached to expiry (60s margin), thread-safe, persists rotated refresh
tokens, graceful null on invalid_grant/network errors. Wired into DI when
online_inbox is enabled. Interactive PKCE login (UI) still pending the
registered redirect URI. 7 tests, stubbed HttpMessageHandler.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Optional, opt-in (online_inbox.enabled, default false → zero network).
Worker-side reconcile loop: pull web-created tasks down as Idle, push the
list catalog and the Idle backlog mirror up. Auth behind IOnlineAuthProvider
(StaticTokenAuthProvider default; ZitadelAuthProvider stubbed for Phase 2).
DPAPI refresh-token store. 35 tests, no real network/Zitadel/Claude.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>