Kareadita/Kavita · error · KavitaException
User is not authenticated
Error message
User is not authenticated
What it means
Thrown by ClaimsPrincipalExtensions.GetUsername when the JWT has no 'name' claim (JwtRegisteredClaimNames.Name). It is a KavitaException, so ExceptionMiddleware maps it to HTTP 500 with the message in the body. The name claim is what every controller uses to resolve the current user's display name.
Solutions
- Ensure the JWT minting code adds JwtRegisteredClaimNames.Name at token creation time.
- For OIDC, map your provider's username claim (e.g. preferred_username) to 'name' in TokenValidationParameters.NameClaimType or in the claim conversion step.
- Guard the endpoint with [Authorize] so unauthenticated principals never reach GetUsername.
- Before calling GetUsername, check user.Identity?.IsAuthenticated and fail early with 401 instead of a 500.
Example fix
// before var username = User.GetUsername(); // after if (User.Identity?.IsAuthenticated != true) return Unauthorized(); var username = User.GetUsername();
Defensive patterns
Strategy: validation
Validate before calling
if (User.Identity?.IsAuthenticated != true
|| User.FindFirst(JwtRegisteredClaimNames.Name) is null)
return Unauthorized(); Type guard
static bool HasNameClaim(ClaimsPrincipal user)
=> user.Identity?.IsAuthenticated == true
&& user.FindFirst(JwtRegisteredClaimNames.Name) is not null; Try / catch
try { var name = User.GetUsername(); }
catch (KavitaException) { return Unauthorized(); } Prevention
- Mint the JWT with JwtRegisteredClaimNames.Name at token creation.
- Guard every GetUsername call site with an IsAuthenticated check.
- For OIDC, ensure the provider's username claim maps to 'name'.
When it happens
Trigger: A request reaches a handler that calls User.GetUsername() but the principal's 'name' claim is absent. Happens with anonymous requests hitting an unguarded endpoint, a token minted without the Name claim, or an OIDC login where the provider did not emit a 'name'/'preferred_username' claim.
Common situations: OIDC providers that send 'preferred_username' instead of 'name'; custom token minting that forgets JwtRegisteredClaimNames.Name; a [AllowAnonymous] or mis-ordered middleware pipeline so HttpContext.User has no claims when the call runs.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- errors.oidc.missing-email
- errors.oidc.missing-external-id
- errors.oidc.email-in-use
- errors.oidc.email-not-verified
- errors.oidc.no-account
AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13).
Data as JSON: /api/errors/75e9ce9489b2f4da.
Report an issue: GitHub.
Appendix: source
Thrown at Kavita.Common/Extensions/ClaimsPrincipalExtensions.cs:22
using System.Security.Claims;
namespace Kavita.Common.Extensions;
public static class ClaimsPrincipalExtensions
{
private const string NotAuthenticatedMessage = "User is not authenticated";
private const string EmailVerifiedClaimType = "email_verified";
/// <summary>
/// Gets the authenticated user's username
/// </summary>
/// <remarks>Warning! Username's can contain .. and /, do not use folders or filenames explicitly with the Username</remarks>
/// <param name="user"></param>
/// <returns></returns>
/// <exception cref="KavitaException"></exception>
public static string GetUsername(this ClaimsPrincipal user)
{
var userClaim = user.FindFirst(JwtRegisteredClaimNames.Name) ?? throw new KavitaException(NotAuthenticatedMessage);
return userClaim.Value;
}
public static int GetUserId(this ClaimsPrincipal user)
{
var userClaim = user.FindFirst(ClaimTypes.NameIdentifier) ?? throw new KavitaException(NotAuthenticatedMessage);
return int.Parse(userClaim.Value);
}
public static bool HasVerifiedEmail(this ClaimsPrincipal user)
{
var emailVerified = user.FindFirst(EmailVerifiedClaimType);
if (emailVerified == null) return false;
if (!bool.TryParse(emailVerified.Value, out bool emailVerifiedValue) || !emailVerifiedValue)
{
return false;
}View on GitHub (pinned to 9c3e540000)