create-authenik8-app 2.4.3 → 2.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +151 -4
  2. package/package.json +10 -8
  3. package/templates/THREAT_MODEL.md +138 -0
  4. package/templates/express-auth/README.md +196 -0
  5. package/templates/express-auth/docker-compose.yml +23 -0
  6. package/templates/express-auth/package.json +4 -2
  7. package/templates/express-auth/src/app.ts +1 -1
  8. package/templates/express-auth/src/server.ts +1 -1
  9. package/templates/express-auth+/README.md +247 -0
  10. package/templates/express-auth+/docker-compose.yml +23 -0
  11. package/templates/express-auth+/package.json +5 -3
  12. package/templates/express-auth+/src/auth/auth.ts +6 -16
  13. package/templates/express-auth+/src/auth/controllers/oauth.controller.ts +1 -0
  14. package/templates/express-auth+/src/auth/{password.controller.ts → controllers/password.controller.ts} +4 -4
  15. package/templates/express-auth+/src/auth/{protected.controller.ts → controllers/protected.controller.ts} +2 -2
  16. package/templates/express-auth+/src/auth/{auth.middleware.ts → middleware/auth.middleware.ts} +1 -1
  17. package/templates/express-auth+/src/auth/routes/oauth.routes.ts +5 -0
  18. package/templates/express-auth+/src/auth/{password.route.ts → routes/password.route.ts} +1 -1
  19. package/templates/express-auth+/src/auth/{protected.routes.ts → routes/protected.routes.ts} +2 -2
  20. package/templates/express-auth+/src/oauth-providers/github/src/auth/auth.ts +42 -0
  21. package/templates/express-auth+/src/oauth-providers/github/src/auth/oauth.controller.ts +37 -0
  22. package/templates/express-auth+/src/oauth-providers/github/src/auth/oauth.routes.ts +11 -0
  23. package/templates/express-auth+/src/oauth-providers/google/src/auth/auth.ts +42 -0
  24. package/templates/express-auth+/src/oauth-providers/google/src/auth/oauth.controller.ts +37 -0
  25. package/templates/express-auth+/src/oauth-providers/google/src/auth/oauth.routes.ts +11 -0
  26. package/templates/express-auth+/src/oauth-providers/google-github/src/auth/auth.ts +47 -0
  27. package/templates/express-auth+/src/oauth-providers/google-github/src/auth/oauth.controller.ts +57 -0
  28. package/templates/express-auth+/src/server.ts +3 -3
  29. package/templates/express-base/README.md +113 -0
  30. package/templates/express-base/app.ts +1 -1
  31. package/templates/express-base/docker-compose.yml +23 -0
  32. package/templates/express-base/package.json +4 -2
  33. package/templates/express-base/src/package-lock.json +0 -1
  34. package/templates/express-base/src/server.ts +1 -1
  35. package/templates/prisma/postgresql/.env.example +11 -0
  36. package/templates/prisma/sqlite/.env.example +11 -0
  37. package/templates/express-auth+/src/auth/oauth.controller.ts +0 -38
  38. package/templates/express-auth+/src/{auth → oauth-providers/google-github/src/auth}/oauth.routes.ts +1 -1
package/README.md CHANGED
@@ -34,12 +34,13 @@
34
34
 
35
35
  Create a new project:
36
36
 
37
- ```
38
- bash
37
+ ```bash
39
38
  npx create-authenik8-app my-app
40
39
 
41
40
  cd my-app
42
41
 
42
+ npm run prisma:migrate
43
+
43
44
  redis-server --daemonize yes
44
45
 
45
46
  npm run dev
@@ -112,12 +113,24 @@ Generated automatically:
112
113
  The CLI generates these automatically:
113
114
 
114
115
  ```
116
+ DATABASE_URL=file:./dev.db
115
117
  JWT_SECRET=your-secret
116
118
  REFRESH_SECRET=your-refresh-secret
117
119
  REDIS_HOST=127.0.0.1
118
120
  REDIS_PORT=6379
119
121
  ```
120
122
 
