Kareadita/Kavita · error · KavitaException

errors.oidc.email-not-verified

Error message

errors.oidc.email-not-verified

What it means

Thrown when settings.RequireVerifiedEmail is true and principal.HasVerifiedEmail() returns false. Kavita refuses to provision/link an account from an OIDC identity whose email is unverified, to prevent account takeover via an unverified email. Only evaluated on the new-user / email-match path, after the email is confirmed present.

Source

Thrown at Kavita.Services/OidcService.cs:92

        }

        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");
            }

            logger.LogDebug("User {UserName} has matched on email to {OidcId}", user.Id, oidcId);
            user.OidcId = oidcId;
            await unitOfWork.CommitAsync(ct);

            await SyncUserSettings(request, settings, principal, user);

View on GitHub (pinned to 9c3e540000)

Solutions

  1. Have the user verify their email in the identity provider, then re-login.
  2. If the IdP doesn't emit email_verified, either disable RequireVerifiedEmail or configure the IdP to include a true email_verified claim.
  3. Decode the IdToken to confirm the 'email_verified' claim value.
  4. Re-check the OIDC settings RequireVerifiedEmail toggle matches your security policy.
Defensive patterns

Strategy: validation

Validate before calling

if (settings.RequireVerifiedEmail && !principal.HasVerifiedEmail())
    return Challenge("Email not verified by identity provider.");

Type guard

bool EmailIsVerifiedForPolicy(ClaimsPrincipal p, OidcConfigDto s) =>
    !s.RequireVerifiedEmail || p.HasVerifiedEmail();

Try / catch

try { var user = await oidcService.LoginOrCreate(Request, principal, ct); }
catch (KavitaException ex) when (ex.Message == "errors.oidc.email-not-verified")
{ return Challenge("Please verify your email with your provider."); }

Prevention

When it happens

Trigger: First-time OIDC login (or email-match to an existing local account) where RequireVerifiedEmail=true in OIDC settings but the token lacks the 'email_verified' claim or it's false.

Common situations: Admin enabled RequireVerifiedEmail but the IdP doesn't issue the email_verified claim or marks it false; user's email in the IdP is pending verification; test/dev IdP that omits the claim.

Related errors


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