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
- 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
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