Skip to content

WIP: auth-user-header: Support for Authelia, etc.#1579

Open
binwiederhier wants to merge 6 commits intomainfrom
user-header
Open

WIP: auth-user-header: Support for Authelia, etc.#1579
binwiederhier wants to merge 6 commits intomainfrom
user-header

Conversation

@binwiederhier
Copy link
Owner

@binwiederhier binwiederhier commented Jan 30, 2026

This implements #601. It partially works. I tested it with Authelia.

What works

  • The configured auth-user-header (e.g. Remote-User) is passed from Authelia to ntfy, and ntfy then uses that header to pick the configured user in the backend
  • The web app redirects to Authelia when not logged in (this was very difficult due to aggressive service worker caching!)
  • The web app displays a logout button if auth-logout-url is configured, which allows you to log out via Authelia
image

What doesn't work

  • There seems to be a race when loading the web app that first initializes the database using "no user" (the ntfy IndexedDB is used instead of ntfy-$username), which leads to the wrong topics being shown in the sidebar. This can be easily reproduced when switching between users.
  • ❓ ❓ I have no idea how the Android app is supposed to work at all with this.
image

Setup

ntfy/server.yml

auth-file: ...
auth-user-header: Remote-User
auth-logout-url: 

Caddyfile

auth.dev.ntfy.sh {
  tls /etc/letsencrypt/live/dev.ntfy.sh/fullchain.pem /etc/letsencrypt/live/dev.ntfy.sh/privkey.pem
  reverse_proxy authelia:9091
}

ntfy.dev.ntfy.sh {
  tls /etc/letsencrypt/live/dev.ntfy.sh/fullchain.pem /etc/letsencrypt/live/dev.ntfy.sh/privkey.pem

  forward_auth authelia:9091 {
    uri /api/verify?rd=https://auth.dev.ntfy.sh/
    copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
  }

  reverse_proxy host.docker.internal:2586
}

authelia/configuration.yml

server:
  address: tcp://0.0.0.0:9091

log:
  level: info

authentication_backend:
  file:
    path: /config/users.yml
    password:
      algorithm: argon2id

access_control:
  default_policy: one_factor

session:
  secret: "REPLACE_WITH_LONG_RANDOM_1________________________________________"
  cookies:
    - domain: "dev.ntfy.sh"
      authelia_url: "https://auth.dev.ntfy.sh"
  expiration: 1h
  inactivity: 5m

storage:
  encryption_key: "REPLACE_WITH_LONG_RANDOM_2________________________________________"
  local:
    path: /config/db.sqlite3

notifier:
  filesystem:
    filename: /config/notification.txt

identity_validation:
  reset_password:
    jwt_secret: "REPLACE_WITH_LONG_RANDOM_3________________________________________"

docker-compose.yml

services:
  caddy:
    image: caddy:2
    ports:
      - "443:443/tcp"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - /etc/letsencrypt:/etc/letsencrypt:ro
      - caddy_data:/data
      - caddy_config:/config
    networks: [auth]
    extra_hosts:
      - "host.docker.internal:host-gateway"

  authelia:
    image: authelia/authelia:latest
    volumes:
      - ./authelia:/config
    networks: [auth]

networks:
  auth: {}

volumes:
  caddy_data: {}
  caddy_config: {}

@binwiederhier binwiederhier changed the title WIP: User header WIP: auth-user-header: Support for Authelia, etc. Feb 1, 2026
@helmut72
Copy link

helmut72 commented Feb 1, 2026

ntfy.dev.ntfy.sh {
  tls /etc/letsencrypt/live/dev.ntfy.sh/fullchain.pem /etc/letsencrypt/live/dev.ntfy.sh/privkey.pem

  forward_auth authelia:9091 {
    uri /api/verify?rd=https://auth.dev.ntfy.sh/
    copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
  }

  reverse_proxy host.docker.internal:2586
}

Don't you need an exception for example for curl? For example: if header contains Authorization, don't do auth on an IDP (here: authelia), but forward it directly to ntfy as usual? Better would be an api path execption like /api, but this doesn't exist. Then I thought to bypass with an header check.

Because curl and other integrations and automations never can support all auth types, for example passkeys. Header based authentication is more for webapps that are used by humans and to avoid creating in every app an own user account.

A real good solution would be integrating oidc directly into ntfy. Header based auth has a flaw. What if user will be disabled in the idp (here: auhelia)? A cronjob need to check if user is disabled on idp (here: authelia) and disable or delete it in the app (here: ntfy), because a disabled user could still use the api keys...

