nginxproxy

NginX redirect to variable value with out by passing proxy authentication


I am running an Emby Server behind NginX Proxy Manager. Although Emby does not support SSO, I was still able to log in to the Web UI with the URL: schenme://emby.domain.com/web/index.html?userId=abc&accessToken=xxx&e=1. This URI to authenticate into Emby can be generated in Authentik and the value can be passed after authentication. I need help doing that in NginX.

I will write the steps and method I used in case it was relevant.

In Authentik > Admin Interface > Directory > Users: Edit the desired users to add emby authentication. Simply add the following values in the Attributes section:

emby_password: ****
emby_username: abc

In Authentik > Admin Interface > Customization > Property Mappings and create a new Scope Maping. The Name will be “Emby Token” and the Scope Name ”ak_proxy”. The expression needs an API Token that you can get from Emby UI. Don’t forget to edit the URL so Authentik has access to Emby:

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=xyz"
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"}}}}

If Authentik has access to Emby Server and the atribiuts to login are correctly provided, this scope should return /web/index.html?userId=abc&accessToken=xxx&e=1 that can be used to login.

After that I created an Aplication, Provider and added them to the Outpost. Authentication workes as intended.Trying to pass this value in NginX Proxy Manager as following is sadly not working:

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_http_version 1.1;
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 = gnin;
    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;
    rewrite ^ $authentik_embyuri;
    proxy_pass  $forward_scheme://$server:$port/;
}
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 gnin {
    internal;
    add_header Set-Cookie $auth_cookie;
    return 302 /outpost.goauthentik.io/start?rd=$request_uri;
}

By going to schenme://emby.domain.com/ssoauth I am promoted to login with Authentik. After authentication, the uri to login is loaded using auth_request_set $authentik_embyuri $upstream_http_x_emby_uri;. Inspecting the value and testing it I am able to login. However, rewriting the uri from /SSO auto into $authentik_embyuri using rewrite ^ $authentik_embyuri; is not working. Using return bypass the Authentik login process and the login uri is not loaded.

I hope the endgoal and method are clear.

The question remains: how can I manager to redirect from /SSO auto into the returned value of $authentik_embyuri in NginX? Can I do this by overwriting the Location header (trying this did not work)?


Solution

  • Each HTTP request passes through a sequence of request processing phases. Your rewrite directive doesn't work properly because it is executed during the NGX_HTTP_REWRITE_PHASE (as well as the return directive), when the $authentik_embyuri variable value hasn't been evaluated yet, since the auth_request directive is executed during the later NGX_HTTP_ACCESS_PHASE.

    To achieve your goal, you need to return a redirect to the new URL after the NGX_HTTP_ACCESS_PHASE is completed and the $authentik_embyuri variable value has been evaluated. Nginx has a dedicated directive, internal_redirect, to do this; unfortunately, it is available only as part of a commercial subscription. A typical way to achieve the same result using open-source nginx is to use the try_files directive, which is executed during the even later NGX_HTTP_PRECONTENT_PHASE (more details here).

    Here is the configuration that might work for you (I moved one of the auth_request_set directives since they should be declared along with the auth_request directive and made several other adjustments):

    location /ssoauth {
        error_page       401 = gnin;
        auth_request     /outpost.goauthentik.io/auth/nginx;
        auth_request_set $auth_cookie $upstream_http_set_cookie;
        auth_request_set $authentik_embyuri $upstream_http_x_emby_uri;
        try_files        "" @redirect;
    }
    location /outpost.goauthentik.io {
        internal;
        proxy_pass              https://authentik-server:9443;
        proxy_set_header        Host $host;
        proxy_set_header        X-Original-URL $scheme://$http_host$request_uri;
        proxy_pass_request_body off;
        proxy_set_header        Content-Length "";
    }
    location @redirect {
        add_header Set-Cookie $auth_cookie;
        return     302 $authentik_embyuri;
    }
    location gnin {
        internal;
        add_header Set-Cookie $auth_cookie;
        return     302 /outpost.goauthentik.io/start?rd=$request_uri;
    }