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.

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)

Solutions

  1. Ensure the JWT minting code adds JwtRegisteredClaimNames.Name at token creation time.
  2. For OIDC, map your provider's username claim (e.g. preferred_username) to 'name' in TokenValidationParameters.NameClaimType or in the claim conversion step.
  3. Guard the endpoint with [Authorize] so unauthenticated principals never reach GetUsername.
  4. 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

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

Related errors


AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13). Data as JSON: /api/errors/75e9ce9489b2f4da. Report an issue: GitHub.