{"record":{"id":"75e9ce9489b2f4da","repo":"Kareadita/Kavita","slug":"user-is-not-authenticated","errorCode":null,"errorMessage":"User is not authenticated","messagePattern":"User is not authenticated","errorType":"exception","errorClass":"KavitaException","httpStatus":null,"severity":"error","filePath":"Kavita.Common/Extensions/ClaimsPrincipalExtensions.cs","lineNumber":22,"sourceCode":"using System.Security.Claims;\n\nnamespace Kavita.Common.Extensions;\n\npublic static class ClaimsPrincipalExtensions\n{\n    private const string NotAuthenticatedMessage = \"User is not authenticated\";\n    private const string EmailVerifiedClaimType = \"email_verified\";\n\n    /// <summary>\n    /// Gets the authenticated user's username\n    /// </summary>\n    /// <remarks>Warning! Username's can contain .. and /, do not use folders or filenames explicitly with the Username</remarks>\n    /// <param name=\"user\"></param>\n    /// <returns></returns>\n    /// <exception cref=\"KavitaException\"></exception>\n    public static string GetUsername(this ClaimsPrincipal user)\n    {\n        var userClaim = user.FindFirst(JwtRegisteredClaimNames.Name) ?? throw new KavitaException(NotAuthenticatedMessage);\n        return userClaim.Value;\n    }\n\n    public static int GetUserId(this ClaimsPrincipal user)\n    {\n        var userClaim = user.FindFirst(ClaimTypes.NameIdentifier) ?? throw new KavitaException(NotAuthenticatedMessage);\n        return int.Parse(userClaim.Value);\n    }\n\n    public static bool HasVerifiedEmail(this ClaimsPrincipal user)\n    {\n        var emailVerified = user.FindFirst(EmailVerifiedClaimType);\n        if (emailVerified == null) return false;\n\n        if (!bool.TryParse(emailVerified.Value, out bool emailVerifiedValue) || !emailVerifiedValue)\n        {\n            return false;\n        }","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/Kareadita/Kavita/blob/9c3e5400007f8a0282f7d883f2ad5e71716e514d/Kavita.Common/Extensions/ClaimsPrincipalExtensions.cs#L4-L40","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nvar username = User.GetUsername();\n\n// after\nif (User.Identity?.IsAuthenticated != true) return Unauthorized();\nvar username = User.GetUsername();","handlingStrategy":"validation","validationCode":"if (User.Identity?.IsAuthenticated != true\n    || User.FindFirst(JwtRegisteredClaimNames.Name) is null)\n    return Unauthorized();","typeGuard":"static bool HasNameClaim(ClaimsPrincipal user)\n    => user.Identity?.IsAuthenticated == true\n       && user.FindFirst(JwtRegisteredClaimNames.Name) is not null;","tryCatchPattern":"try { var name = User.GetUsername(); }\ncatch (KavitaException) { return Unauthorized(); }","preventionTips":["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'."],"tags":["authentication","jwt","claims","oidc"],"backgroundTag":null,"analyzedSha":"9c3e5400007f8a0282f7d883f2ad5e71716e514d","analyzedAt":"2026-08-13T19:06:05.897Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}