A minimal Flask service that implements /.well-known/webfinger to help Tailscale discover your Authentik OpenID Connect provider. Built with uv and designed for local or containerised runs.
- Overview
- Architecture at a glance
- Features
- Prerequisites
- Quick start
- Docker
- Configuration
- Health
- Endpoint
- Production notes
- Development
- Troubleshooting
- Licence
- Security
- Contributing
- Support
The service responds to WebFinger queries by constructing an issuer URL from your DOMAIN and proxying relevant metadata. It exposes a health endpoint for orchestration systems and runs under Flask or Gunicorn.
- Flask app factory with
app:appWSGI target - WebFinger endpoint at
/.well-known/webfinger - Health endpoint
GET /health
- WebFinger responder at
/.well-known/webfinger - Issuer URL built dynamically from
DOMAIN - Optional
APPLICATIONslug support for Authentik - Response caching with
CACHE_TTL - Configurable upstream timeouts
/healthendpoint for liveness checks- Prebuilt container image on GHCR
- Docker / Kubernetes
- (Alternatively) uv and Python 3.13 for local development
Local development with uv
uv sync --all-extras
export DOMAIN=auth.example-domain.com
uv run flask --app app:app run --host 0.0.0.0 --port ${PORT:-8000}Pull and run
docker pull ghcr.io/sudo-kraken//authentik-webfinger-proxy:latest
docker run --rm -p 8000:8000 \
-e DOMAIN=auth.example-domain.com \
ghcr.io/sudo-kraken//authentik-webfinger-proxy:latestYou can deploy the app on Kubernetes using the published Helm chart:
helm install authentik-webfinger-proxy oci://ghcr.io/sudo-kraken/helm-charts/authentik-webfinger-proxy \
--namespace authentik-webfinger-proxy --create-namespace| Variable | Required | Default | Description |
|---|---|---|---|
| PORT | no | 8000 | Port to bind |
| WEB_CONCURRENCY | no | 2 | Gunicorn worker processes |
| DOMAIN | yes | Domain of your Authentik issuer host | |
| APPLICATION | no | tailscale | Authentik application slug |
| CACHE_TTL | no | 300 | Cache IDP endpoints seconds |
| REQUEST_TIMEOUT | no | 10 | HTTP timeout seconds for outbound calls |
.env example
PORT=8000
WEB_CONCURRENCY=2
DOMAIN=auth.example-domain.com
APPLICATION=tailscale
CACHE_TTL=300
REQUEST_TIMEOUT=10GET /healthreturns{ "ok": true }
GET /.well-known/webfinger?resource=acct:user@example.com
Example
curl "http://localhost:8000/.well-known/webfinger?resource=acct:user@example.com"- Expose the service on the network domain referenced by
DOMAIN. Your reverse proxy should terminate TLS. - Keep
REQUEST_TIMEOUTconservative and cache metadata withCACHE_TTLto reduce upstream calls.
uv run ruff check --fix .
uv run ruff format .
uv run pytest --cov- If the issuer cannot be resolved, check
DOMAINand ensure the upstream IDP endpoints are reachable from the container. - If you receive 404 responses, confirm the
resourcequery is present and correctly formatted.
This project is licensed under the MIT Licence. See the LICENCE file for details.
If you discover a security issue, please review and follow the guidance in SECURITY.md, or open a private security-focused issue with minimal details and request a secure contact channel.
Feel free to open issues or submit pull requests if you have suggestions or improvements. See CONTRIBUTING.md
Open an issue with as much detail as possible, including your environment details and relevant logs or output.