fix(oidc): show the sign-in link as a QR so a TV can hand the consent to a phone (#216) - #218
Conversation
|
Thanks a lot for this PR! I have manually tested it out on my server. The QR code generates successfully, however there is a problem: The redirect URI in the QR Code is wrong: Its my local Jellyfin Server address (192.168.x.y) instead of the public facing URL (jellyfin.example.com). I have noticed the same thing happening in the password reset email as well. My assumption is, that the plugin needs to be made aware of the Jellyfin instance's actual user-facing IP/URL. When manually editing the redirect_uri, I was able to login :) |
|
Thank you for testing it on a real LG, and for spotting this. You are right, and your diagnosis is right too. Why it happens. Every externally facing link the plugin builds is derived from the incoming request. That works when the request arrives through your reverse proxy, because the plugin honours It is the same root cause in the password reset email you noticed, and in the device pairing QR codes, which build their URLs the same way. Four places in total. What I am doing about it. PR #219, kept separate because this is a different concern from showing a QR and it touches the OIDC redirect the IdP validates. The plugin will resolve a public base URL once, and use it for all four links, in this order:
That PR is #219. This one stays as it is, since the QR itself works. Your case needs both: this PR to show the code, and that one to put the right host inside it. About the rest of your report. Two things I checked while working on this, both with a throwaway server rather than by reading the code: The 2FA challenge page only appears for an OIDC account when the provider has "Bypass plugin 2FA" turned off and 2FA is required. With the default, the IdP sign-in satisfies the plugin and no challenge is issued at all, which I confirmed with enforcement set to All. If you are seeing the challenge on the TV, that switch on your provider is the thing to look at, and please tell me either way. Keeping the TV signed in afterwards already works and needs nothing new. The plugin persists the verified session tokens to disk, so I signed a TV in, restarted the whole server, and the same token still worked. The device itself is not added to any trusted list, but it does not need to be. One question still open from my earlier comment, which decides whether there is anything left to fix on the TV side: does webOS report error -27 on the popup itself, or only after it tries to hand the link to the TV browser? |
…Seerr guidance (#228) Bumps 2.6.1.0 -> 2.6.2.0. Rolls up the post-2.6.1 work merged to main: - #216: OIDC sign-in QR for TVs (#218), public base URL for redirect/QR/reset (#219), opt-in second-screen device pairing (#220) - #215: GeoIP suspicious-login + impossible-travel detectors now called (#217), proxy-resolved client IP in session-start audit (#231) - #228: corrected Seerr/Jellyseerr + device-pairing README guidance (#230) - deps: Fido2 4.1.0 (RPID/RPName), test tooling (#229); QuestPDF + IdentityModel pins held All bench-tested on both ABIs (10.11 net9 + JF12 net10), incl. live OIDC->2FA (#205), QR (#218), public-URL redirect (#219), device pairing (#220). 570/571 tests (1 Windows-only path flake). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Fixes #216. A TV cannot open a browser and has no keyboard, so both ways out of the OIDC sign-in modal are dead ends there: LG webOS refuses the link (the reporter sees error -27) and "Copy sign-in link" needs somewhere to paste it. This shows the same authorize URL as a QR code, so the consent can be granted on a phone.
Nothing about the flow changes.
Oidc/LoginInfoalready returns an authorize URL and a poll token, andOidc/DevicePollis keyed by that 256 bit single use token with no binding to the device or the address, which is what makes the consent portable between screens. The modal simply gains a picture of the URL it already has.Type of change
It reads as a feature, but the sign-in path on a TV is currently unreachable for an OIDC-only account, so I filed it as the fix for that.
Related issues
Fixes #216. Builds on the in-page device flow from #64 (v2.5.9, v2.5.12), which introduced
Oidc/LoginInfo,Oidc/DevicePolland the modal this touches.How was this tested?
Server tested against: Jellyfin 12.0.0, official Docker image, with the Jellyfin 12 package built from this branch, and Dex 2.41.1 as the identity provider.
The point to prove is that a consent granted somewhere else finishes the sign-in on the screen that is polling. The test drives two separate HTTP sessions, one standing in for the TV and one for a phone, with their own cookie jars:
Server log for the same run:
I also checked that the QR is really the URL and not a picture of something else: re-rendering the same authorize URL with the same library at the same settings produces a byte-identical PNG (1788 bytes both ways), and a control with one character appended to the URL produces different bytes.
Local, with the .NET SDK 9.0.316 that CI installs:
dotnet build JellyfinSecurity.sln -c Releasehas 0 errors (the twoCS8602warnings inJellyfin12ShellTests.csare onmainalready and are what #202 fixes),dotnet test -c Releaseis 513 passed and 0 failed,node --test tests/oidc-bridge.test.mjsis 8 passed, andnode --checkon the injected script passes. With the .NET SDK 10.0.400, the Jellyfin 12 build has 0 warnings and 0 errors.What changed
Api/SecurityController.cs:Oidc/LoginInforeturnsqrCodeBase64alongsideauthUrlandpollToken. A privateRenderQrPnghelper draws it and returns an empty string on any failure, so a sign-in can never break because a picture could not be drawn. Scale 6 rather than the 5 used for TOTP enrolment, since this one is read across a room.Pages/inject.js:showOidcModaltakes the QR as an optional third argument and renders it above the existing buttons when present. The modal opened before the server answers passesnulland looks exactly as it does today.Pages/translations/*.json: one new key,tfa.login.oidc_qr_help, in all eight languages, soEverySupportedTranslation_HasEnglishKeyParitystays green.tests/.../OidcDeviceFlowQrTests.cs(new, 2 tests): the modal renders the QR only when the server sends one, reads the field in either casing, and the caption exists in every language.Checklist
dotnet formatif unsure)dotnet build+dotnet testgreen)Additional notes