Relocate0604 3 Posted December 30, 2024 Posted December 30, 2024 (edited) I understand that Emby does not support SSO/OIDC out of the box. On my free time I managed to come up with a way that is a bit complicated but works with services similar to Emby authentication process. I managed to make it work a couple of times already. I am trying to write a work around guid with Nginx Proxy Manager and Authentik for Emby. I am currently stuck trying to figure out the needed Headers to login as a specific user. Here what I have done so far: Part 1 - fetching Emby AccessToken: - User Attributes: in Authentik Admin Panel, go to users and edit desired user. under "Attributes" add the following values: emby_username: [embyaccounduser] emby_password: [embyaccoundpassword] Make sure to add the correct values for your account credentials. - API Key: In emby, login as Admin, and create an API key. - Property Mapping to fetch Emby AccessToken: in Authentik Admin Panel, go to Customization > Property Mappings > Create Scope Mapping. Fill the fieleds as follow: Name: Emby Token Scoop Name: ak_proxy Under Expression add the following to fetch the code: import json from urllib.parse import urlencode from urllib.request import Request, urlopen if request.user.username == "": return {"ak_proxy": {"user_attributes": {"additionalHeaders": {"X-Emby-Token": "null"}}}} else: embyuser = request.user.attributes.get("emby_username", "placeholderuser") #here we are getting the user name attribute we added earlier to the user embypass = request.user.attributes.get("emby_password", "placeholderpassword") #here we are getting the password name attribute we added earlier to the user base_url = "http://embyserver:80" #make sure Authentik can access Emby. i user server:port because they are on the same network but one can also use domain end_point = "/Users/AuthenticateByName?api_key=[here comes the API key we just created]" json_data = {'Username': embyuser,'Pw': embypass} postdata = json.dumps(json_data).encode() headers = {"Content-Type": "application/json; charset=UTF-8"} try: httprequest = Request(base_url + end_point, data=postdata, method="POST", headers=headers) with urlopen(httprequest) as response: responddata = json.loads(response.read().decode()) return {"ak_proxy": {"user_attributes": {"additionalHeaders": {"X-Emby-Token": responddata['AccessToken']}}}} except: return {"ak_proxy": {"user_attributes": {"additionalHeaders": {"X-Emby-Token": "null"}}}} once saved, test the scoop. if no user is selected, this should be returned: "X-Emby-Token": "null" . If a user with the correct Attributes is selected while testing, it should return the correct AccessToken. According to Emby Documentations, the value of which can be passed as X-Emby-Token header to login. Part 2 - Create Proxy Authentication: - Authentik configurations: in Authentik Admin Panel, go to Applications > Providers and create a new Proxy Provider. Name it Emby. Select desired Authentication flow and Authorization flow. And select Forward Auth. External host is the Domain of Emby you configured in NPM. Under Advanced protocol settings make sure the scoop we have created is selected. once done, safe and then proceed to create an aplication for this provider. Do not forget to add the Provider just created to the Outpost. - NPM configurations: Edit the Emby Host in NPM. Under Advanced add this to Custom Nginx Configuration: (This is where i am currently stuck and need help) proxy_buffers 8 16k; proxy_buffer_size 32k; port_in_redirect off; location /authentik { proxy_pass $forward_scheme://$server:$port; proxy_set_header Upgrade $http_upgrade; auth_request /outpost.goauthentik.io/auth/nginx; error_page 401 = @goauthentik_proxy_signin; auth_request_set $auth_cookie $upstream_http_set_cookie; add_header Set-Cookie $auth_cookie; auth_request_set $authentik_auth $upstream_http_x_emby_token; proxy_set_header X-Emby-Token ${authentik_auth}; proxy_pass_header X-Emby-Token; } location /outpost.goauthentik.io { proxy_pass https://authentik-server:9443/outpost.goauthentik.io; proxy_set_header Host $host; proxy_set_header X-Original-URL $scheme://$http_host$request_uri; add_header Set-Cookie $auth_cookie; auth_request_set $auth_cookie $upstream_http_set_cookie; proxy_pass_request_body off; proxy_set_header Content-Length ""; } location @goauthentik_proxy_signin { internal; add_header Set-Cookie $auth_cookie; return 302 /outpost.goauthentik.io/start?rd=$request_uri; } Theoratically, Normal login should be at https://emby.domain so it does not breake Emby Clients. but when going to https://emby.domain/authentik it should redirect to authenticate with Authentik. In the process it would fetch a X-Emby-Token. tis would be passed as header to login as the user assosiated with Authentik. Sadly it does not work yet. Even when only using this Custom Nginx Configuration it still does not login. proxy_set_header X-Emby-Token "[token created in Authentik scoop]"; proxy_pass_header X-Emby-Token; What other headers are missing here to complete the Authentication into Emby? Edited December 30, 2024 by Relocate0604 clearfing an idea 1
Solution Luke 39617 Posted December 30, 2024 Solution Posted December 30, 2024 Hi, there isn't really any built-in support for this. It would have to be added. What you've done in part 1 is essentially the process for authenticating with your own app. There's no way (at this time), to take the result of that and tell the Emby web app to use it. 1
Relocate0604 3 Posted December 30, 2024 Author Posted December 30, 2024 I Understand. The request must be made from browser so that those headers have the correct value. X-Emby-Client: Emby Web X-Emby-Device-Name: Google Chrome Windows X-Emby-Device-Id: xxx X-Emby-Client-Version: 4.8.10.0 X-Emby-Token: xxx X-Emby-Language: en-gb
Luke 39617 Posted December 30, 2024 Posted December 30, 2024 Correct. Actually looking at the code, try putting userId and accessToken onto the query string when redirecting to the web app. We are checking that and using them. It may have been added for this very purpose. 1
Relocate0604 3 Posted December 31, 2024 Author Posted December 31, 2024 (edited) In NginX Proxy Manager, I tried passing headers to test if I can login. First I used this command to get the values needed: curl 'http://embyserver:port/Users/AuthenticateByName?api_key=[api key i created from admin panel]' -H 'Content-Type: application/json; charset=UTF-8' --data-raw '{"Username":"[my username]","Pw":"[my user passord]"}' --compressed Once I got the values, I passed them like this in NPM: location /authentik { proxy_pass $forward_scheme://$server:$port/web; proxy_set_header Upgrade $http_upgrade; proxy_set_header X-Emby-Token "[AccessToken value I got form the curl request]"; proxy_set_header AccessToken "[AccessToken value I got form the curl request]"; proxy_set_header X-Emby-ServerId "[ServerId value I got form the curl request]"; proxy_set_header X-Emby-Id "[UserId value I got form the curl request]"; proxy_set_header UserId "[UserId value I got form the curl request]"; proxy_pass_header X-Emby-Token; proxy_pass_header AccessToken; proxy_pass_header X-Emby-ServerId; proxy_pass_header X-Emby-Id; proxy_pass_header UserId; } I also tried redirecting to $forward_scheme://$server:$port/web?UserId=[UserId value I got form the curl request]&AccessToken=[AccessToken value I got form the curl request]; Sadly it did not work Edited December 31, 2024 by Relocate0604 missing information
Luke 39617 Posted December 31, 2024 Posted December 31, 2024 It might be case sensitive. Try userId and accessToken.
Relocate0604 3 Posted January 1 Author Posted January 1 using userId and accessToken in url lands me on black screen which means connection to the server cannot be completed.
Relocate0604 3 Posted January 2 Author Posted January 2 In Nginx i forward to proxy_pass $forward_scheme://$server:$port/web/index.html?userId=xyz&accessToken=xyz
Luke 39617 Posted January 8 Posted January 8 Testing it myself I'm not getting a black screen, although I'm not getting auto-logged in either. I'll have to try and recall what the purpose of those params were for, if it was for this after all. 1
Luke 39617 Posted March 1 Posted March 1 On 1/8/2025 at 1:48 PM, Luke said: Testing it myself I'm not getting a black screen, although I'm not getting auto-logged in either. I'll have to try and recall what the purpose of those params were for, if it was for this after all. You need to add &e=1. This may have just been a safeguard to avoid this getting accidentally misinterpreted as something else. I don't recall the specifics, but here is an example that works in my testing: http://localhost:8096/web/index.html?userId=abc&accessToken=xxx&e=1 1
Relocate0604 3 Posted March 4 Author Posted March 4 Thank you this solved it for me. - Property Mapping to fetch Emby AccessToken: import json from urllib.parse import urlencode from urllib.request import Request, urlopen if request.user.username == "": return "null" else: embyuser = request.user.attributes.get("emby_username", "") embypass = request.user.attributes.get("emby_password", "") base_url = "http://embyserver:80" end_point = "/Users/AuthenticateByName?api_key=embyapitoken" json_data = {'Username': embyuser,'Pw': embypass} postdata = json.dumps(json_data).encode() headers = {"Content-Type": "application/json; charset=UTF-8"} try: httprequest = Request(base_url + end_point, data=postdata, method="POST", headers=headers) with urlopen(httprequest) as response: responddata = json.loads(response.read().decode()) AccessToken = responddata['AccessToken'] UserId = responddata['User']['Id'] except: AccessToken = "null" UserId = "null" return {"ak_proxy": {"user_attributes": {"additionalHeaders": {"X-Emby-Uri": "/web/index.html?userId=" + UserId + "&accessToken=" + AccessToken + "&e=1"}}}} - NPM configurations: client_max_body_size 100M; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Sec-WebSocket-Extensions $http_sec_websocket_extensions; proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key; proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_redirect off; proxy_buffering off; location / { proxy_pass $forward_scheme://$server:$port; } location /ssoauth { proxy_set_header Upgrade $http_upgrade; auth_request /outpost.goauthentik.io/auth/nginx; error_page 401 = @goauthentik_proxy_signin; auth_request_set $auth_cookie $upstream_http_set_cookie; add_header Set-Cookie $auth_cookie; auth_request_set $authentik_embyuri $upstream_http_x_emby_uri; add_header X-Debug-Emby-URI $scheme://$host$authentik_embyuri; try_files "" @redirect; } location @redirect { add_header Set-Cookie $auth_cookie; return 302 $scheme://$host$authentik_embyuri; } location /outpost.goauthentik.io { proxy_pass https://authentik-server:9443/outpost.goauthentik.io; proxy_set_header Host $host; proxy_set_header X-Original-URL $scheme://$http_host$request_uri; add_header Set-Cookie $auth_cookie; auth_request_set $auth_cookie $upstream_http_set_cookie; proxy_pass_request_body off; proxy_set_header Content-Length ""; } location @goauthentik_proxy_signin { internal; add_header Set-Cookie $auth_cookie; return 302 /outpost.goauthentik.io/start?rd=$request_uri; } I was able to use Authentik to login I to the web ui with Authentik. 2
CuriousNoob 0 Posted yesterday at 03:47 AM Posted yesterday at 03:47 AM Oh wow, that looks promising. Bear with me, as I am absolutely beginner at NPM/Traefik/Authentik. Based on your latest post, you were able to use authentik to login to the web ui with authentik user credentials right? Was this directly into the user account that you added to authentik's user attribute? I tried to use AI (deepseek) to help me convert this NPM configuration into Traefik configuration but I failed horribly hahaha - I only somehow bypassed the manual login page and go to the select user where I had to manually login to the user I want. By any chance do you know how to set this up in traefik? Like what headers I'd need to pass through my traefik middleware so that I am able to achieve logging in to the Emby user attributed to the Authentik user after you successfully login to the authentik SSO page in front of Emby? It is a pity that Emby still doesn't support SSO in 2025 Cheers. On 3/4/2025 at 2:46 AM, Relocate0604 said: Thank you this solved it for me. - Property Mapping to fetch Emby AccessToken: import json from urllib.parse import urlencode from urllib.request import Request, urlopen if request.user.username == "": return "null" else: embyuser = request.user.attributes.get("emby_username", "") embypass = request.user.attributes.get("emby_password", "") base_url = "http://embyserver:80" end_point = "/Users/AuthenticateByName?api_key=embyapitoken" json_data = {'Username': embyuser,'Pw': embypass} postdata = json.dumps(json_data).encode() headers = {"Content-Type": "application/json; charset=UTF-8"} try: httprequest = Request(base_url + end_point, data=postdata, method="POST", headers=headers) with urlopen(httprequest) as response: responddata = json.loads(response.read().decode()) AccessToken = responddata['AccessToken'] UserId = responddata['User']['Id'] except: AccessToken = "null" UserId = "null" return {"ak_proxy": {"user_attributes": {"additionalHeaders": {"X-Emby-Uri": "/web/index.html?userId=" + UserId + "&accessToken=" + AccessToken + "&e=1"}}}} - NPM configurations: client_max_body_size 100M; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Sec-WebSocket-Extensions $http_sec_websocket_extensions; proxy_set_header Sec-WebSocket-Key $http_sec_websocket_key; proxy_set_header Sec-WebSocket-Version $http_sec_websocket_version; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_redirect off; proxy_buffering off; location / { proxy_pass $forward_scheme://$server:$port; } location /ssoauth { proxy_set_header Upgrade $http_upgrade; auth_request /outpost.goauthentik.io/auth/nginx; error_page 401 = @goauthentik_proxy_signin; auth_request_set $auth_cookie $upstream_http_set_cookie; add_header Set-Cookie $auth_cookie; auth_request_set $authentik_embyuri $upstream_http_x_emby_uri; add_header X-Debug-Emby-URI $scheme://$host$authentik_embyuri; try_files "" @redirect; } location @redirect { add_header Set-Cookie $auth_cookie; return 302 $scheme://$host$authentik_embyuri; } location /outpost.goauthentik.io { proxy_pass https://authentik-server:9443/outpost.goauthentik.io; proxy_set_header Host $host; proxy_set_header X-Original-URL $scheme://$http_host$request_uri; add_header Set-Cookie $auth_cookie; auth_request_set $auth_cookie $upstream_http_set_cookie; proxy_pass_request_body off; proxy_set_header Content-Length ""; } location @goauthentik_proxy_signin { internal; add_header Set-Cookie $auth_cookie; return 302 /outpost.goauthentik.io/start?rd=$request_uri; } I was able to use Authentik to login I to the web ui with Authentik.
Luke 39617 Posted yesterday at 03:54 PM Posted yesterday at 03:54 PM 12 hours ago, CuriousNoob said: Oh wow, that looks promising. Bear with me, as I am absolutely beginner at NPM/Traefik/Authentik. Based on your latest post, you were able to use authentik to login to the web ui with authentik user credentials right? Was this directly into the user account that you added to authentik's user attribute? I tried to use AI (deepseek) to help me convert this NPM configuration into Traefik configuration but I failed horribly hahaha - I only somehow bypassed the manual login page and go to the select user where I had to manually login to the user I want. By any chance do you know how to set this up in traefik? Like what headers I'd need to pass through my traefik middleware so that I am able to achieve logging in to the Emby user attributed to the Authentik user after you successfully login to the authentik SSO page in front of Emby? It is a pity that Emby still doesn't support SSO in 2025 Cheers. HI, what have you tried so far?
CuriousNoob 0 Posted yesterday at 06:49 PM Posted yesterday at 06:49 PM Hi, well to be honest I tried alot of suggestions, basically throwing everything at AI and hoping it would stick. I know that that is not the right way to go about doing things though, but after failing miserably (obviously), I decided to ask the forums. What I have so far, that sends into the Emby user selection page rather than directly into the user attributed to authentik, after logging into Authentik is: Authentik property mapping: import json from urllib.parse import urlencode from urllib.request import Request, urlopen if not request.user.is_authenticated: return "null" embyuser = request.user.attributes.get("emby_username", "") embypass = request.user.attributes.get("emby_password", "") base_url = "myembyurl" # Updated to your Emby URL end_point = "/Users/AuthenticateByName" json_data = {'Username': embyuser, 'Pw': embypass} postdata = json.dumps(json_data).encode() headers = { "Content-Type": "application/json; charset=UTF-8", "X-Emby-Token": "myapikey" # Your API key } try: httprequest = Request(base_url + end_point, data=postdata, method="POST", headers=headers) with urlopen(httprequest) as response: responddata = json.loads(response.read().decode()) AccessToken = responddata['AccessToken'] UserId = responddata['User']['Id'] except Exception as e: ak_logger.warning(f"Failed to authenticate with Emby: {str(e)}") AccessToken = "null" UserId = "null" return { "ak_proxy": { "user_attributes": { "additionalHeaders": { "X-Emby-Uri": f"/web/index.html?userId={UserId}&accessToken={AccessToken}&e=1" } } } } As for middlewares for traefik, I have: emby-auth: forwardAuth: address: "http://authentik-server:9000/outpost.goauthentik.io/auth/traefik" authResponseHeaders: - "Set-Cookie" - "X-Emby-Uri" trustForwardHeader: true emby-headers: headers: customRequestHeaders: X-Forwarded-Proto: "https" X-Original-URL: "https://myembyurl.com{path}" X-Forwarded-For: "{remote_addr}" X-Real-IP: "{remote_addr}" customResponseHeaders: X-Emby-Auth-Debug: "1" # 4. Security Headers (Optional but recommended) emby-security: headers: browserXssFilter: true contentTypeNosniff: true frameDeny: true sslRedirect: true stsIncludeSubdomains: true stsPreload: true stsSeconds: 31536000 # Rate limiting for API (optional but recommended) emby-api-rate-limit: rateLimit: burst: 5 average: 1 period: 10s # Combined chain (simplified) embyauth: chain: middlewares: - emby-auth - emby-headers - emby-security I tested the property mapping and I think it is returning the proper uri analogous to the solution OP provided. I am not confident in knowing what middleware I should pass for my traefik label. Forgive me for my ignorance. Cheers
Luke 39617 Posted yesterday at 07:05 PM Posted yesterday at 07:05 PM What address did it redirect to?
CuriousNoob 0 Posted yesterday at 07:23 PM Posted yesterday at 07:23 PM (edited) It redirects to: https://myembyurl/web/index.html#!/startup/login.html?serverId=myserverid Basically a page where I can select the user (shown in pic below) Just for clarity, I have the following as my labels for my emby container: labels: - "traefik.enable=true" ## Main Router (SSO-protected) - "traefik.http.routers.embymedia-secure.entrypoints=websecure_internal" - "traefik.http.routers.embymedia-secure.rule=Host(`myembyurl`) && !PathPrefix(`/Users/AuthenticateByName`)" - "traefik.http.routers.embymedia-secure.tls=true" - "traefik.http.routers.embymedia-secure.tls.certresolver=cloudflare" - "traefik.http.routers.embymedia-secure.middlewares=embyauth@file" - "traefik.http.routers.embymedia-secure.service=embymedia-svc" - "traefik.http.services.embymedia-svc.loadbalancer.server.port=8096" ## API Router (Bypasses Authentik) - "traefik.http.routers.embymedia-api.entrypoints=websecure_internal" - "traefik.http.routers.embymedia-api.rule=Host(`myembyurl`) && PathPrefix(`/Users/AuthenticateByName`)" - "traefik.http.routers.embymedia-api.tls=true" - "traefik.http.routers.embymedia-api.tls.certresolver=cloudflare" - "traefik.http.routers.embymedia-api.service=embymedia-svc" - "traefik.http.routers.embymedia-api.middlewares=emby-api-rate-limit@file" # Optional rate limiting The reason for this was due to being unable to curl emby using the API and username/password (it would just redirect to authentik based on the the results) Edited yesterday at 07:36 PM by CuriousNoob
Luke 39617 Posted 22 hours ago Posted 22 hours ago Why exactly can’t you just redirect to the URL example given above?
CuriousNoob 0 Posted 21 hours ago Posted 21 hours ago To be quite honest, I am not sure. I understand that the property mapping is supposed to return the X-Emby-Uri (which when tested in Authentik, properly returns this). As far as I understand the flow is basically supposed to be: 1) Access myembyurl and traefik redirect to Authentik login page which protects myembyurl domain. 2) Login to Authentik, and since there is the property mapping it returns the X-Emby-Uri and traefik is supposed to pass this, (I think it does based on my forwardauth middleware called emby-auth). 3) Traefik is supposed to pass that header and possibly some other headers (not too sure?) and once it authenticates it's supposed to redirect straight into the Emby user (I believe to https://myembyurl/web/index.html#!/home ?) I think I am stuck in middle of step 2, perhaps some headers are not passing properly or I just plain don't know what headers are to be passed. Cheers.
Luke 39617 Posted 17 hours ago Posted 17 hours ago Quote 3) Traefik is supposed to pass that header and possibly some other headers (not too sure?) and once it authenticates it's supposed to redirect straight into the Emby user (I believe to https://myembyurl/web/index.html#!/home ?) There's no way to pass a header to the web app. You have to just redirect with the values on the query string using the example url that was provided. That's how the value will get communicated. So the url would not be what you're thinking but rather the url given above.
CuriousNoob 0 Posted 2 hours ago Posted 2 hours ago Aha yes you are correct. I can indeed directly get to the Emby user using the access token by hardcoding the value into traefik's redirect. Unfortunately, so far I have discovered that traefik might not accept dynamic values (which X-Emby-Uri is), so temporarily I think this solution will not work in traefik. emby-auth: forwardAuth: address: "http://authentik-server:9000/outpost.goauthentik.io/auth/traefik" authResponseHeaders: - "X-Emby-Uri" - "Set-Cookie" trustForwardHeader: true emby-redirect: headers: customResponseHeaders: Location: "https://myurl.com{http.response.header.X-Emby-Uri}" The above snippet is my code which will not work. I also discovered that Regex Replacement will simply send me directly to the manual login page even if I hardcode the redirection link. Cheers
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now