feat(online-inbox): gate access on Zitadel "user" project role
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>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
80a2de6c74
commit
23c3065f20
@@ -36,19 +36,29 @@ public sealed class ZitadelAuthProvider : IOnlineAuthProvider
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<string?> GetAccessTokenAsync(CancellationToken ct = default)
|
||||
public Task<string?> GetAccessTokenAsync(CancellationToken ct = default)
|
||||
=> GetAccessTokenAsync(false, ct);
|
||||
|
||||
public async Task<string?> GetAccessTokenAsync(bool forceRefresh, CancellationToken ct = default)
|
||||
{
|
||||
// Fast path: check cache without acquiring the lock.
|
||||
if (_cachedAccessToken is not null && DateTimeOffset.UtcNow < _cacheExpiry)
|
||||
if (!forceRefresh && _cachedAccessToken is not null && DateTimeOffset.UtcNow < _cacheExpiry)
|
||||
return _cachedAccessToken;
|
||||
|
||||
await _lock.WaitAsync(ct);
|
||||
try
|
||||
{
|
||||
// Re-check inside the lock (double-checked locking).
|
||||
if (_cachedAccessToken is not null && DateTimeOffset.UtcNow < _cacheExpiry)
|
||||
if (!forceRefresh && _cachedAccessToken is not null && DateTimeOffset.UtcNow < _cacheExpiry)
|
||||
return _cachedAccessToken;
|
||||
|
||||
if (forceRefresh)
|
||||
{
|
||||
// Drop the stale access token so the refresh-token grant mints a fresh one.
|
||||
_cachedAccessToken = null;
|
||||
_cacheExpiry = default;
|
||||
}
|
||||
|
||||
var refreshToken = _tokenStore.Read();
|
||||
if (refreshToken is null)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user