123
+ For Full Auth (Password + OAuth), also set:
124
+
125
+ ```bash
126
+ GOOGLE_CLIENT_ID=your-google-client-id
127
+ GOOGLE_CLIENT_SECRET=your-google-client-secret
128
+ GOOGLE_REDIRECT_URI=http://localhost:3000/auth/google/callback
129
+ GITHUB_CLIENT_ID=your-github-client-id
130
+ GITHUB_CLIENT_SECRET=your-github-client-secret
131
+ GITHUB_REDIRECT_URI=http://localhost:3000/auth/github/callback
132
+ ```
133
+
121
134
 
122
135
  ---
123
136
 
@@ -162,7 +175,142 @@ This design makes future additions (MFA, WebAuthn, etc.) much cleaner.
162
175
  ---
163
176
  ## Powered by
164
177
 
165
- authenik8-core (v1.0.29) battle-tested identity & token engine
178
+ authenik8-core (v1.0.33) identity & token engine(beta)
179
+
180
+ ---
181
+
182
+ ## How authenik8-core works in generated apps
183
+
184
+ Generated projects call:
185
+
186
+ ```ts
187
+ const auth = await createAuthenik8({
188
+ jwtSecret: requiredSecret("JWT_SECRET"),
189
+ refreshSecret: requiredSecret("REFRESH_SECRET"),
190
+ oauth: {
191
+ google: {
192
+ clientId: requiredEnv("GOOGLE_CLIENT_ID"),
193
+ clientSecret: requiredEnv("GOOGLE_CLIENT_SECRET"),
194
+ redirectUri: requiredEnv("GOOGLE_REDIRECT_URI"),
195
+ },
196
+ github: {
197
+ clientId: requiredEnv("GITHUB_CLIENT_ID"),
198
+ clientSecret: requiredEnv("GITHUB_CLIENT_SECRET"),
199
+ redirectUri: requiredEnv("GITHUB_REDIRECT_URI"),
200
+ },
201
+ },
202
+ });
203
+ ```
204
+
205
+ That factory returns one auth object used by the generated routes:
206
+
207
+ • `signToken(payload)` creates access tokens.
208
+
209
+ • `verifyToken(token)` verifies access tokens.
210
+
211
+ • `generateRefreshToken(payload)` creates stateful refresh tokens.
212
+
213
+ • `refreshToken(refreshToken)` rotates refresh tokens and returns a new access/refresh pair.
214
+
215
+ • `helmet`, `rateLimit`, and `ipWhitelist` are Express middleware.
216
+
217
+ • `requireAdmin` protects admin-only routes by checking `role: "admin"`.
218
+
219
+ • `oauth.google` and `oauth.github` provide redirect and callback handlers.
220
+
221
+ • `issueTokensFromProfile(profile)` turns a verified OAuth profile into app tokens through the Identity Engine.
222
+
223
+ ### Redis-backed token lifecycle
224
+
225
+ Authenik8-core intentionally makes JWT auth stateful:
226
+
227
+ 1. Access tokens are signed with `JWT_SECRET`.
228
+ 2. Refresh tokens are signed with `REFRESH_SECRET` and include a unique `jti`.
229
+ 3. The current valid refresh token is stored in Redis under `refresh:<userId>`.
230
+ 4. Refresh calls acquire a Redis lock with `lock:<userId>`.
231
+ 5. The submitted refresh token must match the Redis value.
232
+ 6. A new access token and refresh token are issued.
233
+ 7. The new refresh token atomically replaces the old one.
234
+ 8. Reusing the old refresh token fails.
235
+
236
+ This is why Redis is required. It enables refresh-token replay protection, concurrent refresh protection, and server-side session control.
237
+
238
+ ### OAuth identity resolution
239
+
240
+ OAuth is not handled as separate unrelated Passport-style strategies. Provider callbacks are normalized into this profile shape:
241
+
242
+ ```ts
243
+ {
244
+ email: "user@example.com",
245
+ name: "User Name",
246
+ provider: "google",
247
+ providerId: "provider-user-id",
248
+ email_verified: true
249
+ }
250
+ ```
251
+
252
+ The Identity Engine then decides:
253
+
254
+ • Existing provider login: provider is already linked, so tokens are issued.
255
+
256
+ • New user creation: no matching identity exists, so a new user identity is created.
257
+
258
+ • Link required: an email match exists but policy requires explicit account linking.
259
+
260
+ • Link provider: an authenticated user links Google or GitHub to their existing account.
261
+
262
+ OAuth state is stored in Redis for five minutes under `oauth:state:<state>`, and Redis-backed identity records use:
263
+
264
+ ```text
265
+ oauth:v1:user:<userId>
266
+ oauth:v1:email:<email>
267
+ oauth:v1:provider:<provider>:<providerId>
268
+ ```
269
+
270
+ ### Security middleware
271
+
272
+ Generated apps use the middleware returned by core:
273
+
274
+ ```ts
275
+ app.use(auth.helmet);
276
+ app.use(auth.rateLimit);
277
+ ```
278
+
279
+ `helmet` applies secure HTTP headers. `rateLimit` is Redis-backed and defaults to 100 requests per 60 seconds with a 300-second block. `ipWhitelist` is available for stricter APIs and allows localhost by default.
280
+
281
+ ### Common core errors
282
+
283
+ • `MissingTokenError`: no refresh token was sent.
284
+
285
+ • `InvalidTokenError`: refresh token is invalid, expired, reused, or replaced.
286
+
287
+ • `Concurrent refresh detected`: two refresh requests tried to rotate the same token at once.
288
+
289
+ • `OAuthError:Invalid or expired state`: OAuth callback state is missing from Redis.
290
+
291
+ • `OAuth profile email must be verified before issuing tokens`: provider email was not verified.
292
+
293
+ • `Provider already linked to another user`: account linking tried to attach an already-owned provider.
294
+
295
+ ---
296
+
297
+ ## Threat Model
298
+
299
+ Generated apps include a `THREAT_MODEL.md` file. It explains:
300
+
301
+ • what the generated app protects,
302
+
303
+ • what `authenik8-core` handles with Redis-backed token state,
304
+
305
+ • what threats remain your responsibility,
306
+
307
+ • and what must be configured before production.
308
+
309
+ Key protections include refresh-token replay detection, concurrent refresh locking, OAuth state validation, verified-email OAuth token issuance, Redis-backed rate limiting, secure headers, session tracking, and admin-route checks.
310
+
311
+ Key non-goals include frontend XSS protection, CSRF for cookie-based auth, object-level authorization, MFA/WebAuthn, password reset, provider dashboard security, and protection from leaked secrets.
312
+
313
+ Before production, replace generated secrets, keep Redis private, use HTTPS, review CORS, configure exact OAuth callback URLs, and add business-level authorization checks to your own routes.
166
314
 
