Skip to content

feat: simultaneous multiple auth methods#2690

Closed
afonsojramos wants to merge 19 commits intoseerr-team:developfrom
afonsojramos:multi-media-server-auth
Closed

feat: simultaneous multiple auth methods#2690
afonsojramos wants to merge 19 commits intoseerr-team:developfrom
afonsojramos:multi-media-server-auth

Conversation

@afonsojramos
Copy link
Copy Markdown

@afonsojramos afonsojramos commented Mar 13, 2026

Description

Fixes #100

So, first time contributor, but contributing towards a feature that I've wanted for a loooooong time, and looking at the issue number I am addressing, I don't think I was the only one.

I went with the approach of adding primaryMediaServer + enabledAuthMethods[] alongside the existing fields rather than a full media server registry because it would be the minimal change needed — no major database schema changes, no new entities, and the existing plexId/jellyfinUserId columns on the User model already support multi-server identity out of the box. Users have a single identity regardless of which method they authenticate with, and email collisions across auth providers are rejected (login is denied if the email is already associated with a different auth method). The login page reuses the existing collapsible button pattern from the original codebase, showing one form at a time with branded toggle buttons for alternatives, keeping Plex as always-a-button since it's just an OAuth redirect. The setup wizard is intentionally left unchanged (this only applies to already-configured instances), and a settings migration handles the rename from mediaServerTypeprimaryMediaServer and newPlexLoginnewUserLogin so existing configurations migrate seamlessly. Multi-library support across servers was explicitly scoped out as a separate concern.

This PR is purely about allowing users to sign in with multiple auth providers simultaneously, instead of needing multiple instances per server (since there are multiple cases of people who have both Jellyfin and Plex for the same media).

Changes

  • Replace single mediaServerType with primaryMediaServer + enabledAuthMethods[] array to support simultaneous Plex, Jellyfin, and Emby authentication
  • Add settings migration (0009_multi_auth) to seamlessly migrate existing configurations
  • Refactor login page to show one form at a time with collapsible branded buttons for alternative methods (matching existing UX patterns)
  • Add per-method checkboxes in Settings > Users for enabling/disabling individual auth methods
  • Show both Plex and Jellyfin/Emby tabs in Settings when both are configured
  • Add Plex settings endpoints (/plex/auth, /plex/status) for secondary Plex configuration
  • Handle email collision across auth providers (login rejected if email belongs to a different auth method)

Key Design Decisions

  • Single identity: Users have one account regardless of which auth method they use
  • Local login as default: When enabled, local login form is shown first; alternatives are collapsed buttons
  • Setup wizard unchanged: Only applies to existing configured instances
  • Backwards compatible: Migration preserves existing auth configuration exactly

How Has This Been Tested?

  • Verified Plex-primary + Jellyfin-secondary login page
  • Verified Jellyfin-primary + Plex-secondary login page
  • Verified collapsible form switching between local/Jellyfin/Plex
  • Verified Settings > Users shows correct auth method checkboxes
  • Verified Settings tabs show both Plex and Jellyfin when configured
  • Updated cypress test fixtures and OpenAPI spec

Screenshots / Logs

image

AI Assistance Disclosure

This PR was developed with Claude Code as a coding assistant. I directed all design decisions: the approach (adding enabledAuthMethods[] vs. a full registry), scope boundaries (auth-only, no multi-library), email collision strategy, and UX pattern (collapsible login matching the existing codebase). Claude Code generated code under my direction and I reviewed each change iteratively, testing locally against multiple configurations (Plex-primary, Jellyfin-primary, both enabled) before submitting. I understand the code produced and can answer questions about any part of it.

Checklist

  • I have read and followed the contribution guidelines.
  • Disclosed any use of AI (see our policy) — see disclosure above
  • I have updated the documentation accordingly.
  • All new and existing tests passed.
  • Successful build pnpm build
  • Translation keys pnpm i18n:extract
  • Database migration (if required) — settings migration 0009_multi_auth.ts (no DB schema change)

Summary by CodeRabbit

  • New Features

    • Multi-auth support: enable/configure Plex, Jellyfin, and Emby independently; new persisted enabledAuthMethods and primaryMediaServer setting.
    • New Plex endpoints to authenticate and check Plex connection status.
  • Improvements

    • Redesigned login UI with selectable auth methods and per-method toggles.
    • Settings UI now exposes granular auth controls and server name display.
  • Chores

    • Migration added to transition existing settings to multi-auth format.

Rename mediaServerType→primaryMediaServer, mediaServerLogin→enabledAuthMethods,
newPlexLogin→newUserLogin. Add settings migration 0009. Add adminToken to PlexSettings.
Add isAuthMethodEnabled helper. No behavioral changes — field renames only.
Replace mutual exclusion auth guards with enabledAuthMethods.includes() checks.
Add Plex admin token fallback for secondary Plex auth. Fix logout device cleanup
to use user.userType. Add email collision rejection across auth methods.
Replace binary media server / local toggle with stacked layout showing all
enabled auth methods simultaneously. Primary server auth on top, secondary
below, local login at bottom with "or" dividers.
- Show both Plex and Jellyfin/Emby tabs when multiple servers are configured
- Replace single media server login toggle with individual checkboxes per auth method
- Add Plex admin token fallback for secondary server configuration
- Add POST /settings/plex/auth endpoint for secondary Plex OAuth
- Add GET /settings/plex/status endpoint to check Plex connection
- Update i18n keys for new auth method messages
@afonsojramos afonsojramos requested a review from a team as a code owner March 13, 2026 14:57
Copilot AI review requested due to automatic review settings March 13, 2026 14:57
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 13, 2026

