Closed strazto closed 5 months ago
Hi - This is migrated from the Jf-dev matrix channel @thornbill @nielsvanvelzen
Thanks, I've looked at some other apps (mainly Element) to see how they add a similar feature. One change I'd like to make is to use a temporary login token that needs to be exchanged by the Jellyfin app for an access token.
So you get a flow similar to this:
jellyfin://connect?exchange_token=ABCDEF&server=SERVERURL
Login implementation for Element: https://github.com/vector-im/element-android/blob/afaa89ad42e1803b53c4610169646fedef4c0f68/docs/signin.md#login-with-sso
The sign in activity in the app will likely show a popup to confirm if the user wants to sign in. Maybe an endpoint to introspect the login token can be used to add more details to this popup (name of the user to sign in with for example).
Awesome, glad to hear you're on-board with the use case!
The sign in activity in the app will likely show a popup to confirm if the user wants to sign in.
Seems reasonable - it'd be nice to make this optional, just for UX, since it's one extra click/touch, though I don't really know how configuring that could work (the point of an explicit login-consent flow is to ensure the user wants this, so we can't trust the query string to toggle it for example)
One change I'd like to make is to use a temporary login token that needs to be exchanged by the Jellyfin app for an access token.
Yep, this occurred to me too- It might be slightly more complex to implement (And I think I'll have to touch other aspects of the architecture?) but I do understand why you're requesting this.
it's kind of like a "reverse quick-connect" - From an authenticated session, generate a one-time code & a client consumes that.
Do you anticipate that I'll have to make changes to jellyfin-server to accomodate the exchange-token
API? I suspect I will.
I don't think this is a bad thing- This being a general feature of jellyfin-server is probably a good thing, and will likely service use-cases beyond my specific one (Once it's generally available, i might even be able to use it to simplify my plugin)
My suspicion is that I'll be exposing the following API endpoints:
(These are just sketches of what it might look like so i understand the components)
/getAccessToken?exchangeToken=...
/generateExchangeToken
something.generateExchangeToken(uid)
This is pretty straightforward from my end
generateExchangeToken
(via whichever API)org.jellyfin://connect?exchange_token=ABCDEF&server=SERVERURL
org.jellyfin://connect?exchange_token=ABCDEF&server=SERVERURL
${SERVERURL}/getAccessToken?excahnge_token=ABCDEF
, get whatever I need from thatAssuming we need to change the jellyfin server's API - which I think is likely:
for a client to use this functionality, they will need to call:
${SERVERURL}/getAccessToken?excahnge_token=ABCDEF
This will fail (404 probably) on jf server versions that predate this feature.
We have a few options to handle this version dependency -
Do you anticipate that I'll have to make changes to jellyfin-server to accomodate the exchange-token API? I suspect I will.
Yeah definitely, the server should always have the API available and just do nothing when there is no plugin using it.
[api stuff]
The API changes seem fine to me, the generateExchangeToken operation might not be needed. The paths and exact parameters will probably change when the actual PR for the server is made. I think most requests will be POST instead of GET.
org.jellyfin://
I think just jellyfin://
should be fine here.
Versioning concern
Assuming all server changes are merged for 10.9 the official clients will only start supporting the feature in 10.9. They will likely read the public system info first (/system/info/public
) and check the server version for support. When connect is not supported on the server it will show an error in the UI.
(Moved to jellyfin-meta since this repository makes more sense for discussing organization wide changes)
Any updates on this? Sounds like an absolute rocker of a feature!
Thanks, I've looked at some other apps (mainly Element) to see how they add a similar feature. One change I'd like to make is to use a temporary login token that needs to be exchanged by the Jellyfin app for an access token.
So you get a flow similar to this:
- User opens SSO app
- SSO app deals with signin
- SSO app creates an exchange token in the server, this token expires in 5 minutes
- SSO app redirects to
jellyfin://connect?exchange_token=ABCDEF&server=SERVERURL
- Jellyfin app reacts to this URL and opens a sign in activity
- Sign in activity checks the server (calling public system info) to see if it exists and if the server version is compatible with the app
- Sign in activity exchanges token with an access_token (server also responds with UserDto) and stores this locally (at this point the token is revoked serverside and cannot be used again)
- User is signed in
Login implementation for Element: https://github.com/vector-im/element-android/blob/afaa89ad42e1803b53c4610169646fedef4c0f68/docs/signin.md#login-with-sso
The sign in activity in the app will likely show a popup to confirm if the user wants to sign in. Maybe an endpoint to introspect the login token can be used to add more details to this popup (name of the user to sign in with for example).
@nielsvanvelzen
To give my view: A flow this way would not be safe. Oauth2 standard requires PKCE for mobile apps, bc otherwise other (malicious) apps could receive the login code.
A safe flow (see also https://datatracker.ietf.org/doc/html/rfc8252 ):
BASE64URL-ENCODE(SHA256(ASCII(verifier)))
. verifier will be kept secret until the token exchange.jellyfin.myserver.com/login?code_challenge=thechallenge&code_challenge_method=S256
(Alternatively this step can be skipped if the app knows the SSO Provider URL. However the advantage is that the clientid can be kept secret).auth.ssoprovider.com/realm/jellyfin/login?client_id=CLIENTID&code_challenge=thechallenge&code_challenge_method=S256&redirect=jellyfin://login
(URL depends on the SSO Provider)Location:
header). This must not be followedLocation:
header in a browser an open sign in activity on the SSO provider pagejellyfin://login?code=CODE
with a code.code
together with verifier
from step 2 can now be used to exchange the code for a token.The SSO provider checks in a flow beginning with a code_challenge if verifier matches the code, if not the code cannot be used. A malicious app cannot unhash the verifier and as such it is safe.
I hope this releases soon... it would be very useful for what appears to be a common setup (using SSO at home to ease password fatigue and improve security)!
Request
Basically, I'd like to be able to open a link with a custom schema to the app that supplies:
To an intent handler (or whatever we call it in ios), that stores that information in the web client to initiate a session, and skips the login.
This would intend to accomodate single-sign-on via plugin, eg the popular
https://github.com/9p4/jellyfin-plugin-sso
Here is a demonstration of the current flow on the android client (behaves similarly)
https://user-images.githubusercontent.com/39424834/180710755-63c8addc-7c18-412f-8e1d-47bc411a1006.mp4
Take note that although we can get a via single sign on, the new session is in the browser, rather than the jellyfin app.
From a technical perspective, this is not too hard to implement, both Android and iOS have APIs for using custom links open apps with some kind of data payload https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app
Background
I am one of the maintainers of a plugin that provides single-sign-on to jellyfin servers
https://github.com/9p4/jellyfin-plugin-sso
The plugin is now at a stage of development where its functionality is quite mature, but its main limitation is that it only supports the web-client.
Flows
Assume the jellyfin server lives at
https://myjellyfin.com
The plugin can serve a page that initiates the SSO flow (either oauth or saml, I'm mainly going to talk about oauth), this page is at
https://myjellyfin.com/SSO/OID/p/<provider_name>
.The plugin also serves a callback page for completing SSO flows, eg
https://myjellyfin.com/SSO/OID/r/<provider_name>
.For example, you might use Google as your provider, so you'd use
https://myjellyfin.com/SSO/OID/p/google
, andhttps://myjellyfin.com/SSO/OID/r/google
On the web-client, the SSO flow from login-page to signed in dashboard is straightforward.
User perspective (Web client):
Actual flow (Web client)
https://myjellyfin.com/SSO/OID/p/google
with a javascript client that initiates an OAuth flowhttps://accounts.google.com/o/oauth2/v2/auth
https://myjellyfin.com/SSO/OID/r/google
with the oauth state requiredhttps://myjellyfin.com
) and is now logged inThis flow works reliably. An important note, however, is that a lot of the oauth client-handling is done using javascript clients in web pages server directly by the plugin.
Limitations
Because the above flow relies on a browser oauth client, it effectively establishes a valid session within the browser
https://github.com/9p4/jellyfin-plugin-sso/blob/fad5a62e07e44c84bc94d5962b8b79e80c102ee8/SSO-Auth/WebResponse.cs#L449-L458
It literally stores all the state the web-app needs in order to resume a session.
The problem is, that we can't use this to log-into the android or ios app
Additional context
Discussion migrated from Matrix - Pretty long but included for posterity
[Matthew Strasiotto](https://matrix.to/#/!YOoxJKhsHoXZiIHyBG:matrix.org/$qultQ8GE3lk6Jmf0N1MIkSj6OmZ9BqqNzpNG4xP9--A?via=bonifacelabs.ca&via=t2bot.io&via=matrix.org): (July 6ish) > question - > Is it at all possible to add an intent to the jf android app that signs onto a user account using a token generated from elsewhere? > i'm reading about SSO for android, and how to add SSO clients to android apps > https://github.com/openid/AppAuth-Android > > i'm a contributor to https://github.com/9p4/jellyfin-plugin-sso > the actual oauth client works on jellyfin-web, & i'd like to get it to work for the android client - > The best way to do that is probably something along the lines of AppAuth-Android / maybe intent filters > > > in reality, as long as we can trigger some kind of redirect back to the jellyfin app with enough data from the authenticated web-session to get a user logged into the app, there isn't a tonne of work > ... [Matthew Strasiottio](https://matrix.to/#/!YOoxJKhsHoXZiIHyBG:matrix.org/$8C4eqpMWwCWa0Gy98pbg0wzlkaKdc_87syR_FUIOL-s?via=bonifacelabs.ca&via=t2bot.io&via=matrix.org): > [mcarlton](https://matrix.to/#/!YOoxJKhsHoXZiIHyBG:matrix.org/$n_R7GOhYoukWRovxbBn7v2Uou4-E42oek3h5o9WLrGM?via=bonifacelabs.ca&via=t2bot.io&via=matrix.org) > Matthew Strasiotto > > > would quick connect accomplish what you're looking for? i'm not sure if it can be used in the android app as a client side yet > > Quick connect is good, and a good workaround for the use-case where people's primary authentication is SSO > It does work on the android app, but the limitation is that the current SSO flow my plugin gives will basically Auth a session in the browser, and a user would need to > 1. Initiate browser SSO > 2. Swap back to jellyfin, initiate quick connect > 3. Swap back to browser, complete quick connect flow > > Pretty clunky, good for Auth across devices tho [Matthew Strasiotto](https://matrix.to/#/!YOoxJKhsHoXZiIHyBG:matrix.org/$Yr7YShEbHvvZCsHIgz4hZbtAELNGxugmjdp6V1qFoBQ?via=bonifacelabs.ca&via=t2bot.io&via=matrix.org) July 17: > I'd like to support the SSO plugin i contribute to in jellyfin-android & jellyfin-expo > I've heard from maintainers that they're not interested in supporting SSO in their apps at this stage as it would require each client implement it. > I'd like to get around that with fully browser/web-app based implementation that's handled by the plugin, which basically handles the flow, gets the userID + access token > > The browser client would then open a custom scheme link that passes the server ID, user ID & access token back to the app, and triggers a "sign on" intent > > for either case, the jellyfin-android + jellyfin-expo app would only have to handle an intent being given with fully-formed credentials, and everything else could defer back to their web wrappers. > > Before i commence work on this, I'm curious if maintainers are open to considering PRs on either project (jf-android / jf-expo) that allow custom URL scheme handling for the scope of "given serverID, userID & accessToken, initate a session then commence main activity" so that my plugin can handle the rest from a native browser ![image](https://user-images.githubusercontent.com/39424834/180710127-d02fb28b-f6a7-4edb-9a5f-1512c961b933.png) ![image](https://user-images.githubusercontent.com/39424834/180710179-eb04ab85-f442-4c79-9fce-25bc5556f27a.png) ![image](https://user-images.githubusercontent.com/39424834/180710206-baa22bc8-04b8-4cf7-b693-4d348478dc89.png) July 21 https://matrix.to/#/!YOoxJKhsHoXZiIHyBG:matrix.org/$qn4tvt0GXVlDnBYIGgM0wCuO0C2SO2AAUyAI9xiQHlc?via=bonifacelabs.ca&via=t2bot.io&via=matrix.org ![image](https://user-images.githubusercontent.com/39424834/180710303-47979e81-4cfa-4b09-9926-4adfbac28e6d.png) ![image](https://user-images.githubusercontent.com/39424834/180710385-33b0f080-0b48-4b3a-9e78-e1c992b8d0a7.png) ![image](https://user-images.githubusercontent.com/39424834/180710445-f995c547-0122-4e71-a8e3-fe6d7a7ab562.png)