goauthentik / goauthentik/authentik

Plex source is unusable on mobile: the popup is blocked and the sign-in hand-off strands the user on plex.tv

Open
#24,613 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
25.6k
Forks
2k
Avg merge
1d 2h
Merged PRs (30d)
659

Description

### Describe the bug

Signing in through a Plex source fails in practice on mobile browsers, and is degraded on desktop. Four related defects in the web flow stage combine to cause this.

**1. The popup is opened outside the user gesture, so it is blocked.**

`popupCenterScreen` in `web/src/common/helpers/plex.ts` wraps `window.open` in a `setTimeout`, which discards transient user activation:

```ts
return new Promise((resolve) => {
setTimeout(() => {
const popup = window.open(url, title, `scrollbars=yes,...`);
resolve(popup);
});
});
```

Measured blocked on 100 percent of runs on iOS Safari 26.5.2 (iOS 18.7) and on desktop Firefox. Every user therefore takes the "If no Plex popup opens, click the button below" fallback path, on desktop as well as mobile.

**2. When the flow auto-advances, there is no gesture available to fix this with.**

`AutoRedirectController.hostUpdate()` auto-dispatches the source challenge when the identification stage has a single source, no user fields, and no passwordless URL. A Plex-only login flow (a common shape for a Plex portal) reaches the Plex stage with no click at all, so the extra button press is structural: there is no user activation to open a popup from.

**3. The hand-off runs entirely in the opener, and Plex is given no way back.**

`PlexAPIClient.getPin` builds `https://app.plex.tv/auth#!?clientID=...&code=...` and never sets Plex's `forwardUrl`, so the second browsing context has nowhere to return to. On mobile, `window.open` produces a tab rather than a popup: after signing in, the user is left sitting on plex.tv with no indication that anything is waiting on them. Polling the pin, closing the auth window, and advancing the flow all happen in the tab they just left.

**4. `pinPoll` never times out, and the stage never handles its rejection.**

`pinStatus` throws once the pin expires (about 15 minutes), and `pinPoll` recurses on a 500 ms `setTimeout` with no deadline. `PlexLoginInit.firstUpdated` attaches only a `.then`, so the eventual rejection is unhandled and a stale tab shows "Waiting for authentication..." forever.

Minor, in the same code path: the redeem error handler does `window.location.assign("/")`. On deployments that expose only the flow paths through a reverse proxy allowlist, `/` is not reachable, so any error strands the user a second time.

### How to reproduce

1. authentik 2026.5.6 (the relevant code is unchanged on current main).
2. Create a Plex source. Create an authentication flow whose identification stage has no user fields, no password stage, and only that Plex source. Point an application's authentication flow at it.
3. Open the application's login page on iOS Safari (a regular window, not Private Browsing).
4. The flow auto-advances to the Plex stage. No popup opens. Pressing the fallback button opens a new tab; after signing in to Plex, the user stays on plex.tv. The original tab never advances unless the user manually switches back to it while the pin is still being polled.

### Expected behavior

Completing the Plex sign-in returns the user to authentik and finishes the flow, without depending on a popup that current browsers block.

Plex supports exactly this: `app.plex.tv/auth` accepts a `forwardUrl` parameter and navigates back to it once the pin is authorized. I have this working as a local patch on my own deployment: the stage redirects the current tab to Plex with `forwardUrl` set to the flow URL (carrying the pin id and a retry counter in the query string, since Safari partitions web storage in Private Browsing), then on return polls the pin with a bounded deadline, redeems the token, and continues the flow. Verified end to end on desktop and iOS Safari, including the auto-advance case, which needs no gesture at all once it is a redirect.

Two notes from getting that working:

* The forward URL must preserve the flow's existing query string. `next=` is what routes the completed flow back to `/application/o/authorize/`; dropping it makes the flow succeed and then land external users on "Permission denied: Interface can only be accessed by internal users".
* Plex's documented form is `auth#?clientID=...`; the `forwardUrl` behavior was verified with that form, not with the `#!?` form the helper currently builds.

I will open a PR with this change. If maintainers would rather keep the popup and fix `popupCenterScreen` to open the window inside the gesture, that helps the clicked cases but cannot help the auto-advance case, where no gesture exists, so the redirect appears necessary regardless.

### Deployment Method

Docker

### Version

2026.5.6

Contributor guide

Open the contributing guide

Research direction

Start with web/src/common/helpers/plex.ts and trace popupCenterScreen, PlexAPIClient.getPin, pinPoll, and pinStatus into AutoRedirectController.hostUpdate() and PlexLoginInit.firstUpdated. Reproduce the auto-advance flow on iOS Safari and desktop Firefox, then verify that Plex authentication returns to the flow, polling expires cleanly, and the completed flow preserves its query string and continues successfully.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
authentication, frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.