AtAuth
1# ATAuth - OIDC Provider for AT Protocol
2
3An OpenID Connect (OIDC) Provider that uses AT Protocol OAuth (Bluesky) as the identity source. Authenticate users via their `@handle` -- works with Bluesky or any self-hosted PDS.
4
5## What It Does
6
7ATAuth sits between your apps and AT Protocol identity:
8
9```text
10Your App (OIDC Client)
11 |
12 v
13ATAuth (OIDC Provider)
14 |
15 v
16User's PDS (Bluesky / self-hosted)
17 |
18 v
19User authenticates with their AT Proto identity
20 |
21 v
22ATAuth issues standard OIDC tokens back to your app
23```
24
25Any app that supports OpenID Connect can use ATAuth. No custom integration needed.
26
27## Screenshots
28
29<!-- Add your own screenshots to docs/screenshots/ -->
30
31
32
33
34
35
36
37
38
39## Features
40
41- **Standard OIDC Provider**: Discovery, authorization, token, userinfo, revocation, JWKS endpoints
42- **Passkey Login**: WebAuthn/FIDO2 support -- users can register passkeys and skip the Bluesky OAuth flow
43- **Forward-Auth SSO Proxy**: nginx `auth_request` based single sign-on for any web service
44- **Admin Dashboard**: Web UI with setup wizard for 20+ common self-hosted apps
45- **Access Control**: Per-user DID and handle pattern rules with deny-overrides
46- **PKCE Support**: Configurable per-client
47- **ES256 Signed JWTs**: Proper key rotation via JWKS endpoint
48
49## Quick Start
50
51### Prerequisites
52
53- Docker and Docker Compose
54- A domain with a DNS record pointing to your server (e.g. `auth.yourdomain.com`)
55- A reverse proxy that terminates TLS (Caddy, Traefik, or nginx)
56
57### 1. Clone and configure
58
59```bash
60git clone https://github.com/Cache8063/atauth.git
61cd atauth
62cp gateway/.env.example .env
63```
64
65Edit `.env` -- replace every instance of `auth.yourdomain.com` with your actual domain:
66
67```env
68# These must all use your real domain
69OAUTH_CLIENT_ID=https://auth.yourdomain.com/client-metadata.json
70OAUTH_REDIRECT_URI=https://auth.yourdomain.com/auth/callback
71OIDC_ISSUER=https://auth.yourdomain.com
72WEBAUTHN_RP_ID=auth.yourdomain.com
73WEBAUTHN_ORIGIN=https://auth.yourdomain.com
74CORS_ORIGINS=https://app1.yourdomain.com,https://app2.yourdomain.com
75```
76
77Generate and fill in the three required secrets (run the command once per value):
78
79```bash
80openssl rand -hex 32
81```
82
83```env
84ADMIN_TOKEN=<paste-generated-value>
85OIDC_KEY_SECRET=<paste-generated-value>
86MFA_ENCRYPTION_KEY=<paste-generated-value>
87```
88
89### 2. Start the service
90
91```bash
92docker compose up -d
93```
94
95The gateway starts on `127.0.0.1:3100`. Put a reverse proxy with TLS in front of it. The simplest option is [Caddy](https://caddyserver.com/) (automatic HTTPS):
96
97```caddyfile
98auth.yourdomain.com {
99 reverse_proxy localhost:3100
100}
101```
102
103See the [Homelab Deployment Guide](docs/HOMELAB.md) for Traefik and nginx examples.
104
105### 3. Verify it's running
106
107```bash
108curl https://auth.yourdomain.com/.well-known/openid-configuration
109```
110
111You should get a JSON document with all the OIDC endpoints.
112
113### 4. Register your first app
114
115Open `https://auth.yourdomain.com/admin/login`, enter your `ADMIN_TOKEN`, and use the setup wizard to register an app. The wizard includes presets that auto-fill redirect URIs, scopes, and grant types.
116
117Save the returned **client secret** -- it is only shown once.
118
119### 5. Configure your app
120
121Point your app's OIDC settings to ATAuth's discovery URL:
122
123| Setting | Value |
124| --- | --- |
125| Discovery URL | `https://auth.yourdomain.com/.well-known/openid-configuration` |
126| Client ID | The ID you chose in the wizard |
127| Client Secret | The secret returned at registration |
128| Scopes | `openid profile` (add `email` if needed) |
129
130## Supported Apps
131
132The setup wizard includes presets for:
133
134| App | Auth Method |
135| --- | --- |
136| Audiobookshelf | OIDC (OpenID Connect) |
137| Jellyfin | OIDC (via SSO plugin) |
138| Gitea / Forgejo | OIDC (built-in) |
139| Nextcloud | OIDC (via app) |
140| Immich | OIDC (built-in) |
141| Grafana | OIDC (built-in) |
142| Wiki.js | OIDC (built-in) |
143| Portainer | OIDC (built-in) |
144| Outline | OIDC (built-in) |
145| Mealie | OIDC (built-in) |
146| Paperless-ngx | OIDC (built-in) |
147| Vaultwarden | OIDC (built-in) |
148| Miniflux | OIDC (built-in) |
149| Mattermost | OIDC (built-in) |
150| Vikunja | OIDC (built-in) |
151| Plane | OIDC (built-in) |
152| GoToSocial | OIDC (built-in) |
153| Stirling-PDF | OIDC (built-in) |
154| Tandoor Recipes | OIDC (built-in) |
155| FreshRSS | OIDC (built-in) |
156| Any web service | Forward-auth proxy (nginx `auth_request`) |
157
158## Forward-Auth Proxy
159
160For apps without OIDC support, ATAuth provides nginx `auth_request` based SSO. Enable it in `.env`:
161
162```env
163FORWARD_AUTH_ENABLED=true
164FORWARD_AUTH_SESSION_SECRET=<generate-with-openssl-rand-hex-32>
165```
166
167Then configure nginx:
168
169```nginx
170location / {
171 auth_request /auth/verify;
172 auth_request_set $auth_did $upstream_http_x_auth_did;
173 auth_request_set $auth_handle $upstream_http_x_auth_handle;
174 proxy_set_header X-Auth-DID $auth_did;
175 proxy_set_header X-Auth-Handle $auth_handle;
176 proxy_pass http://your-app;
177}
178
179location = /auth/verify {
180 internal;
181 proxy_pass http://atauth:3100;
182 proxy_pass_request_body off;
183 proxy_set_header Content-Length "";
184 proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
185}
186```
187
188The admin dashboard generates these config snippets for you.
189
190## Self-Hosted PDS
191
192With a self-hosted PDS, ATAuth becomes a fully independent auth system:
193
194- Users get handles like `@alice.your-domain.com`
195- No dependency on Bluesky servers
196- Works on air-gapped networks
197- Same security model as enterprise OAuth
198
199The gateway auto-discovers the user's PDS from their handle. No special configuration needed.
200
201## Architecture
202
203| Component | Description |
204| --- | --- |
205| **Gateway** | Node.js/Express 5, TypeScript, SQLite |
206| **Identity** | AT Protocol OAuth (user's PDS) |
207| **OIDC Tokens** | ES256 signed JWTs |
208| **Proxy Cookies** | HMAC-SHA256 signed, typed |
209| **Access Control** | Deny-overrides, per-origin + global rules |
210
211## Security
212
213- No hardcoded secrets -- all sensitive config validated at startup
214- Client secrets stored as SHA-256 hashes
215- One-time flash tokens for secret display (never in URLs)
216- PKCE support (configurable per-client)
217- Constant-time comparison for all secret verification
218- HTML escaping on all server-rendered pages
219- HMAC-signed CSRF tokens on all dashboard forms
220- Rate limiting on auth endpoints
221- CSP with per-request nonces for inline scripts
222- WAF-compatible (Cloudflare Managed Ruleset + OWASP)
223
224## Documentation
225
226- [Homelab Deployment Guide](docs/HOMELAB.md)
227- [Security Policy](SECURITY.md)
228- [Changelog](CHANGELOG.md)
229
230## License
231
232MIT