Skip to content

fix(auth): drop popup.closed check in Plex pin poll#2941

Merged
0xSysR3ll merged 3 commits intoseerr-team:developfrom
Finchow:Finchow/fix/plex-login-coop
Apr 27, 2026
Merged

fix(auth): drop popup.closed check in Plex pin poll#2941
0xSysR3ll merged 3 commits intoseerr-team:developfrom
Finchow:Finchow/fix/plex-login-coop

Conversation

@Finchow
Copy link
Copy Markdown
Contributor

@Finchow Finchow commented Apr 23, 2026

Description

popup.closed is inconsistent when used after the OAuth popup makes a cross-origin navigation when deployed under Cross-Origin-Opener-Policy: same-origin-allow-popups.

This results in the opener becoming undefined, and closed evaluating to true, even though the user is still authenticating with app.plex.tv.

The previous pinPoll() considered this situation a cancelled login attempt after the first iteration and therefore failed.

The result was that signing into Plex was impossible through any reverse proxy configured to include this header (e.g., Caddy/nginx/Traefik "secure defaults" blocks).

Continue polling until the PIN is either authorised by Plex or invalidated; the latter condition manifests through the catch. Normal operation is unaffected.

AI Disclosure:

  • No AI was used

How Has This Been Tested?

Built and deployed onto my prod server where I first encountered the issue. Tested after fix applied and works.

Ran all tests in project & checked for issues in the dev console - none.__

Checklist:

  • I have read and followed the contribution guidelines.
  • Disclosed any use of AI (see our policy)
  • 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)

Summary by CodeRabbit

  • Bug Fixes
    • More reliable Plex authentication: PIN-based logins now use a fixed 15-minute expiry, better detect PIN expiration, and avoid premature failures when the login popup is closed, reducing interrupted logins.

`popup.closed` is inconsistent when used after the OAuth
  popup makes a cross-origin navigation when deployed under
  `Cross-Origin-Opener-Policy: same-origin-allow-popups`.
  This results in the opener becoming undefined, and
  `closed` evaluating to `true`, even though the user is
  still authenticating with app.plex.tv.
  The previous `pinPoll()` considered this situation a cancelled
  login attempt after the first iteration and therefore failed.
  The result was that signing into Plex was impossible through
  any reverse proxy configured to include this header
  (e.g., Caddy/nginx/Traefik "secure defaults" blocks).

  Continue polling until the PIN is either authorised by Plex or
  invalidated; the latter condition manifests through the catch.
  Normal operation is unaffected.
@Finchow Finchow requested a review from a team as a code owner April 23, 2026 23:00
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Walkthrough

pinPoll in src/utils/plex.ts was changed to use a fixed 15-minute deadline and to stop rejecting when the popup appears closed; it now derives an expiresAt from the PIN response (or uses the deadline), closes the popup and rejects only when that expiry is reached, otherwise scheduling the next poll after 1 second.

Changes

Cohort / File(s) Summary
Plex Authentication Polling
src/utils/plex.ts
Set a fixed 15-minute deadline; remove reliance on popup.closed for immediate rejection; compute expiresAt from response.data.expiresAt (fallback to deadline); close popup and reject only when current time >= min(expiresAt, deadline); otherwise continue polling every 1s.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Parent as Parent Window
    participant Popup as Plex Popup
    participant Plex as Plex API
    Note over Parent,Popup: Login flow with PIN polling
    User->>Parent: Click "Sign in with Plex"
    Parent->>Popup: Open popup
    User->>Popup: Authenticate on plex.tv
    Popup->>Parent: (postMessage) AUTH_COMPLETE with pin id
    Parent->>Plex: GET /api/v2/pins/{id} (poll)
    Plex-->>Parent: { data: { authToken: ... } } OR { data: { expiresAt: ... } }
    alt authToken present
        Parent->>Popup: close popup
        Parent->>Parent: resolve poll with token -> proceed to POST /api/v1/auth/plex
    else no authToken yet
        Parent->>Parent: compute expiresAt (or use deadline)
        alt now >= min(expiresAt, deadline)
            Parent->>Popup: close popup
            Parent->>Parent: reject poll (PIN expired)
        else
            Parent->>Parent: schedule next poll after 1s
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • 0xSysR3ll
  • fallenbagel
  • gauthier-th

Poem

