goauthentik / authentik

The authentication glue you need.
https://goauthentik.io
Other
12.73k stars 850 forks source link

Redirect Loop on Proxy Auth for Homeassistant #10838

Open akorb90 opened 1 month ago

akorb90 commented 1 month ago

Hello everyone,

I'm struggling on setting up authentication through authentik for homeassistant.

I've configured Authentik and Home Assistant according to the manuel: https://docs.goauthentik.io/integrations/services/home-assistant/

When I access homeassistant.public.domain i get redirected to authentik. After authenticating there I get into a redirect loop. The URL looks like this: https://auth.public.domain/if/flow/default-provider-authorization-explicit-consent/?client_id=EMeLuBu9zfN9Lao20TBf3qXwoM9iCfnwOovuhgrR&redirect_uri=https%3A%2F%2Fhomeassistant.public.domain%2Foutpost.goauthentik.io%2Fcallback%3FX-authentik-auth-callback%3Dtrue&response_type=code&scope=profile+openid+email+ak_proxy&state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnb2F1dGhlbnRpay5pby9vdXRwb3N0L0VNZUx1QnU5emZOOUxhbzIwVEJmM3FYd29NOWlDZm53T292dWhnclIiLCJzaWQiOiJGSkNUNk5ZUUYzS1I3VlFFWTJJNEpDQ0RXU0VNV09IUVo3T1NXREFTVVJZWVEyWFJYRk9aQkFNUlRHQTJSMkZDMkQ0V1lZT0pNVVIySklHNTZHV01FMkRPUzNCRUszNzZMVzdDNk1ZIiwic3RhdGUiOiJIYXlLZ0NMQWwydHR3SEtLdDhSZXRwaDQ2MjEteHl0MFlfZlBKTzF6LXcwIiwicmVkaXJlY3QiOiJodHRwczovL2hvbWVhc3Npc3RhbnQua29yYi5zeXN0ZW1zL2xvdmVsYWNlIn0.CtuLYz05pjPyvNTvct31jz73MZRUGIgJZ8i9FrbFWeU

This is my nginx config:

# Upgrade WebSocket if requested, otherwise use keepalive
map $http_upgrade $connection_upgrade_keepalive {
    default upgrade;
    ''      '';
}

################## upstream config ####################

upstream homeassistant_public_domain {
    server akohome01.home.public.domain:8123;
    # Improve performance by keeping some connections alive.
    keepalive 10;
}

################### server config #####################

server {
    # HTTP server config
    listen 80;
    server_name _;

    # 301 redirect to HTTPS
    return 301 https://$host$request_uri;
}

server {
    # SSL and VHost configuration
    listen                  443 ssl;
    http2                   on;
    server_name             _;

    access_log              /var/log/nginx/access_homeassistant.log combined;
    error_log               /var/log/nginx/error_homeassistant.log warn;

    ssl_certificate         /etc/letsencrypt/live/public.domain/fullchain.pem;
    ssl_certificate_key     /etc/letsencrypt/live/public.domain/privkey.pem;

    # Increase buffer size for large headers
    # This is needed only if you get 'upstream sent too big header while reading response
    # header from upstream' error when trying to access an application protected by goauthentik
    proxy_buffers 8 16k;
    proxy_buffer_size 32k;

    location / {
        # Put your proxy_pass to your application here, and all the other statements you'll need
         proxy_pass http://homeassistant_public_domain;
        # proxy_set_header Host $host;
        # proxy_set_header ...
        # Support for websocket
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade_keepalive;

        ##############################
        # 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;

        # translate headers from the outposts back to the actual upstream
        auth_request_set $authentik_username $upstream_http_x_authentik_username;
        auth_request_set $authentik_groups $upstream_http_x_authentik_groups;
        auth_request_set $authentik_email $upstream_http_x_authentik_email;
        auth_request_set $authentik_name $upstream_http_x_authentik_name;
        auth_request_set $authentik_uid $upstream_http_x_authentik_uid;

        proxy_set_header X-authentik-username $authentik_username;
        proxy_set_header X-authentik-groups $authentik_groups;
        proxy_set_header X-authentik-email $authentik_email;
        proxy_set_header X-authentik-name $authentik_name;
        proxy_set_header X-authentik-uid $authentik_uid;
    }

    # all requests to /outpost.goauthentik.io must be accessible without authentication
    location /outpost.goauthentik.io {
        # When using the embedded outpost, use:
        proxy_pass              http://akoauth01.home.public.domain:9000/outpost.goauthentik.io;
        # For manual outpost deployments:
        # proxy_pass            http://outpost.company:9000;

        # 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;
    }
}

Im running authentik 2024.6.1 via docker-compose with the integrated outpost

Anyone got an idea?

jacobrian commented 1 month ago

Remove:

    proxy_buffer_size 32k;
    proxy_set_header Connection $connection_upgrade_keepalive;

Add:

    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

Uncomment: proxy_set_header Host $host;

Set your Outpost proxy_pass to use the http://[internal_IP]:9000/outpost.goauthentik.io;

This is what I had to do in NPM to make this work, got it working yesterday.

davispuh commented 4 weeks ago

I have same issue with different app but I can't change proxy_set_header Host to IP because I'm relying on SNI and it's on another server.

This is how my config looks like

 location /outpost.goauthentik.io {
        proxy_pass              https://auth.example.org/outpost.goauthentik.io;

        # This doesn't work due to using SNI
        #proxy_set_header        Host $host
        # So need to set explicitly 
        proxy_set_header        Host Auth.example.org;

        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 "";
}
davispuh commented 3 weeks ago

I figured it out. In Outpost ENV variable AUTHENTIK_HOST I had it as AUTHENTIK_HOST=https://Auth.example.org (note the capital A). Then in logs I noticed

{"error":"oidc: id token issued by a different provider, expected \"https://Auth.example.org/application/o/app/\" got \"https://auth.example.org/application/o/app/\"","event":"failed to redeem code","level":"warning","logger":"authentik.outpost.proxyv2.application","name":"Provider","timestamp":"2024-08-16T14:03:08Z"}

This was such an obscure issue... But it's a bug because domain names should be case insensitive. So anyway need to set AUTHENTIK_HOST all lowercase and now it works.

This is also why previously proxy_set_header Host $host wasn't working at all but proxy_set_header Host Auth.example.org; caused infinite redirect. Also looks like Host header is not really needed at all, it works fine without it.