167
315
  ---
168
316
 
@@ -289,4 +437,3 @@ The Identity Engine is what makes Authenik8 feel like a coherent **authenticatio
289
437
  • MFA
290
438
 
291
439
  • Production presets
292
-
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "create-authenik8-app",
3
- "version": "2.4.3",
4
- "description": " Fast Express + TypeScript auth starter with secure JWT, refresh rotation, Redis, RBAC, OAuth & Prisma.\nPowered by the Authenik8 Identity Engine.",
3
+ "version": "2.4.5",
4
+ "description": " Fast Express + TypeScript auth starter with secure JWT, refresh rotation, Redis, RBAC, OAuth & Prisma.\nPowered by the Authenik8 Identity Engine.",
5
5
  "bin": {
6
- "create-authenik8-app": "dist/index.js"
6
+ "create-authenik8-app": "dist/src/bin/index.js"
7
7
  },
8
8
  "keywords": [
9
9
  "cli",
@@ -35,7 +35,8 @@
35
35
  "fs-extra": "^11.3.4",
36
36
  "inquirer": "^13.3.2",
37
37
  "ora": "^9.3.0",
38
- "tsx": "^4.21.0"
38
+ "tsx": "^4.22.4",
39
+ "typescript": "^6.0.3"
39
40
  },
