Skip to content

Extended authentication (SSO) #437

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
lazee486 opened this issue May 30, 2020 · 30 comments
Open

Extended authentication (SSO) #437

lazee486 opened this issue May 30, 2020 · 30 comments

Comments

@lazee486
Copy link

This software is amazing for homelab, I'd love if had the ability to use with

https://github.com/vouch/vouch-proxy/blob/master/README.md

Or keycloak

Basically any single sign on or similar system
It would be at the same step as your current password protection. Currently I have to put password so many times...

Instead of each service having its own(and some don't) any site you tag with auth gets proxied with sso

@ITNerdbox
Copy link

Also, certificate based authentication would be a nice to have feature.

@archness1
Copy link

OIDC/Keycloak integration would be awesome to have with this.

@fbartels
Copy link

#433 added the ability to do oidc

@joe307bad
Copy link

@MarioGK
Copy link

MarioGK commented Jun 25, 2021

Any updates on this issue?

@chaptergy
Copy link
Collaborator

It will not be in this version, unless someone from the community wants to implement it. See https://github.com/jc21/nginx-proxy-manager/discussions/1202.

@stibra
Copy link

stibra commented Sep 16, 2021

Please integrate NPM with spnego for Kerberos integration.

https://github.com/stnoonan/spnego-http-auth-nginx-module

@hairy-tortoise
Copy link

Any update on this?

@marekful
Copy link

marekful commented Feb 24, 2023

FYI #2630 @hairy-tortoise

@Hadatko
Copy link
Contributor

Hadatko commented Aug 25, 2023

+1

@fomurjiom
Copy link

Please integrate NPM with spnego for Kerberos integration.

https://github.com/stnoonan/spnego-http-auth-nginx-module

+1 pls

@alexsalex
Copy link

alexsalex commented Feb 10, 2024

+100500

Authentik implementation will be amazing too!

Copy link

Issue is now considered stale. If you want to keep it open, please comment 👍

@github-actions github-actions bot added the stale label Aug 30, 2024
@dimo414
Copy link

dimo414 commented Aug 30, 2024

Stalebot is a blight

@stibra
Copy link

stibra commented Aug 30, 2024

KeyCloak and Kerberos please.

@lazee486
Copy link
Author

just an update, by using the section where you can post your own Nginx commands on a proxy. NPM does work with Authelia and authentik that ive tested, as a domain level auth. ie: if you go to radarr.mysite.com, it will redirect you to authentik sso page, sign in, then store and use that cookie so going to sonarr.mysite.com or any other site behind your sso becomes passwordless. you also have to configure the apps to accept the SSO or no password to make it seamless, but this does work.

so unless this is a request for the addition of maybe presaved configs or per app buttons for this, it works and Im happy. :)

@MahmoudAlyuDeen
Copy link

MahmoudAlyuDeen commented Oct 17, 2024

NPM does work with Authelia and authentik that ive tested

The reverse proxy functionality of NPM works with Authentik / Authelia / other tools.

But the web GUI of NPM itself doesn't work:

It would be awesome if we don't have to enter an email or password to get to NPM settings.

@github-actions github-actions bot removed the stale label Oct 20, 2024
@moutasem1989
Copy link

moutasem1989 commented Oct 24, 2024

Here is how I set up Authentik to log into NginX Proxy Manager UI:

In this case i created A group with special permition to log into several services but you can do this on user level. In the group/user add the following Attributes with the correct user/pass. Leave the Token as Null

nginx_password: pass
nginx_username: user
additionalHeaders:
  X-Nginx-Token: null

Under Property Mappings create a new Scoop Maping. Name is NginX Token and Scoop Name must be ak_proxy otherwise NginX cannot call the apropeate headers. Adjust the Expression from group_attributes() to attributes for user based authentication.
The Expression should be as following:

import json
from urllib.parse import urlencode
from urllib.request import Request, urlopen

if request.user.username == "":
  return ("null")
else:
  nginxuser = request.user.group_attributes().get("nginx_username", "placeholderuser")
  nginxpass = request.user.group_attributes().get("nginx_password", "placeholderpassword")

base_url = "http://nginx:81"
end_point = "/api/tokens"
json_data = {'identity': nginxuser,'secret': nginxpass}
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-Nginx-Token": responddata['token']}}}}
except: return ("null")

The Expression will fetch a new Autherization Token which can be accessed through the X-Nginx-Token Header.
Create a Proxy Provider and make sure the Scoop we just created is included.
In NPM I added this configuration. Dnt forget to change the Authentik Server address

proxy_buffers 8 16k;
proxy_buffer_size 32k;

# Make sure not to redirect traffic to a port 4443
port_in_redirect off;

location / {
    proxy_pass          $forward_scheme://$server:$port;

    ##############################
    # authentik-specific config
    ##############################
    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;

    # Here we call the Header we created and use the Token that Authentik fetched for us
    auth_request_set $authentik_auth $upstream_http_x_nginx_token;
    proxy_set_header Authorization "Bearer ${authentik_auth}";
    proxy_pass_header Authorization;
}

# all requests to /outpost.goauthentik.io must be accessible without authentication
location /outpost.goauthentik.io {
    # When using the embedded outpost, use:
    proxy_pass              https://authentik-server:9443/outpost.goauthentik.io;

    # Note: ensure the Host header matches your external authentik URL:
    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 "";
}

# Special location for when the /auth endpoint returns a 401,
# redirect to the /start URL which initiates SSO
location @goauthentik_proxy_signin {
    internal;
    add_header Set-Cookie $auth_cookie;
    return 302 /outpost.goauthentik.io/start?rd=$request_uri;
    # For domain level, use the below error_page to redirect to your authentik server with the full redirect path
    # return 302 https://authentik.company/outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri;
}