🐰
I hopped to watch the popup spin,
Removed a check, let timers win,
The PIN will age, the poll will try,
Close when deadlines say goodbye,
Huzzah — a smoother auth supply! 🥕

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(auth): drop popup.closed check in Plex pin poll' accurately describes the main change: removing the popup.closed check from the PIN polling logic to fix the Plex login failure.
Linked Issues check ✅ Passed The PR successfully addresses issue #2939 by removing the unreliable popup.closed check and introducing a deadline-based polling mechanism to continue polling until the PIN expires, enabling Plex login completion under COOP headers.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the Plex PIN polling logic as required by issue #2939; the deadline implementation is an in-scope safeguard against unbounded polling on expired pins.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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


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.

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 `@src/utils/plex.ts`:
- Around line 143-147: The polling loop in executePoll (src/utils/plex.ts) can
run forever because popup.closed was removed and Plex returns 200 with null
authToken for expired pins; update executePoll to read the pin response's
expiresAt or expiresIn and compute a client-side deadline (fallback to ~15
minutes), then stop scheduling further setTimeout calls and reject the original
Promise (via reject) once the deadline is reached; ensure the check runs before
scheduling setTimeout and that any in-flight schedule is not re-queued after
expiry so the Promise always settles.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 558d4912-56c0-443d-8fc8-98c0d29e9356

📥 Commits

Reviewing files that changed from the base of the PR and between 2d4cd03 and 5202a74.

📒 Files selected for processing (1)
  • src/utils/plex.ts

Comment thread src/utils/plex.ts
Copy link
Copy Markdown
Collaborator

@fallenbagel fallenbagel left a comment

Choose a reason for hiding this comment

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

@Finchow Could you also update #2939 to make it clear this is specifically a COOP same-origin-allow-popups issue? Right now the title/description read like a generic "Plex login broken behind reverse proxy," which it isn't.

Without that framing, anyone hitting an unrelated Plex OAuth problem (popup blocker, network, misconfigured reverse proxy, whatever) is going to pile onto this issue with "+1 same here" and create unrelated noise.

A title like "Plex login fails when reverse proxy sets Cross-Origin-Opener-Policy: same-origin-allow-popups" plus a description of the COOP/popup.closed interaction would be fine.

Comment thread src/utils/plex.ts
@Finchow
Copy link
Copy Markdown
Contributor Author

Finchow commented Apr 26, 2026

@fallenbagel. Hi, sorry for the delay I have added the changes. I can confirm it works in a normal env and in my COOP env.

Please let me know if you would like anything else done

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.

🧹 Nitpick comments (1)
src/utils/plex.ts (1)

148-155: Minor: guard against Date.parse returning NaN.

If response.data.expiresAt is ever present but in a format Date.parse can't handle, expiresAt becomes NaN, Math.min(NaN, deadline) is NaN, and Date.now() >= NaN is always false — so the loop would run until tab close. Plex currently returns ISO 8601, so this is defensive only.

🛡️ Defensive fallback to deadline on parse failure
-          const expiresAt = response.data?.expiresAt
-            ? Date.parse(response.data.expiresAt)
-            : deadline;
-          if (Date.now() >= Math.min(expiresAt, deadline)) {
+          const parsedExpiresAt = response.data?.expiresAt
+            ? Date.parse(response.data.expiresAt)
+            : NaN;
+          const expiresAt = Number.isFinite(parsedExpiresAt)
+            ? parsedExpiresAt
+            : deadline;
+          if (Date.now() >= Math.min(expiresAt, deadline)) {
             this.closePopup();
             reject(new Error('Plex PIN expired before login completed.'));
             return;
           }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/plex.ts` around lines 148 - 155, The expiresAt computation uses
Date.parse(response.data.expiresAt) which may return NaN; update the logic
around the expiresAt variable (the code that sets expiresAt from
response.data.expiresAt) to detect NaN (Number.isNaN) and fall back to the
existing deadline when parsing fails so Math.min(expiresAt, deadline) never
becomes NaN; this change should be made where expiresAt is assigned and before
the Date.now() >= Math.min(...) check in the same function/method.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/utils/plex.ts`:
- Around line 148-155: The expiresAt computation uses
Date.parse(response.data.expiresAt) which may return NaN; update the logic
around the expiresAt variable (the code that sets expiresAt from
response.data.expiresAt) to detect NaN (Number.isNaN) and fall back to the
existing deadline when parsing fails so Math.min(expiresAt, deadline) never
becomes NaN; this change should be made where expiresAt is assigned and before
the Date.now() >= Math.min(...) check in the same function/method.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ce68574c-4f6e-4796-9b34-87f73e200eff

📥 Commits

Reviewing files that changed from the base of the PR and between 5202a74 and b64bd75.

📒 Files selected for processing (1)
  • src/utils/plex.ts

@Finchow Finchow requested a review from fallenbagel April 26, 2026 18:47
@seerr-automation-bot seerr-automation-bot added this to the v3.3.0 milestone Apr 27, 2026
Copy link
Copy Markdown
Collaborator

@fallenbagel fallenbagel left a comment

Choose a reason for hiding this comment

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

LGTM. However, please update the description with the AI Disclosure (even if you didn't use you should mention that according to our contributing guidelines).

@0xSysR3ll 0xSysR3ll merged commit ce9643c into seerr-team:develop Apr 27, 2026
15 checks passed
@Finchow Finchow deleted the Finchow/fix/plex-login-coop branch April 27, 2026 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Plex login fails when reverse proxy sets Cross-Origin-Opener-Policy: same-origin-allow-popups

5 participants