40
41
  "files": [
41
42
  "dist",
@@ -45,22 +46,23 @@
45
46
  "test": "vitest run",
46
47
  "test:watch": "vitest",
47
48
  "test:coverage": "vitest run --coverage",
48
- "test:templates": "node --import tsx --test tests/template-servers.test.mjs"
49
+ "test:templates": "node --import tsx --test tests/template-servers.test.mjs",
50
+ "build": "tsc && cp -r ./templates/express-auth/package.json ./templates/THREAT_MODEL.md dist/templates"
49
51
  },
50
52
  "devDependencies": {
51
53
  "@types/express": "^5.0.6",
52
54
  "@types/fs-extra": "^11.0.4",
53
55
  "@types/node": "^25.5.2",
54
56
  "@types/supertest": "^7.2.0",
55
- "@vitest/coverage-v8": "^3.2.4",
57
+ "@vitest/coverage-v8": "^4.1.8",
56
58
  "supertest": "^7.2.2",
57
- "vitest": "^3.2.4"
59
+ "vitest": "^4.1.8"
58
60
  },
59
61
  "repository": {
60
62
  "type": "git",
61
63
  "url": "https://github.com/COD434/create-authenik8-app.git"
62
64
  },
63
65
  "overrides": {
64
- "brace-expansion": "3.0.2"
66
+ "brace-expansion": "2.0.2"
65
67
  }
66
68
  }
