{"record":{"id":"cd5ea3178d74e038","repo":"Kareadita/Kavita","slug":"oidc-managed","errorCode":"oidc-managed","errorMessage":"oidc-managed","messagePattern":"oidc-managed","errorType":"exception","errorClass":"KavitaException","httpStatus":400,"severity":"warning","filePath":"Kavita.Services/AccountService.cs","lineNumber":145,"sourceCode":"                throw new KavitaException(await localizationService.TranslateAsync(actingUserId, \"cannot-change-identity-provider-original-user\"));\n            }\n\n            return false;\n        }\n\n        // Allow changes if users aren't being synced\n        var oidcSettings = (await unitOfWork.SettingsRepository.GetSettingsDtoAsync(ct)).OidcConfig;\n        if (!oidcSettings.SyncUserSettings)\n        {\n            user.IdentityProvider = identityProvider;\n            await unitOfWork.CommitAsync(ct);\n            return false;\n        }\n\n        // Don't allow changes to the user if they're managed by oidc, and their identity provider isn't being changed to something else\n        if (user.IdentityProvider == IdentityProvider.OpenIdConnect && identityProvider == IdentityProvider.OpenIdConnect)\n        {\n            throw new KavitaException(await localizationService.TranslateAsync(actingUserId, \"oidc-managed\"));\n        }\n\n        user.IdentityProvider = identityProvider;\n        await unitOfWork.CommitAsync(ct);\n\n        return user.IdentityProvider == IdentityProvider.OpenIdConnect;\n    }\n\n    public async Task UpdateLibrariesForUser(AppUser user, IList<int> librariesIds, bool hasAdminRole, CancellationToken ct = default)\n    {\n        var allLibraries = (await unitOfWork.LibraryRepository.GetLibrariesAsync(LibraryIncludes.AppUser, ct: ct)).ToList();\n        var currentLibrary = allLibraries.Where(l => l.AppUsers.Contains(user)).ToList();\n\n        List<Library> libraries;\n        if (hasAdminRole)\n        {\n            logger.LogDebug(\"{UserId} is admin. Granting access to all libraries\", user.Id);\n            libraries = allLibraries;","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/Kareadita/Kavita/blob/9c3e5400007f8a0282f7d883f2ad5e71716e514d/Kavita.Services/AccountService.cs#L127-L163","documentation":"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.","triggerScenarios":"With SyncUserSettings on, ChangeIdentityProvider is called for a user whose IdentityProvider is already OpenIdConnect and the requested provider is also OpenIdConnect.","commonSituations":"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.","solutions":["Skip the call (or pass a different provider) when the user is already OIDC-managed and SyncUserSettings is on.","Check user.IdentityProvider before calling ChangeIdentityProvider to avoid a no-op that triggers the guard.","Turn off SyncUserSettings only if you intentionally want to bypass OIDC-managed protection."],"exampleFix":"// before\nawait accountService.ChangeIdentityProvider(actingUserId, user, IdentityProvider.OpenIdConnect, ct);\n\n// after\nif (user.IdentityProvider != IdentityProvider.OpenIdConnect)\n    await accountService.ChangeIdentityProvider(actingUserId, user, IdentityProvider.OpenIdConnect, ct);","handlingStrategy":"validation","validationCode":"if (user.IdentityProvider == IdentityProvider.OpenIdConnect\n    && identityProvider == IdentityProvider.OpenIdConnect)\n    return BadRequest(\"oidc-managed\");","typeGuard":"static bool IsAlreadyOidcManaged(AppUser u, IdentityProvider target)\n    => u.IdentityProvider == IdentityProvider.OpenIdConnect\n       && target == IdentityProvider.OpenIdConnect;","tryCatchPattern":"try { await accountService.ChangeIdentityProvider(actingUserId, user, provider, ct); }\ncatch (KavitaException ex) { return BadRequest(ex.Message); }","preventionTips":["Skip the call when the user is already OIDC-managed and SyncUserSettings is on.","Check user.IdentityProvider before calling ChangeIdentityProvider.","Have sync routines skip no-op provider reassignments."],"tags":["oidc","account","identity-provider","sync","config"],"backgroundTag":null,"analyzedSha":"9c3e5400007f8a0282f7d883f2ad5e71716e514d","analyzedAt":"2026-08-13T19:06:05.897Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}