Kareadita/Kavita · critical · KavitaException
errors.oidc.no-account
Error message
errors.oidc.no-account
What it means
Thrown by OidcService.SyncUserSettings(CookieValidatePrincipalContext) when ParseIdToken returns null during a cookie-refresh token validation. ParseIdToken can return null when the OIDC configurationManager is uninitialized or when the id_token cannot be validated against the IdP discovery document (issuer/audience/signing-key mismatch). The KavitaException is immediately re-wrapped as UnauthorizedAccessException and bubbles to ExceptionMiddleware, which maps it to HTTP 401, logging out the user.
Source
Thrown at Kavita.Services/OidcService.cs:353
/// <summary>
/// Syncs the given user to the principal found in the id token
/// </summary>
/// <param name="ctx"></param>
/// <param name="settings"></param>
/// <param name="idToken"></param>
/// <param name="user"></param>
/// <exception cref="UnauthorizedAccessException">If syncing fails</exception>
private async Task SyncUserSettings(CookieValidatePrincipalContext ctx, OidcConfigDto settings, string idToken, AppUser user)
{
if (!settings.SyncUserSettings || user.IdentityProvider != IdentityProvider.OpenIdConnect) return;
try
{
var newPrincipal = await ParseIdToken(settings, idToken);
if (newPrincipal == null)
{
throw new KavitaException("errors.oidc.no-account");
}
await SyncUserSettings(ctx.HttpContext.Request, settings, newPrincipal, user);
}
catch (KavitaException ex)
{
logger.LogError(ex, "Failed to sync user after token refresh");
throw new UnauthorizedAccessException(ex.Message);
}
}
/// <summary>
/// Updates roles, library access and age rating restriction. Will not modify the default admin
/// </summary>
/// <param name="request"></param>
/// <param name="settings"></param>
/// <param name="claimsPrincipal"></param>
/// <param name="user"></param>
public async Task SyncUserSettings(HttpRequest request, OidcConfigDto settings, ClaimsPrincipal claimsPrincipal, AppUser user)View on GitHub (pinned to 9c3e540000)
Solutions
- Verify the OIDC ClientId/Audience in Kavita's OIDC settings exactly matches the client registration in your IdP (Keycloak, Authentik, Auth0, etc.).
- Force a discovery-document refresh: restart Kavita so configurationManager re-fetches the IdP's .well-known/openid-configuration with current signing keys.
- Check server time sync (NTP) — if Kavita's clock drifts beyond the token's clock-skew tolerance, validation fails silently and ParseIdToken returns null.
- Inspect Kavita logs for the preceding ParseIdToken/GetConfigurationAsync error; the null return masks the underlying JwtSecurityTokenHandler validation exception.
- Temporarily disable 'Sync User Settings' in OIDC config to confirm the token refresh path itself works, isolating the sync failure from the auth failure.
Example fix
// No code fix — this is a runtime configuration issue. // In appsettings / OIDC admin panel, ensure: // Authority = https://idp.example.com/realms/myrealm // ClientId = kavita (must match token 'aud' claim) // Scopes include 'openid' so an id_token is issued // If keys rotated, restart Kavita to refresh the discovery cache.
Defensive patterns
Strategy: validation
Validate before calling
// Before enabling 'Sync User Settings' in OIDC config,
// verify the IdP returns valid id_tokens by testing
// an initial login (which calls ParseIdToken via the
// ticket-received flow). If that succeeds, token refresh
// should also work. Monitor logs for ParseIdToken failures.
//
// Admin check script (conceptual):
// Ensure OIDC settings are valid before enabling sync:
// Assert.NotEmpty(settings.ClientId);
// Assert.NotEmpty(settings.Authority);
// Assert.NotEmpty(scopes && scopes.Contains("openid")); Try / catch
// This fires inside OIDC cookie middleware, not a controller.
// The KavitaException is caught locally and re-thrown as
// UnauthorizedAccessException, which ExceptionMiddleware
// maps to HTTP 401. To handle at the client level:
//
// if (response.StatusCode == HttpStatusCode.Unauthorized)
// {
// // Token refresh failed; re-authenticate the user
// await ReLoginAsync();
// } Prevention
- Keep the OIDC ClientId/Audience in Kavita exactly matching the IdP client registration.
- Ensure server time is NTP-synced to avoid token validation clock-skew failures.
- Restart Kavita after IdP signing-key rotation to refresh the discovery-document cache.
- Always include the 'openid' scope so id_tokens are issued.
- Test OIDC login end-to-end before enabling Sync User Settings.
When it happens
Trigger: A user with IdentityProvider == OpenIdConnect has a valid session cookie that is being refreshed. The OIDC IdP issues an id_token, but Kavita's ParseIdToken fails to validate it (signing key changed, audience mismatch, clock skew past validation window, or configurationManager is null). The null return from ParseIdToken triggers this throw.
Common situations: The IdP rotated its signing keys but Kavita's cached discovery document is stale. The OIDC ClientId in Kavita settings does not match the audience claim in the token. The server clock on Kavita is significantly off from the IdP. A misconfigured or partially initialized OIDC setup where configurationManager failed to load on startup.
Related errors
- User is not authenticated
- errors.oidc.no-account
- errors.oidc.missing-external-id
- errors.oidc.missing-email
- errors.oidc.email-not-verified
AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13).
Data as JSON: /api/errors/d5573654a78f935e.
Report an issue: GitHub.