{"record":{"id":"5ab1d66fba4fb5cd","repo":"clockworklabs/SpacetimeDB","slug":"unexpected-type-for-aud-claim-in-jwt","errorCode":null,"errorMessage":"Unexpected type for 'aud' claim in JWT","messagePattern":"Unexpected type for 'aud' claim in JWT","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"crates/bindings-csharp/Runtime/JwtClaims.cs","lineNumber":82,"sourceCode":"    }\n\n    private List<string> ExtractAudience()\n    {\n        if (!RootElement.TryGetProperty(\"aud\", out var aud))\n        {\n            return [];\n        }\n\n        return aud.ValueKind switch\n        {\n            JsonValueKind.String => [aud.GetString()!],\n            JsonValueKind.Array =>\n            [\n                .. aud.EnumerateArray()\n                    .Where(e => e.ValueKind == JsonValueKind.String)\n                    .Select(e => e.GetString()!),\n            ],\n            _ => throw new InvalidOperationException(\"Unexpected type for 'aud' claim in JWT\"),\n        };\n    }\n\n    public IReadOnlyList<string> Audience => _audience.Value;\n\n    // TODO: Should this be exposed as a JsonDocument, since that it in the stdlib?\n    public string RawPayload => _payload;\n}\n","sourceCodeStart":64,"sourceCodeEnd":91,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/bindings-csharp/Runtime/JwtClaims.cs#L64-L91","documentation":"JwtClaims.ExtractAudience (backing the lazily-evaluated Audience property) accepts an 'aud' claim that is a JSON string or an array of strings; a missing 'aud' is fine (empty list). Any other JSON value kind — number, boolean, object, or null — throws InvalidOperationException when Audience is first accessed.","triggerScenarios":"Reading jwt.Audience when the token's 'aud' claim is a number (e.g. a client_id configured as an int), a boolean, null, or a JSON object. Only triggered by the first access because the audience list is computed lazily.","commonSituations":"Custom auth servers that store audience as a numeric app id; mis-serialized tokens where 'aud' becomes null; issuers that emit aud as an object map of {client: scopes}.","solutions":["Inspect JwtClaims.RawPayload to see the actual JSON type of 'aud'","Fix the issuer to emit 'aud' as a string or an array of strings (the only shapes the JWT spec allows)","If you must tolerate other shapes, parse RawPayload yourself with JsonDocument instead of using Audience"],"exampleFix":"// before\nvar audiences = jwt.Audience; // throws for aud: 42 / null / {}\n\n// after\nusing var doc = JsonDocument.Parse(jwt.RawPayload);\nList<string> audiences = [];\nif (doc.RootElement.TryGetProperty(\"aud\", out var aud))\n{\n    if (aud.ValueKind == JsonValueKind.String) audiences.Add(aud.GetString()!);\n    else if (aud.ValueKind == JsonValueKind.Array)\n        audiences.AddRange(aud.EnumerateArray().Where(e => e.ValueKind == JsonValueKind.String).Select(e => e.GetString()!));\n}","handlingStrategy":"validation","validationCode":"bool HasValidAudShape(JwtClaims? jwt)\n{\n    if (jwt == null) return true; // missing 'aud' is fine\n    using var doc = JsonDocument.Parse(jwt.RawPayload);\n    if (!doc.RootElement.TryGetProperty(\"aud\", out var aud)) return true;\n    return aud.ValueKind is JsonValueKind.String or JsonValueKind.Array;\n}","typeGuard":"static bool AudienceIsSafe(JwtClaims? jwt) => HasValidAudShape(jwt);","tryCatchPattern":"try { var aud = jwt.Audience; }\ncatch (InvalidOperationException e) when (e.Message.Contains(\"aud\")) { /* non-standard 'aud' claim; parse RawPayload yourself */ }","preventionTips":["Confirm your auth server emits 'aud' as string or array of strings","Never read Audience on tokens from untrusted sources without a shape check"],"tags":["jwt","claims","audience","csharp"],"backgroundTag":"jwt-claim-wrong-type","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}