- Go 100%
| .gitignore | ||
| cache.go | ||
| go.mod | ||
| go.sum | ||
| indieauth-bridge.service | ||
| main.go | ||
| main_test.go | ||
| README.md | ||
| store.go | ||
| urls.go | ||
IndieAuth Authentik Bridge
A small IndieAuth authorization server for 32bit.cafe. It uses Authentik for browser authentication, maps member identity URLs to immutable Authentik user UUIDs, and issues opaque IndieAuth authorization codes, access tokens, and rotating refresh tokens.
The OAuth authorization-server metadata document is served at:
https://indieauth.32bit.cafe/.well-known/oauth-authorization-server
Identity mapping
Every two minutes the daemon reads active users from the Authentik API and atomically replaces an on-disk bbolt key/value cache.
- Each value in a user's administratively managed
indieAuthSitesattribute maps to that user's immutable UUID. Do not expose this attribute for unrestricted self-service editing; it is an ownership assertion. username.32bit.cafeis included unlessindieAuthMainis explicitly false.- A bare domain is canonicalized to
https://domain/. - Profile URLs require HTTP(S), a domain name, no port, fragment, credentials,
or dot path segments. An empty path is normalized to
/. - Conflicting claims fail the entire refresh and retain the last-known-good cache.
- Service accounts and inactive users are excluded.
OAuth and IndieAuth behavior
- Authorization Code grant with mandatory PKCE S256
- Scope-less authentication codes redeem only at
POST /authorizeand returnme; they never produce access or refresh tokens - Authentik OIDC Authorization Code flow, also protected by PKCE and nonce
- Bounded exponential retry when Authentik's JWKS endpoint has a transient fetch failure; deterministic signature and claim failures are never retried
- Exact Authentik OIDC
sub-to-user-UUID ownership check - Optional
me, with identity selection after login when necessary - Explicit scope allowlist;
emailrequiresprofile, and refreshes may narrow an access token without narrowing the underlying refresh grant - Explicit downstream client consent after Authentik login
- Same-origin IndieAuth redirect URIs, or cross-origin URIs declared by the
public
client_idJSON metadata, HTTPLink, or HTML<link>metadata - Same-origin-only metadata redirects and special-use-address SSRF protections
- Opaque, hashed-at-rest authorization codes and tokens
- One-hour access tokens and rotating 30-day refresh tokens
- HTTP Basic-authenticated token introspection, revocation, and documented
legacy IndieAuth token verification via
GET /token - A last-known-good cache only while it is at most ten minutes old; startup and authorization fail closed without a fresh cache. Ownership is rechecked at login callback and atomically again when consent becomes a code.
- HTTPS-only hosted web clients. HTTP, loopback, and custom-scheme clients are intentionally outside this deployment's compatibility policy.
Build and test
Go 1.20 or newer is required.
go test ./...
go build -trimpath -ldflags='-s -w' -o indieauth-bridge .
For a Linux amd64 deployment from another platform:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags='-s -w' -o indieauth-bridge .
Configuration
The service reads configuration from environment variables:
| Variable | Purpose |
|---|---|
PUBLIC_BASE_URL |
Public issuer URL, normally https://indieauth.32bit.cafe |
AUTHENTIK_ISSUER |
Provider issuer, normally https://auth.32bit.cafe/application/o/indieauth/ |
AUTHENTIK_CLIENT_ID |
Confidential Authentik OAuth client ID |
AUTHENTIK_CLIENT_SECRET |
Confidential Authentik OAuth client secret |
AUTHENTIK_API_BASE |
Authentik API base, normally https://auth.32bit.cafe/api/v3 |
AUTHENTIK_API_TOKEN |
Token for a service account with only authentik_core.view_user |
INTROSPECTION_CLIENT_ID |
Resource-server credential used with HTTP Basic authentication |
INTROSPECTION_CLIENT_SECRET |
Separate high-entropy introspection secret |
TRUST_PROXY_HEADERS |
Set to true only behind the local trusted reverse proxy, for per-client rate limits |
LISTEN_ADDR |
Local listen address; defaults to 127.0.0.1:9084 |
DATA_PATH |
bbolt state path; defaults to /var/lib/indieauth/indieauth.db |
Keep the environment file root-readable only. The included systemd unit expects
it at /etc/indieauth/indieauth.env and runs the daemon as a dedicated
indieauth system user.
Authentik provider
Create a confidential OAuth2/OpenID provider and application with:
- Application slug:
indieauth - Strict redirect URI:
https://indieauth.32bit.cafe/oidc/callback - Issuer mode: per provider; subject mode: user UUID
- ID-token claims enabled
- Scope mappings:
openid,profile, andemail - Authorization flow: the normal Authentik implicit-consent provider flow
The bridge presents its own consent screen for the downstream IndieAuth client; Authentik's job is only to authenticate the member seamlessly.
HTTP endpoints
| Endpoint | Purpose |
|---|---|
/.well-known/oauth-authorization-server |
RFC 8414 metadata |
/authorize |
GET starts authorization; POST redeems scope-less login codes |
/oidc/callback |
Authentik callback |
/token |
Code and refresh grants; bearer-token verification on GET |
/introspect |
HTTP Basic-authenticated token introspection |
/revoke |
Token revocation |
/healthz |
Cache freshness and service health |
Apache or another TLS reverse proxy should forward this host to
http://127.0.0.1:9084/ and preserve the public hostname.