OrchardCMS/OrchardCore · error · InvalidOperationException
No suitable user name can be found in the principal.
Error message
No suitable user name can be found in the principal.
What it means
OpenIdExtensions.GetUserName extracts a user name from a ClaimsPrincipal, checking OpenID Connect 'name' (Claims.Name) first, then ClaimTypes.Name. If neither claim exists on the principal it throws InvalidOperationException because downstream code (e.g. mapping external logins to a local user) requires a displayable name.
Solutions
- Request the 'profile' (and 'openid') scopes on the OpenIdConnect client options so the name claim is issued
- Configure ClaimActions/claim-type mappings so the provider's display-name claim maps to ClaimTypes.Name
- Use GetUserName on principals known to carry name claims, or fall back to the 'sub'/NameIdentifier claim for user lookup
- Wrap in try-catch and handle missing-name principals explicitly (e.g. prompt user for a username)
Example fix
// before
var name = principal.GetUserName();
// after
var name = principal.FindFirst(Claims.Name)?.Value
?? principal.FindFirst(ClaimTypes.Name)?.Value
?? principal.FindFirst(ClaimTypes.NameIdentifier)?.Value
?? throw new InvalidOperationException("No suitable user name can be found in the principal."); Defensive patterns
Strategy: validation
Validate before calling
if (principal?.Identity?.IsAuthenticated != true ||
(principal.FindFirst(ClaimTypes.Name) is null && principal.FindFirst("name") is null)) {
throw new InvalidOperationException("Principal has no name claim; request 'profile' scope or map claims first.");
} Try / catch
try { name = principal.GetUserName(); } catch (InvalidOperationException ex) { logger.LogWarning(ex, "Principal missing name claim"); /* fallback to subject claim */ } Prevention
- Always request openid and profile scopes from the OIDP
- Verify claim-type mappings (ClaimActions) in the OIDC options during integration testing
- Log principal claims when debugging external logins
When it happens
Trigger: Calling GetUserName on a principal whose identity token contains no 'name' or 'nameidentifier'-style Name claim — e.g. an OpenID Connect provider that only sends 'sub', or a token missing the 'profile'/'email' scopes that populate name claims.
Common situations: Configuring an external OIDP (Keycloak, Auth0, Azure AD) without requesting the profile scope; a misconfigured claim-type mapping so the provider's name claim isn't mapped to ClaimTypes.Name; custom tokens issued without standard claims.
Related errors
- No suitable user identifier can be found in the identity.
- No suitable user identifier can be found in the principal.
- An error occurred while pruning authorizations.
- Multiple certificates with the same thumbprint were found.
- The application details cannot be found.
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/18f89eb4fa1a48e1.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore.Modules/OrchardCore.OpenId/OpenIdExtensions.cs:23
internal static class OpenIdExtensions
{
internal static string GetUserIdentifier(this ClaimsIdentity identity)
=> identity.FindFirst(Claims.Subject)?.Value ??
identity.FindFirst(ClaimTypes.NameIdentifier)?.Value ??
identity.FindFirst(ClaimTypes.Upn)?.Value ??
throw new InvalidOperationException("No suitable user identifier can be found in the identity.");
internal static string GetUserIdentifier(this ClaimsPrincipal principal)
=> principal.FindFirst(Claims.Subject)?.Value ??
principal.FindFirst(ClaimTypes.NameIdentifier)?.Value ??
principal.FindFirst(ClaimTypes.Upn)?.Value ??
throw new InvalidOperationException("No suitable user identifier can be found in the principal.");
internal static string GetUserName(this ClaimsPrincipal principal)
=> principal.FindFirst(Claims.Name)?.Value ??
principal.FindFirst(ClaimTypes.Name)?.Value ??
throw new InvalidOperationException("No suitable user name can be found in the principal.");
internal static string[] GetRoles(this ClaimsPrincipal principal)
=> principal.FindAll(c => c.Type is Claims.Role or ClaimTypes.Role)
.Select(x => x.Value)
.Distinct()
.ToArray();
internal static async Task<bool> AnyAsync<T>(this IAsyncEnumerable<T> source)
{
ArgumentNullException.ThrowIfNull(source);
await using var enumerator = source.GetAsyncEnumerator();
return await enumerator.MoveNextAsync();
}
internal static Task<List<T>> ToListAsync<T>(this IAsyncEnumerable<T> source)
{
ArgumentNullException.ThrowIfNull(source);View on GitHub (pinned to 4306c0717f)