That should be it. I tried it and it works perfectly

edit code to handle exceptions

@MahmoudAlyuDeen
Copy link

@moutasem1989 great approach!

I tried the custom property mapping with user properties and I got this exception when navigating to the proxied page, same when using the test function from authentik admin panel:

Traceback (most recent call last): File "nginx-token", line 27, in <module> File "nginx-token", line 17, in handler File "/usr/local/lib/python3.12/json/__init__.py", line 346, in loads return _default_decoder.decode(s) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/json/decoder.py", line 337, in decode obj, end = self.raw_decode(s, idx=_w(s, 0).end()) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/json/decoder.py", line 355, in raw_decode raise JSONDecodeError("Expecting value", s, err.value) from None json.decoder.JSONDecodeError: Expecting value: line 5 column 1 (char 4)

@moutasem1989
Copy link

@MahmoudAlyuDeen The code above makes a call to Nginx to retrieve an authentication code in JSON file. If the server cannot be reached because of the wrong host name or IP address or simply bad authentication values it will throw an error. Make sure the Authentik server and NginX are on the same network and try the following. It should return the entire JSON file not just the token. If it is also unsuccessful, then a connection was not possible.

comment out all the lines after with urlopen… and add this and try the test function.

with urlopen(httprequest) as response:
     return(response.read().decode())

In Terminal you can also try this to see if an authentication token can be fetched. The bove code is the express of this curl command:

curl 'http://nginx:81/api/tokens' \
  -H 'Content-Type: application/json; charset=UTF-8' \
  --data-raw '{"identity":"[Your Email]","secret":"[Your Secret]"}' \
  --compressed

Hope it works out!

@MahmoudAlyuDeen
Copy link

Okay, I figured out one problem with your help, thanks! 😅 I had the external URL in the expression but switching to the internal URL of NPM works.

Testing the scope mapping in authentik generates a valid token now.

image

🤔 Somehow I still get this error when navigating to the proxied page:

image

This is the same error I get when I test the scope mapping with a user, that doesn't have the appropriate attributes.

@moutasem1989
Copy link

moutasem1989 commented Oct 25, 2024

If you added the attributes to the group use group_attributes() otherwise it would be a user attribute and should be replaced with attributes. Check that the attributes names in User/Group are identical to the ones called in the code. Only Users/Groups with attributes names can log in.

I did some changes to handle exceptions. Check the Property Mapping code: I made it so if a user has no name it will return null instead of throwing an error trying to retrieve values

Alternatively you can directly set the values in the code.

@MahmoudAlyuDeen
Copy link

Nice! No more exceptions. But now login doesn't work, I'm now just getting redirected to NPM login page.

@moutasem1989
Copy link

Also try it incognito mode cuz cookies could mess things up

@MahmoudAlyuDeen
Copy link

Tried in incognito.

Looking at the logs, the token is there with the correct scope. ✅

But there are also default scopes that I suspect could be confusing NPM, when I try removing the default scopes from the provider, they just keep coming back.

image

Is there any configuration I need to do on NPM side? Other than the custom nginx code you posted?

@moutasem1989
Copy link

moutasem1989 commented Oct 25, 2024

The scoop is there but you need to check the headers

    auth_request_set $authentik_auth $upstream_http_x_nginx_token;
    proxy_set_header Authorization "Bearer ${authentik_auth}";
    proxy_pass_header Authorization;

$upstream_http_x_nginx_token calls for the value of X-Nginx-Token attribute. Check if the user has the right to get the token and then check if the attribute value is being called. The header Authorization should be “Bearer <token>”.

@lazee486
Copy link
Author

I mean I know it could be an issue if it broke, but in docker restrict port 81 to localhost and use npm to access it, and put authentik etc there...if it broke just remove the interface restriction on the docker till you fixed it

@phoenix-limited
Copy link

The scoop is there but you need to check the headers

    auth_request_set $authentik_auth $upstream_http_x_nginx_token;
    proxy_set_header Authorization "Bearer ${authentik_auth}";
    proxy_pass_header Authorization;

$upstream_http_x_nginx_token calls for the value of X-Nginx-Token attribute. Check if the user has the right to get the token and then check if the attribute value is being called. The header Authorization should be “Bearer <token>”.

@moutasem1989 Is this method still working, even with commit 280bac8 causing problems with some of the code here?

I'm in the same boat as @MahmoudAlyuDeen where my scope tests fine with a token, but I get redirected to the login page.

With "custom locations" not working, can you define locations in the advanced field? Depending on the configuration, I can quickly get an error 500

@apocaliss92
Copy link

apocaliss92 commented Dec 7, 2024

Tried this approach and getting redirect on the login page too, mapping seems fine on authentic
The token check seems failing, returns a bad request error but no further details unfortunately

Actually something is there
[12/7/2024] [11:46:42 AM] [Express ] › ⚠ warning Existing token contained invalid user data

@exenza
Copy link

exenza commented Apr 25, 2025

Tried this approach and getting redirect on the login page too, mapping seems fine on authentic The token check seems failing, returns a bad request error but no further details unfortunately

Actually something is there [12/7/2024] [11:46:42 AM] [Express ] › ⚠ warning Existing token contained invalid user data

get same error, and when I test the Property mappings I get the following:

Traceback (most recent call last): File "/authentik/core/models.py", line 984, in evaluate return evaluator.evaluate(self.expression) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/authentik/core/expression/evaluator.py", line 89, in evaluate return super().evaluate(*args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/authentik/lib/expression/evaluator.py", line 263, in evaluate raise exc File "NgiX Token", line 23, in <module> File "NgiX Token", line 9, in handler builtins.TypeError: 'dict' object is not callable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests