Kareadita/Kavita · error · KavitaException

errors.oidc.missing-email

Error message

errors.oidc.missing-email

What it means

Thrown in LoginOrCreate after a new (unknown oidcId) user passes the NameIdentifier check but the principal has no ClaimTypes.Email. Kavita requires an email to either match an existing account or build a new one; without it the flow cannot proceed. The 'email' scope being absent is the usual cause.

Source

Thrown at Kavita.Services/OidcService.cs:87

        var oidcId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
        if (string.IsNullOrEmpty(oidcId))
        {
            throw new KavitaException("errors.oidc.missing-external-id");
        }

        var user = await unitOfWork.UserRepository.GetByOidcId(oidcId, AppUserIncludes.UserPreferences | AppUserIncludes.SideNavStreams, ct);
        if (user != null)
        {
            await SyncUserSettings(request, settings, principal, user);

            return user;
        }

        var email = principal.FindFirstValue(ClaimTypes.Email);
        if (string.IsNullOrEmpty(email))
        {
            throw new KavitaException("errors.oidc.missing-email");
        }

        if (settings.RequireVerifiedEmail && !principal.HasVerifiedEmail())
        {
            throw new KavitaException("errors.oidc.email-not-verified");
        }


        user = await unitOfWork.UserRepository.GetUserByEmailAsync(email, AppUserIncludes.UserPreferences | AppUserIncludes.SideNavStreams, ct);
        if (user != null)
        {
            // Don't allow taking over accounts
            // This could happen if the user changes their email in OIDC, and then someone else uses the old one
            if (!string.IsNullOrEmpty(user.OidcId))
            {
                throw new KavitaException("errors.oidc.email-in-use");
            }

View on GitHub (pinned to 9c3e540000)

Solutions

  1. Add 'email' to the requested OIDC scopes (Kavita's DefaultScopes include it — confirm your config didn't override).
  2. Ensure the user has an email address set in the identity provider's profile.
  3. Map the IdP's email claim to ClaimTypes.Email if it uses a custom name.
  4. Decode the IdToken to confirm the email claim is present and non-empty.
Defensive patterns

Strategy: validation

Validate before calling

var email = principal.FindFirstValue(ClaimTypes.Email);
if (string.IsNullOrEmpty(email))
    return Challenge("OIDC token is missing the email claim.");

Type guard

bool HasOidcEmail(ClaimsPrincipal p) =>
    !string.IsNullOrWhiteSpace(p.FindFirstValue(ClaimTypes.Email));

Try / catch

try { var user = await oidcService.LoginOrCreate(Request, principal, ct); }
catch (KavitaException ex) when (ex.Message == "errors.oidc.missing-email")
{ return Challenge(); // request 'email' scope }

Prevention

When it happens

Trigger: OIDC login for a first-time user whose token has no email claim. The check fires only when no existing user matched the oidcId (new user path); returning users with valid oidcId bypass this entirely.

Common situations: The 'email' or 'profile' scope was not requested; provider doesn't expose email in tokens (some corporate IdPs); email claim mapped under a different name; user's profile in the IdP has no email set.

Related errors


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