feat(online-inbox): carry ownerId on sync to prepare for multi-user
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>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
23c3065f20
commit
cee051bb6d
@@ -0,0 +1,49 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace ClaudeDo.Worker.Online;
|
||||
|
||||
/// <summary>
|
||||
/// Minimal, dependency-free reader for a JWT access token's payload claims. Used to resolve the
|
||||
/// current user's Zitadel subject (<c>sub</c>) so sync payloads can be stamped with an owner.
|
||||
/// Never throws — returns null when the token is absent or cannot be parsed.
|
||||
/// </summary>
|
||||
public static class JwtClaims
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the <c>sub</c> claim of the JWT, or null if the token is absent/unparseable or
|
||||
/// carries no subject.
|
||||
/// </summary>
|
||||
public static string? GetSubject(string? jwt)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(jwt))
|
||||
return null;
|
||||
|
||||
var parts = jwt.Split('.');
|
||||
if (parts.Length < 2)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
using var doc = JsonDocument.Parse(Base64UrlDecode(parts[1]));
|
||||
if (doc.RootElement.TryGetProperty("sub", out var sub) &&
|
||||
sub.ValueKind == JsonValueKind.String)
|
||||
return sub.GetString();
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] Base64UrlDecode(string input)
|
||||
{
|
||||
var s = input.Replace('-', '+').Replace('_', '/');
|
||||
switch (s.Length % 4)
|
||||
{
|
||||
case 2: s += "=="; break;
|
||||
case 3: s += "="; break;
|
||||
}
|
||||
return Convert.FromBase64String(s);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user