using System; using System.Text; using ClaudeDo.Ui.Services; using Xunit; namespace ClaudeDo.Ui.Tests.Services; public class ZitadelTokenInspectorTests { // Builds a fake JWT (header.payload.signature) carrying the given JSON payload. private static string MakeToken(string payloadJson) { static string B64Url(string s) { var bytes = Encoding.UTF8.GetBytes(s); return Convert.ToBase64String(bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_'); } return $"{B64Url("{\"alg\":\"RS256\"}")}.{B64Url(payloadJson)}.sig"; } [Fact] public void HasUserRole_True_ForGenericRolesClaim() { var token = MakeToken( "{\"urn:zitadel:iam:org:project:roles\":{\"user\":{\"org1\":\"example.com\"}}}"); Assert.True(ZitadelTokenInspector.HasUserRole(token)); } [Fact] public void HasUserRole_True_ForProjectScopedRolesClaim() { var token = MakeToken( "{\"urn:zitadel:iam:org:project:376787351902355727:roles\":{\"user\":{\"org1\":\"example.com\"}}}"); Assert.True(ZitadelTokenInspector.HasUserRole(token)); } [Fact] public void HasUserRole_False_WhenRoleMissing() { var token = MakeToken( "{\"urn:zitadel:iam:org:project:roles\":{\"admin\":{\"org1\":\"example.com\"}}}"); Assert.False(ZitadelTokenInspector.HasUserRole(token)); } [Fact] public void HasUserRole_False_WhenNoRolesClaim() { var token = MakeToken("{\"sub\":\"123\",\"email\":\"a@b.c\"}"); Assert.False(ZitadelTokenInspector.HasUserRole(token)); } [Theory] [InlineData(null)] [InlineData("")] [InlineData("not-a-jwt")] [InlineData("only.two")] public void HasUserRole_FailsOpen_ForUnparseableInput(string? token) { // Cannot decide -> fail open (server remains the source of truth). Assert.True(ZitadelTokenInspector.HasUserRole(token)); } }