public static IEnumerable BuildJwtClaims()

in src/Microsoft.Azure.SignalR.Common/Utilities/ClaimsUtility.cs [38:163]


        public static IEnumerable<Claim> BuildJwtClaims(
            ClaimsPrincipal user,
            string userId,
            Func<IEnumerable<Claim>> claimsProvider,
            string serverName = null,
            ServerStickyMode mode = ServerStickyMode.Disabled,
            bool enableDetailedErrors = false,
            int endpointsCount = 1,
            int? maxPollInterval = null,
            bool isDiagnosticClient = false, int handshakeTimeout = Constants.Periods.DefaultHandshakeTimeout,
            HttpTransportType? httpTransportType = null,
            bool closeOnAuthenticationExpiration = false, DateTimeOffset? authenticationExpiresOn = default
            )
        {
            if (userId != null)
            {
                yield return new Claim(Constants.ClaimType.UserId, userId);
            }

            if (serverName != null && mode != ServerStickyMode.Disabled)
            {
                yield return new Claim(Constants.ClaimType.ServerName, serverName);
                yield return new Claim(Constants.ClaimType.ServerStickyMode, mode.ToString());
            }

            if (isDiagnosticClient)
            {
                yield return new Claim(Constants.ClaimType.DiagnosticClient, "true");
            }

            if (handshakeTimeout != Constants.Periods.DefaultHandshakeTimeout)
            {
                yield return new Claim(Constants.ClaimType.CustomHandshakeTimeout, handshakeTimeout.ToString(CultureInfo.InvariantCulture));
            }

            var authenticationType = user?.Identity?.AuthenticationType;

            // No need to pass it when the authentication type is Bearer
            if (authenticationType != null && authenticationType != DefaultAuthenticationType)
            {
                yield return new Claim(Constants.ClaimType.AuthenticationType, authenticationType);
            }

            // Trace multiple instances
            if (endpointsCount > 1)
            {
                yield return new Claim(Constants.ClaimType.ServiceEndpointsCount, endpointsCount.ToString(CultureInfo.InvariantCulture));
            }

            if (enableDetailedErrors)
            {
                yield return new Claim(Constants.ClaimType.EnableDetailedErrors, true.ToString());
            }

            // Return custom NameClaimType and RoleClaimType
            // We can have multiple Identities, for now, choose the default one 
            if (user?.Identity is ClaimsIdentity identity)
            {
                var nameType = identity.NameClaimType;
                if (nameType != null && nameType != DefaultNameClaimType)
                {
                    yield return new Claim(Constants.ClaimType.NameType, nameType);
                }

                var roleType = identity.RoleClaimType;
                if (roleType != null && roleType != DefaultRoleClaimType)
                {
                    yield return new Claim(Constants.ClaimType.RoleType, roleType);
                }
            }

            // add claim if exists, validation is in DI  
            if (maxPollInterval.HasValue)
            {
                yield return new Claim(Constants.ClaimType.MaxPollInterval, maxPollInterval.Value.ToString(CultureInfo.InvariantCulture));
            }

            if (httpTransportType.HasValue)
            {
                yield return new Claim(Constants.ClaimType.HttpTransportType, ((int)httpTransportType).ToString(CultureInfo.InvariantCulture));
            }

            if (closeOnAuthenticationExpiration && authenticationExpiresOn != null && authenticationExpiresOn.HasValue)
            {
                yield return new Claim(Constants.ClaimType.CloseOnAuthExpiration, "true");
                yield return new Claim(Constants.ClaimType.AuthExpiresOn, authenticationExpiresOn.Value.ToUnixTimeSeconds().ToString(CultureInfo.InvariantCulture));
            }

            // return customer's claims
            var customerClaims = (claimsProvider == null ? user?.Claims : claimsProvider.Invoke())?.ToArray();
            if (customerClaims != null)
            {
                // According to the spec https://datatracker.ietf.org/doc/html/rfc7519#section-4.1
                // the "sub" value is a case-sensitive string containing a StringOrURI value
                // "sub" is used as the UserId if userId is not specified
                // If "sub" exists, we here make sure only one "sub" is preserved, others will be renamed as user claim type
                var hasSubClaim = false;
                foreach (var claim in customerClaims)
                {
                    if (claim.Type == "sub")
                    {
                        if (hasSubClaim)
                        {
                            // only the first "sub" is preserved as "sub", others will be renamed as user claims
                            yield return new Claim(Constants.ClaimType.AzureSignalRUserPrefix + claim.Type, claim.Value);
                        }
                        else
                        {
                            hasSubClaim = true;
                            // The first "sub" would be also considered as "nameIdentifier" and used as SignalR's UserIdentifier
                            yield return claim;
                        }

                    }
                    // Add AzureSignalRUserPrefix if customer's claim name is duplicated with SignalR system claims.
                    else if (SystemClaims.Contains(claim.Type))
                    {
                        yield return new Claim(Constants.ClaimType.AzureSignalRUserPrefix + claim.Type, claim.Value);
                    }
                    else
                    {
                        yield return claim;
                    }
                }
            }
        }