@@ -0,0 +1,138 @@
1
+ # Authenik8 Generated App Threat Model
2
+
3
+ This document describes what a generated Authenik8 app is designed to help with, what it does not solve for you, and what you must configure before production use.
4
+
5
+ ## System Boundary
6
+
7
+ A generated app includes:
8
+
9
+ - Express API routes.
10
+ - JWT access tokens.
11
+ - Redis-backed refresh-token rotation.
12
+ - Prisma database schema.
13
+ - Optional Google/GitHub OAuth.
14
+ - Authenik8 security middleware: Helmet, rate limiting, and optional IP whitelist.
15
+ - Optional PM2 production process config.
16
+
17
+ External systems are outside the generated app boundary:
18
+
19
+ - Browser or mobile frontend.
20
+ - OAuth provider dashboards.
21
+ - Redis hosting.
22
+ - SQL database hosting.
23
+ - TLS termination and reverse proxy.
24
+ - Secret management.
25
+ - Email delivery, MFA, and password reset flows unless you add them.
26
+
27
+ ## Assets Protected
28
+
29
+ - Access tokens.
30
+ - Refresh tokens.
31
+ - OAuth authorization state.
32
+ - OAuth identity records.
33
+ - User IDs, emails, roles, and provider links.
34
+ - Redis-backed session state.
35
+ - Admin-only routes.
36
+
37
+ ## Trust Assumptions
38
+
39
+ - `JWT_SECRET` and `REFRESH_SECRET` are long, random, private values.
40
+ - Redis is private to the app and not exposed to the public internet.
41
+ - Database credentials are private.
42
+ - OAuth callback URLs match provider dashboard settings exactly.
43
+ - Production traffic uses HTTPS.
44
+ - Reverse proxy headers are trusted only when you control the proxy.
45
+ - Developers validate and authorize their own business-domain routes.
46
+
47
+ ## Threats Addressed
48
+
49
+ ### Refresh-token replay
50
+
51
+ Refresh tokens are stateful. The currently valid refresh token is stored in Redis under `refresh:<userId>`. When a refresh succeeds, Authenik8-core rotates the refresh token and replaces the Redis value. Reusing an old refresh token fails.
52
+
53
+ ### Concurrent refresh abuse
54
+
55
+ Refresh requests acquire a Redis lock under `lock:<userId>`. Two simultaneous refresh attempts for the same user should not both rotate successfully.
56
+
57
+ ### Stateless JWT logout limitations
58
+
59
+ Access-token sessions are persisted in Redis under `sessions:<userId>`. Admin helpers can list sessions and revoke one or all sessions for a user.
60
+
61
+ ### Basic request flooding
62
+
63
+ `auth.rateLimit` uses Redis-backed rate limiting. Generated apps apply it globally by default.
64
+
65
+ ### Common HTTP header risks
66
+
67
+ `auth.helmet` applies secure HTTP headers through Helmet.
68
+
69
+ ### OAuth CSRF/state tampering
70
+
71
+ OAuth redirects generate random state and store it in Redis for five minutes under `oauth:state:<state>`. Callback handlers reject missing, invalid, or expired state.
72
+
73
+ ### Duplicate OAuth identities
74
+
75
+ OAuth profiles are normalized into provider, provider ID, email, name, and email verification status. The Identity Engine checks provider and email records before creating a new identity.
76
+
77
+ ### Unverified OAuth email token issuance
78
+
79
+ `issueTokensFromProfile` rejects OAuth profiles whose email is not verified.
80
+
81
+ ### Admin-route access
82
+
83
+ `auth.requireAdmin` checks for a valid JWT with `role: "admin"`.
84
+
85
+ ## Threats Not Fully Addressed
86
+
87
+ ### XSS in your frontend
88
+
89
+ If frontend JavaScript is compromised, tokens stored in memory or browser storage can be stolen. Use a strong frontend CSP, avoid unsafe HTML rendering, and choose token storage deliberately.
90
+
91
+ ### CSRF for cookie-based auth
92
+
93
+ Generated examples use bearer tokens. If you move tokens into cookies, add CSRF protection and strict cookie settings.
94
+
95
+ ### Weak or leaked secrets
96
+
97
+ Authenik8 cannot protect tokens if `JWT_SECRET` or `REFRESH_SECRET` is short, reused, committed, logged, or leaked.
98
+
99
+ ### Public Redis exposure
100
+
101
+ Redis must not be reachable from the public internet. Use private networking, authentication, TLS where available, and provider-level access controls.
102
+
103
+ ### Database authorization bugs
104
+
105
+ Authenik8 authenticates users and provides route middleware. Your application must still enforce object-level authorization, ownership checks, and tenant isolation.
106
+
107
+ ### Password reset, email verification, MFA, and WebAuthn
108
+
109
+ These are not included unless you add them.
110
+
111
+ ### OAuth provider compromise or misconfiguration
112
+
113
+ Provider dashboard settings, app approval screens, callback URLs, and provider secrets must be managed outside the generated app.
114
+
115
+ ### Brute-force protection per credential
116
+
117
+ Global rate limiting is included. Add stricter per-email or per-account login throttling for high-risk apps.
118
+
119
+ ### Token theft before expiry
120
+
121
+ Short-lived access tokens reduce exposure, but a stolen access token can be used until it expires or is rejected by your session policy.
122
+
123
+ ## Production Checklist
124
+
125
+ - Replace generated development secrets with long random values.
126
+ - Run Redis on private networking.
127
+ - Run Postgres or your database on private networking.
128
+ - Use HTTPS only.
129
+ - Set exact OAuth callback URLs in Google/GitHub dashboards.
130
+ - Use `npx prisma migrate deploy` in production.
131
+ - Review CORS policy before connecting a frontend.
132
+ - Add business-level authorization checks to every protected resource route.
133
+ - Add logging and alerting for refresh failures, OAuth failures, and admin actions.
134
+ - Keep `authenik8-core` and generated dependencies updated.
135
+
136
+ ## Security Reporting
137
+
138
+ If you find a vulnerability in the generated app or Authenik8-core integration, do not publish exploit details publicly first. Open a private security report or contact the maintainer through the repository security policy.
@@ -0,0 +1,196 @@
1
+ # Authenik8 Express Password API
2
+
3
+ Generated by `create-authenik8-app`.
4
+
5
+ ## Start
6
+
7
+ ```bash
8
+ npm install
9
+ npm run docker:up
10
+ npm run prisma:migrate
11
+ npm run dev
12
+ ```
13
+
14
+ For SQLite, Postgres in `docker-compose.yml` is optional. Redis is required for refresh-token rotation and replay protection.
15
+
16
+ ## Environment
17
+
18
+ Review `.env` before running. The generated secrets are development placeholders and must be replaced before deployment.
19
+
20
+ Required:
21
+
22
+ ```bash
23
+ DATABASE_URL=file:./dev.db
24
+ JWT_SECRET=dev-jwt-secret-change-before-production-123456
25
+ REFRESH_SECRET=dev-refresh-secret-change-before-production-123456
26
+ REDIS_HOST=127.0.0.1
27
+ REDIS_PORT=6379
28
+ ```
29
+
30
+ ## API Contract
31
+
32
+ Register:
33
+
34
+ ```http
35
+ POST /auth/register
36
+ Content-Type: application/json
37
+
38
+ {
39
+ "email": "dev@example.com",
40
+ "password": "password123"
41
+ }
42
+ ```
43
+
44
+ Login:
45
+
46
+ ```http
47
+ POST /auth/login
48
+ Content-Type: application/json
49
+
50
+ {
51
+ "email": "dev@example.com",
52
+ "password": "password123"
53
+ }
54
+ ```
55
+
56
+ Refresh:
57
+
58
+ ```http
59
+ POST /auth/refresh
60
+ Content-Type: application/json
61
+
62
+ {
63
+ "refreshToken": "<refreshToken>"
64
+ }
65
+ ```
66
+
67
+ Protected:
68
+
69
+ ```http
70
+ GET /protected
71
+ Authorization: Bearer <accessToken>
72
+ ```
73
+
74
+ ## 3-Minute Verification
75
+
76
+ Start the API in one terminal:
77
+
78
+ ```bash
79
+ npm run docker:up
80
+ npm run prisma:migrate
81
+ npm run dev
82
+ ```
83
+
84
+ Register a user:
85
+
86
+ ```bash
87
+ curl -s -X POST http://localhost:3000/auth/register \
88
+ -H "Content-Type: application/json" \
89
+ -d '{"email":"dev@example.com","password":"password123"}'
90
+ ```
91
+
92
+ Login and save the response:
93
+
94
+ ```bash
95
+ curl -s -X POST http://localhost:3000/auth/login \
96
+ -H "Content-Type: application/json" \
97
+ -d '{"email":"dev@example.com","password":"password123"}'
98
+ ```
99
+
100
+ Expected shape:
101
+
102
+ ```json
103
+ {
104
+ "user": {
105
+ "id": "user-id",
106
+ "email": "dev@example.com"
107
+ },
108
+ "accessToken": "access-token",
109
+ "refreshToken": "refresh-token"
110
+ }
111
+ ```
112
+
113
+ Call a protected route:
114
+
115
+ ```bash
116
+ curl http://localhost:3000/protected \
117
+ -H "Authorization: Bearer <accessToken>"
118
+ ```
119
+
120
+ Refresh an access token:
121
+
122
+ ```bash
123
+ curl -s -X POST http://localhost:3000/auth/refresh \
124
+ -H "Content-Type: application/json" \
125
+ -d '{"refreshToken":"<refreshToken>"}'
126
+ ```
127
+
128
+ ## Environment Variables
129
+
130
+ - `DATABASE_URL`: Prisma database connection. SQLite uses `file:./dev.db`; Postgres uses a `postgresql://...` URL.
131
+ - `JWT_SECRET`: signs short-lived access tokens. Use a long random value in production.
132
+ - `REFRESH_SECRET`: signs refresh tokens. Use a different long random value in production.
133
+ - `REDIS_HOST`: Redis host for refresh-token/session security.
134
+ - `REDIS_PORT`: Redis port, usually `6379` locally.
135
+
136
+ ## Frontend Use
137
+
138
+ Store the access token in memory and use the refresh token only through your chosen secure storage strategy. Add the access token to API requests with the `Authorization` header.
139
+
140
+ ```ts
141
+ let accessToken = "";
142
+ let refreshToken = "";
143
+
144
+ export async function login(email: string, password: string) {
145
+ const response = await fetch("http://localhost:3000/auth/login", {
146
+ method: "POST",
147
+ headers: { "Content-Type": "application/json" },
148
+ body: JSON.stringify({ email, password }),
149
+ });
150
+
151
+ if (!response.ok) {
152
+ throw new Error(`Login failed: ${response.status}`);
153
+ }
154
+
155
+ const session = await response.json();
156
+ accessToken = session.accessToken;
157
+ refreshToken = session.refreshToken;
158
+ return session;
159
+ }
160
+
161
+ export async function getProtected() {
162
+ const response = await fetch("http://localhost:3000/protected", {
163
+ headers: { Authorization: `Bearer ${accessToken}` },
164
+ });
165
+
166
+ if (!response.ok) {
167
+ throw new Error(`Protected request failed: ${response.status}`);
168
+ }
169
+
170
+ return response.json();
171
+ }
172
+ ```
173
+
174
+ ## Troubleshooting
175
+
176
+ `Redis connection refused`: run `npm run docker:up` or start Redis locally with `redis-server --daemonize yes`.
177
+
178
+ `Prisma Client did not initialize`: run `npm run prisma:migrate`, then restart `npm run dev`.
179
+
180
+ `JWT_SECRET must be set to at least 32 characters`: check `.env`; both token secrets must be long strings.
181
+
182
+ `Cannot POST /auth/login`: confirm the server is running and you generated the password auth template, not the JWT-only base template.
183
+
184
+ `Invalid email or password`: register the user first, then login with the same email/password.
185
+
186
+ `Port 3000 already in use`: stop the other process or change the `app.listen(3000)` port in `src/server.ts`.
187
+
188
+ `DATABASE_URL is wrong`: for SQLite use `file:./dev.db`; for local Docker Postgres use `postgresql://postgres:postgres@localhost:5432/authenik8?schema=public`.
189
+
190
+ ## Threat Model
191
+
192
+ Read `THREAT_MODEL.md` before deploying. It explains what Authenik8 protects, what Redis-backed token state handles, and what remains your responsibility.
193
+
194
+ ## Deploy
195
+
196
+ Use `npm run build`, run `npx prisma migrate deploy` for production databases, set real secrets in your host, and point Redis/Postgres env vars at managed services.
@@ -0,0 +1,23 @@
1
+ services:
2
+ redis:
3
+ image: redis:7-alpine
4
+ ports:
5
+ - "6379:6379"
6
+ command: ["redis-server", "--appendonly", "yes"]
7
+ volumes:
8
+ - redis-data:/data
9
+
10
+ postgres:
11
+ image: postgres:16-alpine
12
+ ports:
13
+ - "5432:5432"
14
+ environment:
15
+ POSTGRES_USER: postgres
16
+ POSTGRES_PASSWORD: postgres
17
+ POSTGRES_DB: authenik8
18
+ volumes:
19
+ - postgres-data:/var/lib/postgresql/data
20
+
21
+ volumes:
22
+ redis-data:
23
+ postgres-data:
@@ -7,10 +7,12 @@
7
7
  "dev": "ts-node-dev --respawn --transpile-only src/server.ts",
8
8
  "build": "tsc",
9
9
  "start": "node dist/server.js",
10
- "prisma:migrate": "prisma migrate dev"
10
+ "prisma:migrate": "prisma migrate dev",
11
+ "docker:up": "docker compose up -d",
12
+ "docker:down": "docker compose down"
11
13
  },
12
14
  "dependencies": {
13
- "authenik8-core": "^1.0.3",
15
+ "authenik8-core": "^1.0.33",
14
16
  "dotenv": "^16.0.0",
15
17
  "express": "^4.19.2",
16
18
  "@prisma/client": "5.22.0"
@@ -1,4 +1,4 @@
1
- import express from "express";
1
+ import express from "express";
2
2
  import { createAuthRoutes } from "./routes/auth.routes";
3
3
  import { createProtectedRoutes } from "./routes/protected.routes";
4
4
 
@@ -1,4 +1,4 @@
1
- import dotenv from "dotenv";
1
+ import dotenv from "dotenv";
2
2
  import { createAuthenik8 } from "authenik8-core";
3
3
  import { createApp } from "./app";
4
4
  import { requiredSecret } from "./utils/security";