@JoeJoeTV
Copy link

JoeJoeTV commented Feb 1, 2026

I would maybe agree that since there is no separate api route that can be easily filtered, that makes it a bit more diffisult to use nfty with anything that would use just a token, so OIDC seems like a good solution, as it does not change how tokens work and basically just "replaces" the username + password authentication and of course facillitates creation of new users.

@simonfelding
Copy link

simonfelding commented Feb 1, 2026

@helmut72 @JoeJoeTV You guys are wrong. This header is meant to effectively bypass authentication. You can use it with https://github.com/oauth2-proxy/oauth-proxy if you want oidc.

The point is to keep ntfy.sh simple and allow for outsourcing auth to something else. If the user is disabled, your auth proxy obviously should not forward the request with the auth header.

@binwiederhier regarding how the android app should work: can it maybe open a webview to follow redirects if it gets one that doesn't lead to a ntfy.sh server, and save the session cookie? That's all you need for it to work with any normal auth proxy.

@JoeJoeTV
Copy link

JoeJoeTV commented Feb 1, 2026

@helmut72 @JoeJoeTV You guys are wrong. This header is meant to effectively bypass authentication. You can use it with https://github.com/oauth2-proxy/oauth-proxy if you want oidc.

The point is to keep ntfy.sh simple and allow for outsourcing auth to something else. If the user is disabled, your auth proxy obviously should not forward the request with the auth header.

But let's way you have a reverse proxy that handles the authetication request and sets the header before sending the actual request to ntfy.
If there are no extra exclusion rules here, authentication tokens set in ntfy are effectively useless, because the reverse proxy will always have to make the authentication request to the IDP. If your client is just e.g. curl or the ntfy cli app, then you will not easily be able to log in without doing it externally and setting cookies or IDP auth tokens manually.
If the IDP has a token system similarly to ntfy, this could not be much of a problem, but at least I don't think Authelia has such a system.
You could set up your reverse proxy to let requests with a toke through, but since it can't verify it, you could effectively bypass authentication this way, since, again, these tokens are managed by ntfy.

@JoeJoeTV
Copy link

JoeJoeTV commented Feb 1, 2026

@binwiederhier regarding how the android app should work: can it maybe open a webview to follow redirects if it gets one that doesn't lead to a ntfy.sh server, and save the session cookie? That's all you need for it to work with any normal auth proxy.

This seems more complex to me than just having a redirect by the web app which gets handled by the app, like other services do with their e.g. mobile apps.
This also makes it easy to manage these token like normal authentication tokens that already exist in ntfy.

@simonfelding
Copy link

You can set up another endpoint for token auth, there's no rule this endpoint has to be the only one. If you're using something like authelia or oauth-proxy or a custom setup, then you you should use an auth method they support. This PR doesn't mean that other auth methods will stop existing

@JoeJoeTV
Copy link

JoeJoeTV commented Feb 1, 2026

You can set up another endpoint for token auth, there's no rule this endpoint has to be the only one. If you're using something like authelia or oauth-proxy or a custom setup, then you you should use an auth method they support. This PR doesn't mean that other auth methods will stop existing

I'm not really sure what you're referring to.
I currently have things that use authentication tokens generated in ntfy (e.g. using the ntfy token command or via the web UI). These can have expiration time and labels and are easily vieweable inside the ntfy web app.
Since ntfy does not have just a single route which these token can be used with, I can't have an exception for the api route, so I can still use ntfy tokens.
Can you explain to me how I can still use ntfy tokens while also having header authentication set-up using e.g. Authelia?

@JoeJoeTV
Copy link

JoeJoeTV commented Feb 1, 2026

I am interested in support for external authentication to be added to ntfy (since I am trying to put most services in SSO), but if I use it, I would still like to use all the features provided by ntfy.

EDIT: Additionally, if I want to use ntfy for notifications somewhere, I would like to not just put my username and password for the entire IDP account there, but just have it limited to ntfy and also be easily revokable without changing the passwort of the account.
This is usually what these tokens are best at, in addition to making it easier for programmatic API access.

@helmut72
Copy link

helmut72 commented Feb 2, 2026

You can set up another endpoint for token auth, there's no rule this endpoint has to be the only one.

You mean a path exception like for example /api, that is rewritten to the backend to /? ntfy.example.com/api/mytopic became ntfy.example.com/mytopic?

if you want oidc

Don't get me wrong. I only mentioned oidc because it's the smartest solution.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants