Extrahiert die zuvor zweimal von Hand gebaute Plumbing aus pageta-native + nutriphi-native in ein Paket ManaEventSync (Counterpart zu @mana/event-sync): - Core: EventEnvelope (+clientId/payloadHash), JSONValue, ULID, JWTSubject, EventSyncError - EventStorage (SwiftData): parametrierbar (Store/App-Group/additionalModels/In-Memory), Outbox, Claim-Reattribute, Anonymous-Retention-Prune - Transport: SyncHTTPClient (+X-Schema-Hash/422), optionaler SyncWSClient - Crypto: CryptoProvider/NoOp/MasterKey (AES-GCM enc:1:), VaultClient — opt-in - EventSyncEngine: besitzt die Identität (makeEnvelope anon/echt), emit offline, signIn(realUserId) re-taggt idempotent + drained, Retention 10k/90d 18 Tests grün. Plan: mana/docs/playbooks/MANA_SWIFT_EVENT_SYNC.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
27 lines
1.1 KiB
Swift
27 lines
1.1 KiB
Swift
import Foundation
|
|
|
|
/// Liest den `sub`-Claim aus einem JWT (Base64URL-Decode des Payloads).
|
|
///
|
|
/// Bewusst hier statt in `ManaCore` dupliziert, solange `JWT.sub(of:)`
|
|
/// dort nicht öffentlich ist — vermeidet eine Cross-Repo-Änderung.
|
|
public enum JWTSubject {
|
|
public static func subject(of token: String) -> String? {
|
|
let parts = token.split(separator: ".")
|
|
guard parts.count == 3 else { return nil }
|
|
guard let payloadData = base64URLDecode(String(parts[1])) else { return nil }
|
|
guard
|
|
let dict = try? JSONSerialization.jsonObject(with: payloadData) as? [String: Any],
|
|
let sub = dict["sub"] as? String
|
|
else { return nil }
|
|
return sub
|
|
}
|
|
|
|
private static func base64URLDecode(_ value: String) -> Data? {
|
|
var base64 = value
|
|
.replacingOccurrences(of: "-", with: "+")
|
|
.replacingOccurrences(of: "_", with: "/")
|
|
let padding = (4 - base64.count % 4) % 4
|
|
base64.append(String(repeating: "=", count: padding))
|
|
return Data(base64Encoded: base64)
|
|
}
|
|
}
|