Jump to content

Login into Web App with HTTP Headers: How can i make it happen?


Go to solution Solved by Luke,

Recommended Posts

Relocate0604
Posted (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 CustomizationProperty 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 AuthExternal 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 by Relocate0604
clearfing an idea
  • Like 1
  • Solution
Posted

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.

  • Like 1
Relocate0604
Posted

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
Posted

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.

  • Agree 1
Relocate0604
Posted (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 by Relocate0604
missing information
Posted

It might be case sensitive. Try userId and accessToken.

Relocate0604
Posted

using userId and accessToken in url lands me on black screen which means connection to the server cannot be completed.

Posted

With what url?

Relocate0604
Posted

In Nginx i forward to proxy_pass  $forward_scheme://$server:$port/web/index.html?userId=xyz&accessToken=xyz

Posted

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.

  • Like 1
  • 1 month later...
Posted
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

 

  • Agree 1
Relocate0604
Posted

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.

  • Thanks 2
  • 1 month later...
Posted

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.

 

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

Posted

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

Posted (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)

image.thumb.png.4920895b659f18c1de79339f346749cf.png

Edited by CuriousNoob
Posted

Why exactly can’t you just redirect to the URL example given above?

CuriousNoob
Posted

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.

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

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

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...