Kareadita/Kavita · warning · KavitaException

oidc-managed

oidc-managed

Error message

oidc-managed

What it means

Thrown as KavitaException with localized key 'oidc-managed' by AccountService.ChangeIdentityProvider when OIDC SyncUserSettings is enabled and an attempt is made to set a user's provider to OpenIdConnect while it is already OpenIdConnect — i.e. re-asserting OIDC on an already OIDC-managed user. Guards the sync path from no-op/loop writes.

Source

Thrown at Kavita.Services/AccountService.cs:145

                throw new KavitaException(await localizationService.TranslateAsync(actingUserId, "cannot-change-identity-provider-original-user"));
            }

            return false;
        }

        // Allow changes if users aren't being synced
        var oidcSettings = (await unitOfWork.SettingsRepository.GetSettingsDtoAsync(ct)).OidcConfig;
        if (!oidcSettings.SyncUserSettings)
        {
            user.IdentityProvider = identityProvider;
            await unitOfWork.CommitAsync(ct);
            return false;
        }

        // Don't allow changes to the user if they're managed by oidc, and their identity provider isn't being changed to something else
        if (user.IdentityProvider == IdentityProvider.OpenIdConnect && identityProvider == IdentityProvider.OpenIdConnect)
        {
            throw new KavitaException(await localizationService.TranslateAsync(actingUserId, "oidc-managed"));
        }

        user.IdentityProvider = identityProvider;
        await unitOfWork.CommitAsync(ct);

        return user.IdentityProvider == IdentityProvider.OpenIdConnect;
    }

    public async Task UpdateLibrariesForUser(AppUser user, IList<int> librariesIds, bool hasAdminRole, CancellationToken ct = default)
    {
        var allLibraries = (await unitOfWork.LibraryRepository.GetLibrariesAsync(LibraryIncludes.AppUser, ct: ct)).ToList();
        var currentLibrary = allLibraries.Where(l => l.AppUsers.Contains(user)).ToList();

        List<Library> libraries;
        if (hasAdminRole)
        {
            logger.LogDebug("{UserId} is admin. Granting access to all libraries", user.Id);
            libraries = allLibraries;

View on GitHub (pinned to 9c3e540000)

Solutions

  1. Skip the call (or pass a different provider) when the user is already OIDC-managed and SyncUserSettings is on.
  2. Check user.IdentityProvider before calling ChangeIdentityProvider to avoid a no-op that triggers the guard.
  3. Turn off SyncUserSettings only if you intentionally want to bypass OIDC-managed protection.

Example fix

// before
await accountService.ChangeIdentityProvider(actingUserId, user, IdentityProvider.OpenIdConnect, ct);

// after
if (user.IdentityProvider != IdentityProvider.OpenIdConnect)
    await accountService.ChangeIdentityProvider(actingUserId, user, IdentityProvider.OpenIdConnect, ct);
Defensive patterns

Strategy: validation

Validate before calling

if (user.IdentityProvider == IdentityProvider.OpenIdConnect
    && identityProvider == IdentityProvider.OpenIdConnect)
    return BadRequest("oidc-managed");

Type guard

static bool IsAlreadyOidcManaged(AppUser u, IdentityProvider target)
    => u.IdentityProvider == IdentityProvider.OpenIdConnect
       && target == IdentityProvider.OpenIdConnect;

Try / catch

try { await accountService.ChangeIdentityProvider(actingUserId, user, provider, ct); }
catch (KavitaException ex) { return BadRequest(ex.Message); }

Prevention

When it happens

Trigger: With SyncUserSettings on, ChangeIdentityProvider is called for a user whose IdentityProvider is already OpenIdConnect and the requested provider is also OpenIdConnect.

Common situations: A sync routine unconditionally re-applies the OIDC provider to all OIDC users; admin UI resubmits an unchanged provider selection; repeated OIDC login callbacks re-run the change.

Related errors


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