📝 Walkthrough

Walkthrough

Renames global media server fields to a primary server and adds multi-auth support: mediaServerTypeprimaryMediaServer, mediaServerLoginenabledAuthMethods: number[], newPlexLoginnewUserLogin. Backend, migration, routes, job logic, and many frontend components updated to use the new fields and per-method gating.

Changes

Cohort / File(s) Summary
Config & API Specs
cypress/config/settings.cypress.json, seerr-api.yml, src/i18n/locale/en.json
Rename settings keys in test/config and API spec; add enabledAuthMethods and localization keys for per-method login UI.
Public Types / Context
server/interfaces/api/settingsInterfaces.ts, src/context/SettingsContext.tsx, src/pages/_app.tsx
Update public settings types and default client/server payload to include primaryMediaServer, enabledAuthMethods, newUserLogin.
Settings Core & Migration
server/lib/settings/index.ts, server/lib/settings/migrations/0009_multi_auth.ts
Introduce enabledAuthMethods, newUserLogin, primaryMediaServer, server name fields, adminToken, isAuthMethodEnabled() and migration 0009_multi_auth to seed/rename fields.
Auth & Settings Routes
server/routes/auth.ts, server/routes/settings/index.ts, server/routes/avatarproxy.ts
Refactor login/link/unlink/logout flows to use primaryMediaServer and isAuthMethodEnabled(); centralize Plex token via getPlexToken; add POST /settings/plex/auth and GET /settings/plex/status.
User Routes & Import
server/routes/user/index.ts, server/routes/user/usersettings.ts, src/components/UserList/*, src/components/UserProfile/*
Adjust user import/linking flows and userType resolution to prefer primaryMediaServer and check enabled auth methods; Plex token fallbacks updated.
Backend Logic & Jobs
server/api/jellyfin.ts, server/entity/Media.ts, server/lib/*, server/job/schedule.ts, server/lib/scanners/jellyfin/index.ts, server/lib/overseerrMerge.ts
Swap references from mediaServerType to primaryMediaServer, accept legacy field in migrations, and propagate new field through URL generation, sync, scanners, and scheduling.
Settings UI
src/components/Settings/SettingsUsers/index.tsx, src/components/Settings/SettingsLayout.tsx, src/components/Settings/SettingsJellyfin.tsx, src/components/Settings/SettingsJobsCache/index.tsx
Add per-method toggles (plex/jellyfin/emby), newUserLogin option, validate and submit enabledAuthMethods; dynamic tabs and labels based on primaryMediaServer and enabled methods.
Login UI & Flows
src/components/Login/index.tsx, src/components/Login/JellyfinLogin.tsx
Replace Plex-centric login UI with multi-method selectable flow; use primaryMediaServer and newUserLogin for conditional rendering and behavior.
Widespread Frontend Adjustments
many components under src/components/... (e.g., ExternalLinkBlock, IssueDetails, MovieDetails, TvDetails, StatusBadge, ManageSlideOver, UserList, PermissionEdit, etc.)
Replace checks of mediaServerType/mediaServerLogin with primaryMediaServer or isAuthMethodEnabled() across numerous render paths and labels to reflect renamed settings and multi-auth behavior.
Client Utilities
src/hooks/useDeepLinks.ts
Deep link logic adjusted to rely on primaryMediaServer for iOS Plex URLs.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Client
    participant AuthAPI
    participant Settings as SettingsSvc
    participant DB
    participant PlexAPI

    User->>Client: Select auth method & submit credentials
    Client->>AuthAPI: POST /auth/login {method, creds}
    AuthAPI->>SettingsSvc: isAuthMethodEnabled(method)?
    SettingsSvc->>DB: read enabledAuthMethods & primaryMediaServer
    DB-->>SettingsSvc: enabledAuthMethods, primaryMediaServer
    SettingsSvc-->>AuthAPI: allowed / not allowed

    alt allowed
        AuthAPI->>AuthAPI: route to method handler
        alt Plex
            AuthAPI->>SettingsSvc: getPlexToken()
            SettingsSvc-->>AuthAPI: admin or user token
            AuthAPI->>PlexAPI: validate token/credentials
            PlexAPI-->>AuthAPI: validation result
        else Jellyfin/Emby
            AuthAPI->>AuthAPI: validate via Jellyfin/Emby handler
        end
        AuthAPI->>DB: create/update user (userType from method/primaryMediaServer)
        DB-->>AuthAPI: user saved
        AuthAPI-->>Client: 200 OK + session
    else not allowed
        AuthAPI-->>Client: 403 Auth method disabled
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • M0NsTeRRR
  • 0xSysR3ll

Poem

🐰 I hopped through files with nimble paws,

Swapped a flag and planted auths in rows.
Plex, Jellyfin, Emby — all may play,
NewUserLogin lights the way.
A migration nibble, a cheerful thump — hooray!

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: simultaneous multiple auth methods' clearly summarizes the main change—implementing support for concurrent authentication via multiple platforms (Plex, Jellyfin, Emby) instead of a single provider.
Linked Issues check ✅ Passed The PR fulfills issue #100's primary objective to enable simultaneous authentication with multiple platforms. Core requirements met: users can now authenticate via Plex, Jellyfin, or Emby concurrently; login UI presents multiple methods; auth methods are now interchangeable through enabledAuthMethods configuration.
Out of Scope Changes check ✅ Passed All code changes directly support the simultaneous multi-auth feature. Changes include: configuration restructuring (primaryMediaServer + enabledAuthMethods), login flow refactoring, UI updates for per-method toggles, Plex admin token endpoints, and settings migration—all scoped to implementing multi-provider auth support.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can make CodeRabbit's review stricter and more nitpicky using the `assertive` profile, if that's what you prefer.

Change the reviews.profile setting to assertive to make CodeRabbit's nitpick more issues in your PRs.

@afonsojramos afonsojramos changed the base branch from main to develop March 13, 2026 14:58
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors Seerr’s authentication configuration to support multiple simultaneous auth providers (Plex + Jellyfin/Emby + local), including settings migration and UI updates, while also bundling several unrelated improvements (notifications, trending filters, tests, i18n, etc.).

Changes:

  • Introduces primaryMediaServer + enabledAuthMethods[] (replacing mediaServerType + mediaServerLogin) and adds a settings migration (0009_multi_auth).
  • Updates login/settings UI to handle multiple auth methods and adds Plex secondary-token endpoints (/settings/plex/auth, /settings/plex/status).
  • Adds unit test runner plumbing (node:test) + DB seeding utilities, plus various notification/trending/i18n enhancements.

Reviewed changes

Copilot reviewed 39 out of 39 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
server/routes/auth.ts Switches auth enablement checks to enabledAuthMethods and adds email-collision logic (but currently misses setup initialization for enabledAuthMethods).
server/lib/settings/migrations/0009_multi_auth.ts Migrates legacy settings to the new multi-auth shape.
src/components/Login/index.tsx Refactors login page to show one method at a time with collapsible alternatives.
src/components/Settings/SettingsUsers/index.tsx Adds per-method toggles backed by enabledAuthMethods (but currently renders Jellyfin/Emby as a single combined toggle).
server/routes/settings/index.ts Adds Plex token helper + new /plex/auth and /plex/status endpoints.
server/routes/discover.ts Adds mediaType + timeWindow query support for trending.
server/test/*, server/utils/seedTestDb.ts, server/datasource.ts Adds node:test runner integration and deterministic test DB seeding.
server/lib/notifications/*, src/components/Settings/Notifications/*, docs Adds webhook custom headers + ntfy priority support (plus documentation updates).
src/pages/_error.tsx + various components Renames default error export/import to ErrorPage to avoid Error collisions.
Comments suppressed due to low confidence (1)

server/routes/user/usersettings.ts:384

  • Linked-account routes still gate Jellyfin/Emby linking on primaryMediaServer being Jellyfin/Emby. With multi-auth, Jellyfin/Emby could be enabled even when not primary, so this should be based on the enabled auth methods (e.g., settings.isAuthMethodEnabled(MediaServerType.JELLYFIN/EMBY)) rather than primaryMediaServer.
    // Make sure jellyfin login is enabled
    if (
      settings.main.primaryMediaServer !== MediaServerType.JELLYFIN &&
      settings.main.primaryMediaServer !== MediaServerType.EMBY
    ) {
      return res
        .status(500)
        .json({ message: 'Jellyfin/Emby login is disabled' });
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread server/routes/auth.ts
Comment thread server/routes/auth.ts
Comment thread server/routes/auth.ts
Comment thread server/routes/user/usersettings.ts Outdated
Comment thread src/components/Settings/SettingsUsers/index.tsx
Comment thread server/routes/settings/index.ts
@afonsojramos
Copy link
Copy Markdown
Author

I was not expecting two people to be working on the same feature at the same time 😅
As I took quite a few design decisions differently to #2689, do let me know if this PR still serves a purpose @M0NsTeRRR

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/components/TvDetails/index.tsx (1)

343-351: ⚠️ Potential issue | 🟡 Minor

4K play label is incorrect for Emby/Jellyfin branches

getAvailable4kMediaServerName() uses messages.play (non-4K) on Line 343 and Line 351. This drops the “4K” label for those providers.

💡 Suggested fix
   function getAvailable4kMediaServerName() {
     if (settings.currentSettings.primaryMediaServer === MediaServerType.EMBY) {
-      return intl.formatMessage(messages.play, { mediaServerName: 'Emby' });
+      return intl.formatMessage(messages.play4k, { mediaServerName: 'Emby' });
     }

     if (settings.currentSettings.primaryMediaServer === MediaServerType.PLEX) {
       return intl.formatMessage(messages.play4k, { mediaServerName: 'Plex' });
     }

-    return intl.formatMessage(messages.play, { mediaServerName: 'Jellyfin' });
+    return intl.formatMessage(messages.play4k, { mediaServerName: 'Jellyfin' });
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/TvDetails/index.tsx` around lines 343 - 351, The
getAvailable4kMediaServerName() implementation currently uses messages.play
(non-4K) for the Emby and Jellyfin branches; update those branches so they use
messages.play4k instead of messages.play. Specifically, change the branch that
checks settings.currentSettings.primaryMediaServer === MediaServerType.EMBY to
call intl.formatMessage(messages.play4k, { mediaServerName: 'Emby' }) and change
the final return (Jellyfin) to intl.formatMessage(messages.play4k, {
mediaServerName: 'Jellyfin' }), leaving the Plex branch as-is.
src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/LinkJellyfinModal.tsx (1)

70-70: ⚠️ Potential issue | 🟡 Minor

Typo in Transition enterTo prop.

opacuty-100 should be opacity-100. This typo will prevent the enter transition from working correctly.

✏️ Proposed fix
-        enterTo="opacuty-100"
+        enterTo="opacity-100"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/LinkJellyfinModal.tsx`
at line 70, The Transition prop in LinkJellyfinModal has a typo: the enterTo
value uses "opacuty-100" which prevents the enter transition; update the prop in
the LinkJellyfinModal component (the Transition element's enterTo prop) to
"opacity-100" so the CSS class is correct and the enter animation works as
intended.
src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/index.tsx (1)

116-142: ⚠️ Potential issue | 🟠 Major

Linking options may be too restrictive for multi-auth scenarios.

The hide conditions only check primaryMediaServer, but with multi-auth support, users should be able to link accounts for any enabled auth method, not just the primary one. For example, if Plex is configured as a secondary auth method via enabledAuthMethods, users won't see the option to link their Plex account.

Consider also checking enabledAuthMethods:

🐛 Proposed fix
   const linkable = [
     {
       name: 'Plex',
       action: () => {
         plexOAuth.preparePopup();
         setTimeout(() => linkPlexAccount(), 1500);
       },
       hide:
-        settings.currentSettings.primaryMediaServer !== MediaServerType.PLEX ||
+        (settings.currentSettings.primaryMediaServer !== MediaServerType.PLEX &&
+          !settings.currentSettings.enabledAuthMethods?.includes(MediaServerType.PLEX)) ||
         accounts.some((a) => a.type === LinkedAccountType.Plex),
     },
     {
       name: 'Jellyfin',
       action: () => setShowJellyfinModal(true),
       hide:
-        settings.currentSettings.primaryMediaServer !==
-          MediaServerType.JELLYFIN ||
+        (settings.currentSettings.primaryMediaServer !== MediaServerType.JELLYFIN &&
+          !settings.currentSettings.enabledAuthMethods?.includes(MediaServerType.JELLYFIN)) ||
         accounts.some((a) => a.type === LinkedAccountType.Jellyfin),
     },
     {
       name: 'Emby',
       action: () => setShowJellyfinModal(true),
       hide:
-        settings.currentSettings.primaryMediaServer !== MediaServerType.EMBY ||
+        (settings.currentSettings.primaryMediaServer !== MediaServerType.EMBY &&
+          !settings.currentSettings.enabledAuthMethods?.includes(MediaServerType.EMBY)) ||
         accounts.some((a) => a.type === LinkedAccountType.Emby),
     },
   ].filter((l) => !l.hide);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/index.tsx`
around lines 116 - 142, The link option visibility logic in the linkable array
is too restrictive because hide only checks
settings.currentSettings.primaryMediaServer; update each hide predicate in the
linkable entries (for Plex, Jellyfin, Emby) to also allow linking when the
corresponding auth method is enabled in
settings.currentSettings.enabledAuthMethods (e.g., treat a linkable item as
visible if primaryMediaServer === MediaServerType.PLEX OR enabledAuthMethods
includes the Plex auth constant), while keeping the existing accounts.some(...)
check for already-linked accounts; adjust the conditions for
LinkedAccountType.Plex, LinkedAccountType.Jellyfin and LinkedAccountType.Emby
respectively in the linkable array.
🧹 Nitpick comments (1)
server/routes/auth.ts (1)

263-270: Error message doesn't mention Emby.

The error message says "Jellyfin login is disabled" but this route handles both Jellyfin and Emby. Consider a more accurate message.

✏️ Suggested improvement
   if (
     settings.main.primaryMediaServer !== MediaServerType.NOT_CONFIGURED &&
     !settings.isAuthMethodEnabled(MediaServerType.JELLYFIN) &&
     !settings.isAuthMethodEnabled(MediaServerType.EMBY)
   ) {
-    return res.status(500).json({ error: 'Jellyfin login is disabled' });
+    return res.status(500).json({ error: 'Jellyfin/Emby login is disabled' });
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/routes/auth.ts` around lines 263 - 270, The error response is
misleading for Emby; update the check in the auth route so the returned error
message covers both servers: when primaryMediaServer is configured but neither
settings.isAuthMethodEnabled(MediaServerType.JELLYFIN) nor
settings.isAuthMethodEnabled(MediaServerType.EMBY) is true, change the
res.status(500).json call in the auth route to a message that mentions both
Jellyfin and Emby (or use a generic "media server login is disabled") and keep
the same status and structure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@seerr-api.yml`:
- Around line 245-252: Update the OpenAPI schema for primaryMediaServer and
enabledAuthMethods to restrict them to the MediaServerType enum and make
enabledAuthMethods unique: replace the free number type for primaryMediaServer
with an enum containing the exact numeric values from the MediaServerType
definition (so requests can only pick supported media servers) and change
enabledAuthMethods from a plain numeric array to an array whose items use that
same MediaServerType enum and include uniqueItems: true so duplicate/unsupported
auth-method IDs are rejected at the API boundary; locate the settings schema
definitions for primaryMediaServer and enabledAuthMethods in seerr-api.yml and
apply these changes.

In `@server/lib/overseerrMerge.ts`:
- Around line 12-14: The migration gate only checks
settings.main.primaryMediaServer, so legacy configs that only have
settings.main.mediaServerType can slip through and trigger the Overseerr merge;
update the gate in overseerrMerge.ts to also consider the legacy field by
returning false when either settings.main.primaryMediaServer or
settings.main.mediaServerType is present (i.e., change the condition around the
existing if that references settings.main.primaryMediaServer to include
settings.main.mediaServerType).

In `@server/routes/auth.ts`:
- Around line 102-103: The code currently instantiates PlexTvAPI with a possibly
empty plexToken (derived from mainUser.plexToken || settings.plex.adminToken ||
''), which can lead to misleading behavior when checkUserAccess() simply returns
false; before creating new PlexTvAPI or calling checkUserAccess(), explicitly
validate that at least one token exists (check mainUser.plexToken and
settings.plex.adminToken), and if neither is present return the intended 403
response (or log a clear error) rather than proceeding to instantiate PlexTvAPI;
update the block around plexToken / new PlexTvAPI(...) and the subsequent call
to PlexTvAPI.checkUserAccess to short-circuit when token is empty.

In `@server/routes/settings/index.ts`:
- Around line 266-328: The OpenAPI spec is missing operations for the new
endpoints settingsRoutes.post('/plex/auth') and
settingsRoutes.get('/plex/status'), which breaks validation and documentation;
update seerr-api.yml to add a POST /settings/plex/auth operation (request body
with authToken, 200 response schema with username/email, and 400/500 responses)
and a GET /settings/plex/status operation (200 response schema covering
configured, serverName, username, and connectionError cases), ensuring
operationIds/method names align with existing patterns and any security
requirements match other settings routes so the upstream OpenAPI validator
accepts requests to Plex auth/status.
- Around line 66-74: The token precedence in getPlexToken is reversed: change
the return logic in getPlexToken so it prefers the stored settings token first
(settings.plex.adminToken) and falls back to the user's token (admin.plexToken)
only if the settings value is empty, returning '' as final fallback; update the
return expression that currently reads admin.plexToken ||
settings.plex.adminToken || '' to settings.plex.adminToken || admin.plexToken ||
'' (ensure you reference getPlexToken, admin.plexToken and
settings.plex.adminToken).

In `@server/routes/user/index.ts`:
- Around line 595-601: The code constructs plexToken from mainUser.plexToken and
settings.plex.adminToken and then instantiates PlexTvAPI with it; add validation
to ensure plexToken is non-empty before creating PlexTvAPI (check the computed
plexToken variable after the fallback), and if missing throw or return a clear
error/log (mentioning where: mainUser, settings.plex.adminToken) so PlexTvAPI is
not called with an empty string; ensure the error path uses the same control
flow as surrounding logic (e.g., throw an Error or call next/error handler) so
callers see a meaningful message.
- Around line 714-716: The userType assignment currently relies solely on
settings.isAuthMethodEnabled(MediaServerType.EMBY) which will pick EMBy when
both are enabled; change the logic in the user creation path (the code assigning
userType) to first inspect primaryMediaServer (or equivalent property on the
import/source object) and set UserType based on that (e.g., if
primaryMediaServer === MediaServerType.JELLYFIN then UserType.JELLYFIN, else if
=== MediaServerType.EMBY then UserType.EMBY), and only fall back to
settings.isAuthMethodEnabled(MediaServerType.EMBY) ? UserType.EMBY :
UserType.JELLYFIN when primaryMediaServer is not present; update references
around userType, settings.isAuthMethodEnabled, MediaServerType.EMBY,
UserType.EMBY and UserType.JELLYFIN accordingly.

In `@server/routes/user/usersettings.ts`:
- Around line 279-281: The current checks in the Plex, Jellyfin, and Emby
account link/unlink routes only allow operations when
settings.main.primaryMediaServer equals the specific MediaServerType (e.g.,
MediaServerType.PLEX); update these to also allow the operation when the
corresponding server is present in settings.main.enabledAuthMethods (e.g.,
settings.main.enabledAuthMethods.includes(MediaServerType.PLEX)). Concretely,
replace the primary-only guard used in the Plex route (where
settings.main.primaryMediaServer !== MediaServerType.PLEX) and the similar
guards for Jellyfin/Emby with a condition that permits the request if either
settings.main.primaryMediaServer === <TYPE> OR
settings.main.enabledAuthMethods?.includes(<TYPE>); apply this pattern to the
Plex link/unlink handlers and the Jellyfin/Emby checks (referencing
MediaServerType.PLEX, MediaServerType.JELLYFIN, MediaServerType.EMBY and
settings.main.enabledAuthMethods) so the API matches the client-side
availability logic.

In `@src/components/Login/index.tsx`:
- Around line 72-76: The defaultMethod logic can fall back to 'plex' even when
no Plex UI exists; change it to pick the first actually enabled method or
surface a misconfiguration state: compute defaultMethod from formMethods[0] if
present, otherwise use 'local' only when localLogin is true, and if neither is
available set activeMethod to a sentinel (e.g., undefined or 'none') and update
the render path to show an explicit misconfiguration message; update the
identifiers defaultMethod, formMethods, localLogin, activeMethod and
setActiveMethod accordingly so the component renders the first enabled auth
method or a clear error instead of an empty card.
- Around line 19-20: The import statement only pulls hooks so the React
namespace isn't in scope, causing unsafe references to React types; update the
imports to include the React namespace so types like React.FC and React.SVGProps
are available — e.g., import React along with useEffect/useState (so references
in the Login component where React.FC and any SVG prop types are used are
valid). Ensure the top-level import brings in React (the namespace) while
keeping existing hook imports.

In `@src/components/Settings/SettingsJellyfin.tsx`:
- Around line 257-267: The mediaServerName in mediaServerFormatValues can be
undefined when neither Jellyfin nor Emby is selected; update the assignment in
SettingsJellyfin to provide a safe fallback (e.g., use the value of
primaryMediaServer or a generic string like "Media Server") so UI messages that
consume mediaServerFormatValues always receive a string; change the logic that
computes mediaServerName (referencing
settings.currentSettings.primaryMediaServer,
settings.currentSettings.enabledAuthMethods, MediaServerType, and
mediaServerFormatValues) to return Jellyfin/Emby when matched and otherwise fall
back to the chosen primaryMediaServer or a default label.

In `@src/components/Settings/SettingsLayout.tsx`:
- Around line 119-130: The getAvailableMediaServerName function can return
undefined when primaryMediaServer is Plex and both jellyfinConfigured and
embyConfigured are true; update getAvailableMediaServerName to handle the
both-enabled case explicitly (e.g., prefer one server by priority, return a
combined label like "Jellyfin / Emby", or a safe fallback string such as "Media
Server") so mediaServerName is never undefined before calling
intl.formatMessage; refer to getAvailableMediaServerName, primaryMediaServer,
jellyfinConfigured, embyConfigured, and MediaServerType to implement the
explicit branching and ensure the formatted message always receives a concrete
mediaServerName value.

In
`@src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/LinkJellyfinModal.tsx`:
- Around line 59-62: The modal title uses
settings.currentSettings.primaryMediaServer to compute mediaServerName, which
can be incorrect when the user explicitly selects an account type from the
dropdown; update the LinkJellyfinModal component to accept a prop (e.g.,
selectedMediaServer or mediaServerType) and use that prop instead of
settings.currentSettings.primaryMediaServer when determining mediaServerName
(and anywhere else the modal needs the selected server type); update the parent
that opens the modal to pass the user-selected value (matching MediaServerType
enum) into LinkJellyfinModal so the title and behavior reflect the user’s
dropdown choice.

---

Outside diff comments:
In `@src/components/TvDetails/index.tsx`:
- Around line 343-351: The getAvailable4kMediaServerName() implementation
currently uses messages.play (non-4K) for the Emby and Jellyfin branches; update
those branches so they use messages.play4k instead of messages.play.
Specifically, change the branch that checks
settings.currentSettings.primaryMediaServer === MediaServerType.EMBY to call
intl.formatMessage(messages.play4k, { mediaServerName: 'Emby' }) and change the
final return (Jellyfin) to intl.formatMessage(messages.play4k, {
mediaServerName: 'Jellyfin' }), leaving the Plex branch as-is.

In
`@src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/index.tsx`:
- Around line 116-142: The link option visibility logic in the linkable array is
too restrictive because hide only checks
settings.currentSettings.primaryMediaServer; update each hide predicate in the
linkable entries (for Plex, Jellyfin, Emby) to also allow linking when the
corresponding auth method is enabled in
settings.currentSettings.enabledAuthMethods (e.g., treat a linkable item as
visible if primaryMediaServer === MediaServerType.PLEX OR enabledAuthMethods
includes the Plex auth constant), while keeping the existing accounts.some(...)
check for already-linked accounts; adjust the conditions for
LinkedAccountType.Plex, LinkedAccountType.Jellyfin and LinkedAccountType.Emby
respectively in the linkable array.

In
`@src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/LinkJellyfinModal.tsx`:
- Line 70: The Transition prop in LinkJellyfinModal has a typo: the enterTo
value uses "opacuty-100" which prevents the enter transition; update the prop in
the LinkJellyfinModal component (the Transition element's enterTo prop) to
"opacity-100" so the CSS class is correct and the enter animation works as
intended.

---

Nitpick comments:
In `@server/routes/auth.ts`:
- Around line 263-270: The error response is misleading for Emby; update the
check in the auth route so the returned error message covers both servers: when
primaryMediaServer is configured but neither
settings.isAuthMethodEnabled(MediaServerType.JELLYFIN) nor
settings.isAuthMethodEnabled(MediaServerType.EMBY) is true, change the
res.status(500).json call in the auth route to a message that mentions both
Jellyfin and Emby (or use a generic "media server login is disabled") and keep
the same status and structure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 572ef217-18da-4117-af11-c262b650b162

📥 Commits

Reviewing files that changed from the base of the PR and between f40323c and 5cc9e05.

📒 Files selected for processing (39)
  • cypress/config/settings.cypress.json
  • seerr-api.yml
  • server/api/jellyfin.ts
  • server/entity/Media.ts
  • server/interfaces/api/settingsInterfaces.ts
  • server/job/schedule.ts
  • server/lib/availabilitySync.ts
  • server/lib/overseerrMerge.ts
  • server/lib/scanners/jellyfin/index.ts
  • server/lib/settings/index.ts
  • server/lib/settings/migrations/0009_multi_auth.ts
  • server/routes/auth.ts
  • server/routes/avatarproxy.ts
  • server/routes/settings/index.ts
  • server/routes/user/index.ts
  • server/routes/user/usersettings.ts
  • src/components/ExternalLinkBlock/index.tsx
  • src/components/IssueDetails/index.tsx
  • src/components/Login/JellyfinLogin.tsx
  • src/components/Login/index.tsx
  • src/components/ManageSlideOver/index.tsx
  • src/components/MovieDetails/index.tsx
  • src/components/PermissionEdit/index.tsx
  • src/components/Settings/SettingsJellyfin.tsx
  • src/components/Settings/SettingsJobsCache/index.tsx
  • src/components/Settings/SettingsLayout.tsx
  • src/components/Settings/SettingsUsers/index.tsx
  • src/components/Setup/index.tsx
  • src/components/StatusBadge/index.tsx
  • src/components/TvDetails/index.tsx
  • src/components/UserList/JellyfinImportModal.tsx
  • src/components/UserList/PlexImportModal.tsx
  • src/components/UserList/index.tsx
  • src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/LinkJellyfinModal.tsx
  • src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/index.tsx
  • src/context/SettingsContext.tsx
  • src/hooks/useDeepLinks.ts
  • src/i18n/locale/en.json
  • src/pages/_app.tsx

Comment thread seerr-api.yml
Comment thread server/lib/overseerrMerge.ts Outdated
Comment thread server/routes/auth.ts
Comment thread server/routes/settings/index.ts
Comment thread server/routes/settings/index.ts
Comment thread src/components/Login/index.tsx
Comment thread src/components/Login/index.tsx
Comment thread src/components/Settings/SettingsJellyfin.tsx
Comment thread src/components/Settings/SettingsLayout.tsx
@KakatkarAkshay
Copy link
Copy Markdown

I was not expecting two people to be working on the same feature at the same time 😅 As I took quite a few design decisions differently to #2689, do let me know if this PR still serves a purpose @M0NsTeRRR

And that too on the same day lol. Mine is very much rough right now. I mainly opened the PR here to get code reviews from coderabbit. Don't know why github isn't letting me convert the PR into draft.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
server/routes/user/index.ts (1)

595-601: ⚠️ Potential issue | 🟡 Minor

Empty Plex token fallback may cause API failure.

If both mainUser.plexToken and settings.plex.adminToken are empty/undefined, plexToken becomes an empty string. The subsequent PlexTvAPI call at line 601 will fail without a meaningful error message to the caller.

🛡️ Proposed fix to validate token before API call
       // Use admin's Plex token, falling back to secondary Plex admin token
       const mainUser = await userRepository.findOneOrFail({
         select: { id: true, plexToken: true },
         where: { id: 1 },
       });
       const plexToken = mainUser.plexToken || settings.plex.adminToken || '';
+      if (!plexToken) {
+        return next({
+          status: 400,
+          message: 'No Plex authentication token configured.',
+        });
+      }
       const mainPlexTv = new PlexTvAPI(plexToken);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/routes/user/index.ts` around lines 595 - 601, The code constructs
PlexTvAPI with a potentially empty token (plexToken) derived from
mainUser.plexToken or settings.plex.adminToken; validate that plexToken is
non-empty before calling new PlexTvAPI (e.g., in the same block that uses
userRepository.findOneOrFail and before instantiating PlexTvAPI) and if it's
empty throw or return a clear error (including context like "missing Plex admin
token" and the affected user id) so the caller receives a meaningful message
instead of a downstream API failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/routes/auth.ts`:
- Around line 444-451: The code is resolving ServerType using
settings.main.primaryMediaServer (via MediaServerType) which misclassifies users
for multi-primary setups; change all such ternaries that produce ServerType (the
expressions using settings.main.primaryMediaServer === MediaServerType.JELLYFIN
? ServerType.JELLYFIN : ServerType.EMBY) to derive the server type from the
active login/auth method (e.g., the request/session auth provider variable such
as loginMethod/provider/authMethod that indicates whether the current auth flow
is Jellyfin or Emby) and update the corresponding log messages that reference
updating user with X to use that resolved value; apply this replacement where
those ternaries appear (the same logic at the three affected branches) so
downstream behavior uses the active login method instead of
settings.main.primaryMediaServer.
- Around line 699-707: The axios.delete call that removes the Jellyfin/Emby
device (the await axios.delete(...) in server/routes/auth.ts using
getHostname(), user.jellyfinDeviceId, getAppVersion(), and
settings.jellyfin.apiKey) must include an explicit timeout and not block logout;
add a timeout property (e.g. timeout: 5000) to the request config passed to
axios.delete and ensure the call is wrapped in a try/catch so any timeout or
network error is logged/ignored but does not prevent the subsequent session
destruction code from running.

---

Duplicate comments:
In `@server/routes/user/index.ts`:
- Around line 595-601: The code constructs PlexTvAPI with a potentially empty
token (plexToken) derived from mainUser.plexToken or settings.plex.adminToken;
validate that plexToken is non-empty before calling new PlexTvAPI (e.g., in the
same block that uses userRepository.findOneOrFail and before instantiating
PlexTvAPI) and if it's empty throw or return a clear error (including context
like "missing Plex admin token" and the affected user id) so the caller receives
a meaningful message instead of a downstream API failure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c8067431-606b-41f8-9ed4-1c0d2dc92f8c

📥 Commits

Reviewing files that changed from the base of the PR and between 5cc9e05 and 8527ac0.

📒 Files selected for processing (9)
  • seerr-api.yml
  • server/lib/overseerrMerge.ts
  • server/routes/auth.ts
  • server/routes/settings/index.ts
  • server/routes/user/index.ts
  • server/routes/user/usersettings.ts
  • src/components/Login/index.tsx
  • src/components/Settings/SettingsJellyfin.tsx
  • src/components/Settings/SettingsLayout.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/Login/index.tsx
  • server/lib/overseerrMerge.ts
  • src/components/Settings/SettingsLayout.tsx

Comment thread server/routes/auth.ts Outdated
Comment thread server/routes/auth.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/routes/auth.ts`:
- Around line 266-270: The current gating uses a combined check against both
Jellyfin and Emby which allows a disabled provider to be used when the other
provider is enabled; update the logic to determine the actual provider (use the
existing resolvedServerType / settings.main.primaryMediaServer resolution) and
call settings.isAuthMethodEnabled(resolvedType) for that single provider,
returning/blocking when that specific provider is disabled; apply the same fix
to the other occurrences that reference the dual check (the later block around
resolvedServerType fallback and the checks at the regions noted) so every
auth-path validates the resolved provider via
settings.isAuthMethodEnabled(resolvedType) instead of checking both
MediaServerType.JELLYFIN and MediaServerType.EMBY.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e16cde0e-d6ae-4f1e-b073-100e38681649

📥 Commits

Reviewing files that changed from the base of the PR and between 8527ac0 and f260871.

📒 Files selected for processing (1)
  • server/routes/auth.ts

Comment thread server/routes/auth.ts Outdated
@github-actions github-actions Bot added the merge conflict Cannot merge due to merge conflicts label Mar 30, 2026
@github-actions
Copy link
Copy Markdown

This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.

@M0NsTeRRR
Copy link
Copy Markdown
Member

I haven’t reviewed any of the PRs yet, so I can’t say which approach we prefer. However, this won’t be worked on anytime soon, as we’re currently focusing on OIDC before tackling this feature.

@fallenbagel
Copy link
Copy Markdown
Collaborator

fallenbagel commented Mar 30, 2026

I have not reviewed this but based on the description, here's my two cents:

We already have a linked accounts feature that was built specifically for allowing multiple auth methods on a single account. The right approach here would be to extend that system to support cross-server linking, rather than introducing a parallel enabledAuthMethods[] + primaryMediaServer system, thus rendering the already existing linked account feature irrelevant.

Currently linked accounts is gated to only the configured mediaServerType. Relaxing those guards on the link/unlink routes and updating the auth routes to accept login via any linked identity would achieve the same goal with a much smaller change. The User entity already has both plexId and jellyfinUserId columns so the data model supports this out of the box.

Also as @M0NsTeRRR mentioned, OIDC is the current priority before this feature which properly makes use of this linked accounts feature (you can take a look at it to see how it's implemented).

@afonsojramos
Copy link
Copy Markdown
Author

@fallenbagel @M0NsTeRRR Thanks for the feedback — that’s a really good point about extending the linked accounts system. I hadn’t considered that OIDC would build on top of it too, which makes alignment there more important.

The main gap I was trying to fill is allowing direct sign-in via a secondary server without needing a pre-existing account + manual linking step. But I can see how extending linked accounts + relaxing the auth route guards would be a simpler foundation, especially if OIDC is coming first.

Happy to rework toward that approach or wait until after OIDC lands to see what makes sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge conflict Cannot merge due to merge conflicts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Simultaneous multiple auth methods

5 participants