create-authenik8-app 2.4.14 → 2.4.15

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 (227) hide show
  1. package/README.md +34 -516
  2. package/dist/src/bin/cli.d.ts +3 -0
  3. package/dist/src/bin/cli.d.ts.map +1 -0
  4. package/dist/src/bin/cli.js +112 -0
  5. package/dist/src/bin/cli.js.map +1 -0
  6. package/dist/src/bin/index.js +108 -23
  7. package/dist/src/bin/index.js.map +1 -1
  8. package/dist/src/commands/add/apply.d.ts +6 -0
  9. package/dist/src/commands/add/apply.d.ts.map +1 -0
  10. package/dist/src/commands/add/apply.js +123 -0
  11. package/dist/src/commands/add/apply.js.map +1 -0
  12. package/dist/src/commands/add/ci.d.ts +6 -0
  13. package/dist/src/commands/add/ci.d.ts.map +1 -0
  14. package/dist/src/commands/add/ci.js +108 -0
  15. package/dist/src/commands/add/ci.js.map +1 -0
  16. package/dist/src/commands/add/context.d.ts +5 -0
  17. package/dist/src/commands/add/context.d.ts.map +1 -0
  18. package/dist/src/commands/add/context.js +19 -0
  19. package/dist/src/commands/add/context.js.map +1 -0
  20. package/dist/src/commands/add/diff.d.ts +4 -0
  21. package/dist/src/commands/add/diff.d.ts.map +1 -0
  22. package/dist/src/commands/add/diff.js +114 -0
  23. package/dist/src/commands/add/diff.js.map +1 -0
  24. package/dist/src/commands/add/index.d.ts +6 -0
  25. package/dist/src/commands/add/index.d.ts.map +1 -0
  26. package/dist/src/commands/add/index.js +61 -0
  27. package/dist/src/commands/add/index.js.map +1 -0
  28. package/dist/src/commands/add/output.d.ts +5 -0
  29. package/dist/src/commands/add/output.d.ts.map +1 -0
  30. package/dist/src/commands/add/output.js +58 -0
  31. package/dist/src/commands/add/output.js.map +1 -0
  32. package/dist/src/commands/add/plan.d.ts +6 -0
  33. package/dist/src/commands/add/plan.d.ts.map +1 -0
  34. package/dist/src/commands/add/plan.js +93 -0
  35. package/dist/src/commands/add/plan.js.map +1 -0
  36. package/dist/src/commands/add/registry.d.ts +4 -0
  37. package/dist/src/commands/add/registry.d.ts.map +1 -0
  38. package/dist/src/commands/add/registry.js +37 -0
  39. package/dist/src/commands/add/registry.js.map +1 -0
  40. package/dist/src/commands/add/types.d.ts +37 -0
  41. package/dist/src/commands/add/types.d.ts.map +1 -0
  42. package/dist/src/commands/add/types.js +2 -0
  43. package/dist/src/commands/add/types.js.map +1 -0
  44. package/dist/src/commands/doctor/checks.d.ts +3 -0
  45. package/dist/src/commands/doctor/checks.d.ts.map +1 -0
  46. package/dist/src/commands/doctor/checks.js +353 -0
  47. package/dist/src/commands/doctor/checks.js.map +1 -0
  48. package/dist/src/commands/doctor/context.d.ts +5 -0
  49. package/dist/src/commands/doctor/context.d.ts.map +1 -0
  50. package/dist/src/commands/doctor/context.js +134 -0
  51. package/dist/src/commands/doctor/context.js.map +1 -0
  52. package/dist/src/commands/doctor/index.d.ts +9 -0
  53. package/dist/src/commands/doctor/index.d.ts.map +1 -0
  54. package/dist/src/commands/doctor/index.js +80 -0
  55. package/dist/src/commands/doctor/index.js.map +1 -0
  56. package/dist/src/commands/doctor/output.d.ts +4 -0
  57. package/dist/src/commands/doctor/output.d.ts.map +1 -0
  58. package/dist/src/commands/doctor/output.js +53 -0
  59. package/dist/src/commands/doctor/output.js.map +1 -0
  60. package/dist/src/commands/doctor/postGeneration.d.ts +7 -0
  61. package/dist/src/commands/doctor/postGeneration.d.ts.map +1 -0
  62. package/dist/src/commands/doctor/postGeneration.js +22 -0
  63. package/dist/src/commands/doctor/postGeneration.js.map +1 -0
  64. package/dist/src/commands/doctor/services.d.ts +5 -0
  65. package/dist/src/commands/doctor/services.d.ts.map +1 -0
  66. package/dist/src/commands/doctor/services.js +80 -0
  67. package/dist/src/commands/doctor/services.js.map +1 -0
  68. package/dist/src/commands/doctor/types.d.ts +69 -0
  69. package/dist/src/commands/doctor/types.d.ts.map +1 -0
  70. package/dist/src/commands/doctor/types.js +2 -0
  71. package/dist/src/commands/doctor/types.js.map +1 -0
  72. package/dist/src/commands/upgrade/context.d.ts +5 -0
  73. package/dist/src/commands/upgrade/context.d.ts.map +1 -0
  74. package/dist/src/commands/upgrade/context.js +57 -0
  75. package/dist/src/commands/upgrade/context.js.map +1 -0
  76. package/dist/src/commands/upgrade/index.d.ts +7 -0
  77. package/dist/src/commands/upgrade/index.d.ts.map +1 -0
  78. package/dist/src/commands/upgrade/index.js +35 -0
  79. package/dist/src/commands/upgrade/index.js.map +1 -0
  80. package/dist/src/commands/upgrade/migrations.d.ts +4 -0
  81. package/dist/src/commands/upgrade/migrations.d.ts.map +1 -0
  82. package/dist/src/commands/upgrade/migrations.js +22 -0
  83. package/dist/src/commands/upgrade/migrations.js.map +1 -0
  84. package/dist/src/commands/upgrade/output.d.ts +4 -0
  85. package/dist/src/commands/upgrade/output.d.ts.map +1 -0
  86. package/dist/src/commands/upgrade/output.js +52 -0
  87. package/dist/src/commands/upgrade/output.js.map +1 -0
  88. package/dist/src/commands/upgrade/plan.d.ts +3 -0
  89. package/dist/src/commands/upgrade/plan.d.ts.map +1 -0
  90. package/dist/src/commands/upgrade/plan.js +119 -0
  91. package/dist/src/commands/upgrade/plan.js.map +1 -0
  92. package/dist/src/commands/upgrade/types.d.ts +48 -0
  93. package/dist/src/commands/upgrade/types.d.ts.map +1 -0
  94. package/dist/src/commands/upgrade/types.js +2 -0
  95. package/dist/src/commands/upgrade/types.js.map +1 -0
  96. package/dist/src/lib/args.d.ts +21 -0
  97. package/dist/src/lib/args.d.ts.map +1 -1
  98. package/dist/src/lib/args.js +119 -1
  99. package/dist/src/lib/args.js.map +1 -1
  100. package/dist/src/lib/constants.d.ts +1 -1
  101. package/dist/src/lib/constants.d.ts.map +1 -1
  102. package/dist/src/lib/constants.js +3 -1
  103. package/dist/src/lib/constants.js.map +1 -1
  104. package/dist/src/lib/expressOAuth.d.ts +10 -0
  105. package/dist/src/lib/expressOAuth.d.ts.map +1 -0
  106. package/dist/src/lib/expressOAuth.js +164 -0
  107. package/dist/src/lib/expressOAuth.js.map +1 -0
  108. package/dist/src/lib/interruption.d.ts +14 -0
  109. package/dist/src/lib/interruption.d.ts.map +1 -0
  110. package/dist/src/lib/interruption.js +16 -0
  111. package/dist/src/lib/interruption.js.map +1 -0
  112. package/dist/src/lib/nonInteractive.d.ts +8 -0
  113. package/dist/src/lib/nonInteractive.d.ts.map +1 -0
  114. package/dist/src/lib/nonInteractive.js +111 -0
  115. package/dist/src/lib/nonInteractive.js.map +1 -0
  116. package/dist/src/lib/oauth.d.ts +9 -0
  117. package/dist/src/lib/oauth.d.ts.map +1 -0
  118. package/dist/src/lib/oauth.js +62 -0
  119. package/dist/src/lib/oauth.js.map +1 -0
  120. package/dist/src/lib/onboarding.d.ts +8 -0
  121. package/dist/src/lib/onboarding.d.ts.map +1 -0
  122. package/dist/src/lib/onboarding.js +35 -0
  123. package/dist/src/lib/onboarding.js.map +1 -0
  124. package/dist/src/lib/packageManagerCommands.d.ts +11 -0
  125. package/dist/src/lib/packageManagerCommands.d.ts.map +1 -0
  126. package/dist/src/lib/packageManagerCommands.js +34 -0
  127. package/dist/src/lib/packageManagerCommands.js.map +1 -0
  128. package/dist/src/lib/process.d.ts.map +1 -1
  129. package/dist/src/lib/process.js +9 -0
  130. package/dist/src/lib/process.js.map +1 -1
  131. package/dist/src/lib/projectManifest.d.ts +67 -0
  132. package/dist/src/lib/projectManifest.d.ts.map +1 -0
  133. package/dist/src/lib/projectManifest.js +134 -0
  134. package/dist/src/lib/projectManifest.js.map +1 -0
  135. package/dist/src/lib/release.d.ts +9 -0
  136. package/dist/src/lib/release.d.ts.map +1 -0
  137. package/dist/src/lib/release.js +48 -0
  138. package/dist/src/lib/release.js.map +1 -0
  139. package/dist/src/lib/rootCommand.d.ts +15 -0
  140. package/dist/src/lib/rootCommand.d.ts.map +1 -0
  141. package/dist/src/lib/rootCommand.js +17 -0
  142. package/dist/src/lib/rootCommand.js.map +1 -0
  143. package/dist/src/lib/schemas.d.ts +15 -6
  144. package/dist/src/lib/schemas.d.ts.map +1 -1
  145. package/dist/src/lib/schemas.js +8 -1
  146. package/dist/src/lib/schemas.js.map +1 -1
  147. package/dist/src/lib/semver.d.ts +10 -0
  148. package/dist/src/lib/semver.d.ts.map +1 -0
  149. package/dist/src/lib/semver.js +51 -0
  150. package/dist/src/lib/semver.js.map +1 -0
  151. package/dist/src/lib/types.d.ts +5 -1
  152. package/dist/src/lib/types.d.ts.map +1 -1
  153. package/dist/src/steps/configurePrisma.d.ts.map +1 -1
  154. package/dist/src/steps/configurePrisma.js +18 -23
  155. package/dist/src/steps/configurePrisma.js.map +1 -1
  156. package/dist/src/steps/configureReadme.d.ts +4 -0
  157. package/dist/src/steps/configureReadme.d.ts.map +1 -0
  158. package/dist/src/steps/configureReadme.js +22 -0
  159. package/dist/src/steps/configureReadme.js.map +1 -0
  160. package/dist/src/steps/createProject.d.ts.map +1 -1
  161. package/dist/src/steps/createProject.js +16 -170
  162. package/dist/src/steps/createProject.js.map +1 -1
  163. package/dist/src/steps/finalSetup.d.ts +3 -2
  164. package/dist/src/steps/finalSetup.d.ts.map +1 -1
  165. package/dist/src/steps/finalSetup.js +14 -9
  166. package/dist/src/steps/finalSetup.js.map +1 -1
  167. package/dist/src/steps/installDeps.d.ts +1 -0
  168. package/dist/src/steps/installDeps.d.ts.map +1 -1
  169. package/dist/src/steps/installDeps.js +11 -0
  170. package/dist/src/steps/installDeps.js.map +1 -1
  171. package/dist/src/steps/prompts.d.ts.map +1 -1
  172. package/dist/src/steps/prompts.js +32 -13
  173. package/dist/src/steps/prompts.js.map +1 -1
  174. package/dist/src/utils/output.d.ts.map +1 -1
  175. package/dist/src/utils/output.js +23 -11
  176. package/dist/src/utils/output.js.map +1 -1
  177. package/package.json +5 -4
  178. package/templates/express-auth/README.md +21 -12
  179. package/templates/express-auth/package.json +11 -7
  180. package/templates/express-auth/src/config/redis.ts +109 -0
  181. package/templates/express-auth/src/server.ts +2 -0
  182. package/templates/express-auth+/README.md +25 -18
  183. package/templates/express-auth+/package.json +11 -7
  184. package/templates/express-auth+/src/auth/auth.ts +2 -0
  185. package/templates/express-auth+/src/auth/controllers/password.controller.ts +12 -1
  186. package/templates/express-auth+/src/auth/routes/password.route.ts +1 -0
  187. package/templates/express-auth+/src/config/redis.ts +109 -0
  188. package/templates/express-auth+/src/utils/security.ts +12 -0
  189. package/templates/express-base/README.md +21 -12
  190. package/templates/express-base/package.json +11 -8
  191. package/templates/express-base/src/.env.example +1 -1
  192. package/templates/express-base/src/config/redis.ts +109 -0
  193. package/templates/express-base/src/server.ts +2 -0
  194. package/templates/fullstack/.env.example +3 -2
  195. package/templates/fullstack/PRESET_CONTRACT.md +7 -3
  196. package/templates/fullstack/README.md +45 -7
  197. package/templates/fullstack/apps/api/package.json +6 -4
  198. package/templates/fullstack/apps/api/prisma/migrations/20260721000000_init/migration.sql +163 -0
  199. package/templates/fullstack/apps/api/prisma/migrations/migration_lock.toml +3 -0
  200. package/templates/fullstack/apps/api/src/auth/authenik8.ts +13 -4
  201. package/templates/fullstack/apps/api/src/config/env.ts +9 -1
  202. package/templates/fullstack/apps/api/src/config/logger.ts +21 -11
  203. package/templates/fullstack/apps/api/tests/logger.test.ts +43 -0
  204. package/templates/fullstack/apps/web/package.json +3 -1
  205. package/templates/fullstack/apps/web/src/App.tsx +1 -1
  206. package/templates/fullstack/apps/web/src/auth/RouteGuards.tsx +1 -1
  207. package/templates/fullstack/apps/web/src/components/AppShell.tsx +90 -45
  208. package/templates/fullstack/apps/web/src/components/AuthShell.tsx +10 -7
  209. package/templates/fullstack/apps/web/src/components/Page.tsx +6 -3
  210. package/templates/fullstack/apps/web/src/features/projects/ProjectPages.tsx +1 -1
  211. package/templates/fullstack/apps/web/src/main.tsx +13 -6
  212. package/templates/fullstack/apps/web/src/pages/DashboardPage.tsx +163 -23
  213. package/templates/fullstack/apps/web/src/pages/StatusPages.tsx +1 -1
  214. package/templates/fullstack/apps/web/src/pages/auth/LoginPage.tsx +93 -17
  215. package/templates/fullstack/apps/web/src/pages/auth/OAuthCallbackPage.tsx +1 -1
  216. package/templates/fullstack/apps/web/src/pages/auth/RecoveryPages.tsx +1 -1
  217. package/templates/fullstack/apps/web/src/pages/auth/RegisterPage.tsx +1 -1
  218. package/templates/fullstack/apps/web/src/pages/auth/VerifyEmailPage.tsx +1 -1
  219. package/templates/fullstack/apps/web/src/styles.css +119 -43
  220. package/templates/fullstack/apps/web/vite.config.ts +12 -0
  221. package/templates/fullstack/docker-compose.yml +2 -2
  222. package/templates/fullstack/gitignore.template +1 -0
  223. package/templates/fullstack/package.json +17 -8
  224. package/templates/fullstack/scripts/run-local.mjs +157 -0
  225. package/templates/prisma/postgresql/.env.example +1 -2
  226. package/templates/prisma/sqlite/.env.example +1 -2
  227. package/templates/prisma/sqlite/client.ts +2 -2
package/README.md CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  <div align="center">
3
2
 
4
3
  ![create-authenik8-app logo](https://raw.githubusercontent.com/COD434/create-authenik8-app/main/assets/logo.svg)
@@ -16,538 +15,57 @@
16
15
  ![CI](https://github.com/COD434/create-authenik8-app/actions/workflows/ci.yml/badge.svg)
17
16
  [![Coverage](https://img.shields.io/badge/coverage-80%25-green)](https://github.com/COD434/create-authenik8-app/actions/workflows/ci.yml)
18
17
 
19
- **See a real generated example [create-authenik8-app-example](https://github.com/COD434/create-authenik8-app-example)**
20
-
21
-
22
- ---
18
+ See a real generated project in [create-authenik8-app-example](https://github.com/COD434/create-authenik8-app-example).
23
19
 
24
20
  ## Quickstart
25
21
 
26
- Create a new project:
27
-
28
22
  ```bash
29
23
  npx create-authenik8-app my-app
30
24
  ```
31
25
 
32
- Choose the installer for an Express preset when needed:
33
-
34
- ```bash
35
- npx create-authenik8-app my-api --package-manager pnpm
36
- ```
37
-
38
- The CLI uses the invoking package manager when it can identify one, otherwise it defaults to npm. Install commands use the local package cache first and suppress registry audit/funding requests during scaffolding. Set `AUTHENIK8_VERBOSE=1` to show complete installer output.
39
-
40
- Choose **Full-stack application** for the complete App Kit, or select one of the three Express API presets.
41
-
42
- ### Full-stack application
43
-
44
- Requirements: Node.js 20.19+, 22.12+, or 24+, npm, and Docker with Compose.
45
-
46
- ```bash
47
- cd my-app
48
- npm run docker:up
49
- npm run db:migrate
50
- npm run db:seed
51
- npm run dev
52
- ```
53
-
54
- Open `http://localhost:5173`. Vite proxies `/api` to Express on `http://localhost:3000`.
55
-
56
- ### Express API
57
-
58
- Requirements: Node.js 20.19+, 22.12+, or 24+.
59
-
60
- ```bash
61
- cd my-app
62
- npm run docker:up
63
- npm run prisma:migrate # Skip only when JWT-only was generated without Prisma
64
- npm run dev
65
- ```
66
-
67
- ---
68
-
69
- ## Presets
70
-
71
- | Preset | Best for | Included |
72
- | --- | --- | --- |
73
- | **Express API (JWT only)** | APIs that manage identities elsewhere | Protected routes, rotating refresh tokens, RBAC, optional Prisma |
74
- | **Express API + email/password** | First-party accounts | Registration, login, password hashing, Prisma |
75
- | **Express API + OAuth** | Multiple sign-in methods | Password auth, Google and/or GitHub OAuth, account linking |
76
- | **Full-stack application** | Starting a complete product | React/Vite, Express, shared contracts, typed client, PostgreSQL, Redis, account/admin UI, Project CRUD |
77
-
78
- ## Full-stack App Kit
79
-
80
- The fullstack preset is an npm workspace with connected frontend, backend, and shared packages:
81
-
82
- ```text
83
- apps/
84
- api/ Express, Authenik8, Prisma, PostgreSQL, Redis
85
- web/ React, Vite, React Router, TanStack Query
86
- packages/
87
- contracts/ Shared Zod request schemas and response types
88
- api-client/ Typed browser client and refresh handling
89
- ui/ Reusable application primitives
90
- ```
91
-
92
- It includes:
93
-
94
- - Registration, login, logout, OAuth, password recovery, and email verification
95
- - Profile, password, linked-provider, session, and session-revocation screens
96
- - Admin users, roles, status controls, session revocation, and audit events
97
- - An owned Project resource across database, API, policy, client, and UI layers
98
- - OpenAPI 3.1 output, health checks, structured logs, request IDs, and security middleware
99
- - Docker Compose for PostgreSQL and Redis, Prisma migrations, seed data, tests, and production docs
100
-
101
- Access tokens remain in browser memory. Refresh tokens use a restricted HttpOnly cookie, and server-side policies remain the authorization boundary.
102
-
103
- ### Full-stack commands
104
-
105
- | Command | Purpose |
106
- | --- | --- |
107
- | `npm run dev` | Build shared packages and watch the API, web app, contracts, client, and UI |
108
- | `npm run build` | Build all production bundles |
109
- | `npm start` | Start Express and serve the built SPA and `/api` |
110
- | `npm run db:migrate` | Apply the Prisma development migration |
111
- | `npm run db:seed` | Create the configured administrator and example data |
112
- | `npm test` | Run API policy/security tests and web storage tests |
113
- | `npm run typecheck` | Type-check every workspace |
114
-
115
- ---
116
-
117
- ## Core authentication features
118
-
119
- • JWT authentication (access + refresh tokens) with secure rotation
120
-
121
- • Redis-based token storage
122
-
123
- • Role-Based Access Control (RBAC)
124
-
125
- • TypeScript setup
126
-
127
- • Express server preconfigured with security middleware
128
-
129
- • Clean scalable folder structure
130
-
131
- • .env file generated automatically
132
-
133
- • Generated threat model and production guidance
134
-
135
-
136
- ---
137
-
138
- ## Why create-authenik8-app
139
-
140
- Most developers waste days (or weeks) on:
141
-
142
- • Manual JWT setup
143
-
144
- • Secure refresh token handling
145
-
146
- • Redis session configuration
147
-
148
- • Proper access control
149
-
150
- Authenik8 provides these foundations so you can start with a secure API or a connected application instead of rebuilding identity infrastructure.
151
-
152
-
153
- ---
154
-
155
- ## Requirements
156
-
157
- • Node.js 20.19+, 22.12+, or 24+ for every preset
158
-
159
- • Docker with Compose for the generated PostgreSQL and Redis services
160
-
161
- • Redis is required for refresh-token rotation, OAuth state, rate limits, and session controls
162
-
163
- ### CLI compatibility
164
-
165
- The CLI is tested on Linux, Windows, and macOS. Express presets support npm, pnpm, and Bun dependency installation; the fullstack preset uses npm workspaces. Prisma-backed pnpm and Bun projects include explicit approvals for the native dependency build steps they require.
166
-
167
- | Option | Purpose |
168
- | --- | --- |
169
- | `--package-manager npm\|pnpm\|bun` | Select the installer for an Express preset |
170
- | `--no-install` | Generate files without installing dependencies |
171
- | `--resume` | Continue an interrupted generation |
172
- | `--production-ready` | Add PM2 configuration to an Express preset |
173
- | `--version` | Print the installed CLI version |
174
-
175
- Git initialization is skipped with a clear status when Git is unavailable. Installer failures retain the package manager's diagnostic output, while successful interactive runs keep the progress display concise.
176
-
177
- ---
178
-
179
- ## Environment Variables
180
-
181
- The CLI creates a git-ignored `.env` with a unique ES256 signing key and refresh secret. Move those values into your deployment secret manager; never commit the private JWK.
182
-
183
- ### Express API presets
184
-
185
- ```dotenv
186
- DATABASE_URL=file:./dev.db
187
- AUTHENIK8_SIGNING_JWKS='[{"kty":"EC","crv":"P-256","kid":"<key-id>","x":"...","y":"...","d":"...","alg":"ES256"}]'
188
- AUTHENIK8_ACTIVE_KID=<key-id>
189
- AUTHENIK8_ISSUER=http://localhost:3000
190
- AUTHENIK8_AUDIENCE=my-app-api
191
- REFRESH_SECRET=<generated-random-secret>
192
- REDIS_HOST=127.0.0.1
193
- REDIS_PORT=6379
194
- ```
195
-
196
- For password + OAuth, also configure the enabled providers:
197
-
198
- ```dotenv
199
- GOOGLE_CLIENT_ID=your-google-client-id
200
- GOOGLE_CLIENT_SECRET=your-google-client-secret
201
- GOOGLE_REDIRECT_URI=http://localhost:3000/auth/google/callback
202
- GITHUB_CLIENT_ID=your-github-client-id
203
- GITHUB_CLIENT_SECRET=your-github-client-secret
204
- GITHUB_REDIRECT_URI=http://localhost:3000/auth/github/callback
205
- ```
206
-
207
- ### Full-stack preset
208
-
209
- ```dotenv
210
- WEB_ORIGIN=http://localhost:5173
211
- DATABASE_URL=postgresql://postgres:postgres@localhost:5432/authenik8?schema=public
212
- REDIS_URL=redis://localhost:6379
213
- AUTHENIK8_SIGNING_JWKS='[{"kty":"EC","crv":"P-256","kid":"<key-id>","x":"...","y":"...","d":"...","alg":"ES256"}]'
214
- AUTHENIK8_ACTIVE_KID=<key-id>
215
- AUTHENIK8_ISSUER=http://localhost:3000
216
- AUTHENIK8_AUDIENCE=my-app-api
217
- REFRESH_SECRET=<generated-random-secret>
218
- GOOGLE_REDIRECT_URI=http://localhost:3000/api/auth/oauth/google/callback
219
- GITHUB_REDIRECT_URI=http://localhost:3000/api/auth/oauth/github/callback
220
- ```
221
-
222
- OAuth variables are generated only for selected providers. The fullstack `.env.example` also documents cookie, proxy, logging, mail delivery, and seed-admin settings.
223
-
224
- ---
225
-
226
-
227
- ## RBAC Example
228
-
229
- Example of a protected route:
230
- ```
231
- app.get("/admin", auth.requireAdmin, (req, res) => {
232
- res.json({ message: "Admin only route" });
233
- });
234
- ```
235
-
236
- ---
237
-
238
- ### Testing
239
-
240
- - Full test suite with ``80%`` coverage (actively improving)
241
-
242
- - CI runs tests + coverage on every push and PR
243
-
244
- ---
245
-
246
- ## How It Works (Key Concept)
247
-
248
- Authenik8 is not just another auth library.
249
- It is an auth system generator.
250
- At its core is the Identity Engine ``(authenik8-core)`` that treats authentication as an
251
-
252
- # identity resolution problem:
253
-
254
- • Unifies credentials (email/password) + OAuth providers
255
-
256
- • Prevents duplicate identities
257
-
258
- • Handles account linking intelligently
259
-
260
- • Normalizes data across providers
261
-
262
- This design makes future additions (MFA, WebAuthn, etc.) much cleaner.
263
-
264
- ---
265
- ## Powered by
266
-
267
- authenik8-core (v2.0.3) JOSE/JWK human + agent identity engine (beta)
268
-
269
- ---
270
-
271
- ## How authenik8-core works in generated apps
272
-
273
- Generated projects call:
274
-
275
- ```ts
276
- const auth = await createAuthenik8({
277
- jwt: authJwkConfig(),
278
- refreshSecret: requiredSecret("REFRESH_SECRET"),
279
- agent: agentIdentityConfig(),
280
- oauth: {
281
- google: {
282
- clientId: requiredEnv("GOOGLE_CLIENT_ID"),
283
- clientSecret: requiredEnv("GOOGLE_CLIENT_SECRET"),
284
- redirectUri: requiredEnv("GOOGLE_REDIRECT_URI"),
285
- },
286
- github: {
287
- clientId: requiredEnv("GITHUB_CLIENT_ID"),
288
- clientSecret: requiredEnv("GITHUB_CLIENT_SECRET"),
289
- redirectUri: requiredEnv("GITHUB_REDIRECT_URI"),
290
- },
291
- },
292
- });
293
- ```
294
-
295
- That factory returns one auth object used by the generated routes:
296
-
297
- • `signToken(payload)` creates access tokens.
298
-
299
- • `verifyToken(token)` verifies access tokens.
300
-
301
- • `requireAuth` verifies the token and rejects Redis-revoked sessions.
302
-
303
- • `getJwks()` returns only public verification keys for the generated JWKS endpoint.
304
-
305
- • `generateRefreshToken(payload)` creates stateful refresh tokens.
306
-
307
- • `refreshToken(refreshToken)` rotates refresh tokens and returns a new access/refresh pair.
308
-
309
- • `helmet`, `rateLimit`, and `ipWhitelist` are Express middleware.
310
-
311
- • `requireAdmin` protects admin-only routes by checking `role: "admin"`.
312
-
313
- • `oauth.google` and `oauth.github` provide redirect and callback handlers.
314
-
315
- • `issueTokens(payload)` creates an access/refresh pair with one shared session ID.
316
-
317
- • `agent.issueToken(...)` creates a scoped, Redis-session-backed M2M identity after your application authenticates the workload.
318
-
319
- • `agent.issueDelegatedToken(...)` creates an agent token bound to an active human session and explicit delegation policy.
320
-
321
- • `agent.requireScopes(...)`, `agent.revokeSession(...)`, and `agent.revokeAgent(...)` enforce machine authorization and revocation.
322
-
323
- ### Agent identity mapping
324
-
325
- Generated projects keep agent identity disabled with `AUTHENIK8_AGENTS={}`.
326
- Adding a validated agent-to-scope mapping enables the optional core API. Every
327
- project also receives `AGENT_IDENTITY.md` with a database-registry example,
328
- trusted workload exchange, scoped route, delegated-user flow, and revocation.
329
-
330
- The CLI intentionally does not scaffold a public token-minting endpoint. It
331
- cannot safely infer whether your workload uses mTLS, a cloud workload identity,
332
- or a signed client assertion. Applications must authenticate that workload
333
- before calling the privileged `agent.issueToken()` primitive.
334
-
335
- ### Redis-backed token lifecycle
336
-
337
- Authenik8-core intentionally makes JWT auth stateful:
338
-
339
- 1. Access tokens are signed with the active ES256 JWK and carry its `kid`, issuer, audience, and token purpose.
340
- 2. Refresh tokens are signed with `REFRESH_SECRET` and include a unique `jti`.
341
- 3. Each session's current refresh token is stored under `refresh:<userId>:<sessionId>` and indexed for complete user-wide revocation.
342
- 4. Refresh calls acquire a Redis lock with `lock:<userId>:<sessionId>`.
343
- 5. The submitted refresh token must match the Redis value.
344
- 6. A new access token and refresh token are issued.
345
- 7. The new refresh token atomically replaces the old one.
346
- 8. Reusing the old refresh token fails.
347
-
348
- This is why Redis is required. It enables refresh-token replay protection, concurrent refresh protection, and server-side session control.
349
-
350
- To rotate keys, append a new private JWK to `AUTHENIK8_SIGNING_JWKS`, set `AUTHENIK8_ACTIVE_KID` to its `kid`, and retain old public JWKs until all tokens signed by them have expired. `/.well-known/jwks.json` publishes every verification key without private fields.
351
-
352
- ### OAuth identity resolution
353
-
354
- OAuth is not handled as separate unrelated Passport-style strategies. Provider callbacks are normalized into this profile shape:
355
-
356
- ```ts
357
- {
358
- email: "user@example.com",
359
- name: "User Name",
360
- provider: "google",
361
- providerId: "provider-user-id",
362
- email_verified: true
363
- }
364
- ```
365
-
366
- The Identity Engine then decides:
367
-
368
- • Existing provider login: provider is already linked, so tokens are issued.
369
-
370
- • New user creation: no matching identity exists, so a new user identity is created.
371
-
372
- • Link required: an email match exists but policy requires explicit account linking.
373
-
374
- • Link provider: an authenticated user links Google or GitHub to their existing account.
375
-
376
- OAuth state is stored in Redis for five minutes under `oauth:state:<state>`, and Redis-backed identity records use:
377
-
378
- ```text
379
- oauth:v1:user:<userId>
380
- oauth:v1:email:<email>
381
- oauth:v1:provider:<provider>:<providerId>
382
- ```
383
-
384
- ### Security middleware
385
-
386
- Generated apps use the middleware returned by core:
387
-
388
- ```ts
389
- app.use(auth.helmet);
390
- app.use(auth.rateLimit);
391
- ```
392
-
393
- `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.
394
-
395
- ### Common core errors
396
-
397
- • `MissingTokenError`: no refresh token was sent.
398
-
399
- • `InvalidTokenError`: refresh token is invalid, expired, reused, or replaced.
400
-
401
- • `Concurrent refresh detected`: two refresh requests tried to rotate the same token at once.
402
-
403
- • `OAuthError:Invalid or expired state`: OAuth callback state is missing from Redis.
404
-
405
- • `OAuth profile email must be verified before issuing tokens`: provider email was not verified.
406
-
407
- • `Provider already linked to another user`: account linking tried to attach an already-owned provider.
408
-
409
- ---
410
-
411
- ## Threat Model
412
-
413
- Generated apps include a `THREAT_MODEL.md` file. It explains:
414
-
415
- • what the generated app protects,
416
-
417
- • what `authenik8-core` handles with Redis-backed token state,
418
-
419
- • what threats remain your responsibility,
420
-
421
- • and what must be configured before production.
422
-
423
- Key protections include refresh-token replay detection, concurrent refresh locking, OAuth state validation, verified-email OAuth token issuance, Redis-backed rate limiting, secure headers, human and agent session tracking, scoped agent middleware, delegated actor chains, and revocation.
424
-
425
- 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.
426
-
427
- 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.
428
-
429
- ---
430
-
431
- ## Production Enhancements
432
-
433
- • PM2 cluster mode + auto-restart
434
-
435
- • Memory usage guardrails
436
-
437
- • Security middleware (Helmet, rate limiting, etc.)
438
-
439
- ---
440
-
441
- ## The Identity Engine
442
-
443
- At the heart of Authenik8 is the **Identity Engine** , a unified authentication core built into `authenik8-core`.
444
-
445
- ---
446
-
447
- ### Why a dedicated Identity Engine?
448
-
449
- Traditional auth systems treat login as separate, isolated flows:
450
-
451
- • Email/password goes one way
452
-
453
- • Google OAuth another way
454
-
455
- • GitHub yet another
456
-
457
- This leads to duplicate accounts, inconsistent data, fragile linking logic, and security gaps.
458
-
459
- The **Identity Engine** solves this by treating authentication as an **identity resolution problem** instead of just credential validation.
460
-
461
- ---
462
-
463
- ### What the Identity Engine does
464
-
465
- • **Unified Identity Resolution**
466
- It intelligently resolves any login method (credentials, OAuth, or future strategies) into a single, consistent user identity in your system.
467
-
468
- • **Smart Account Linking**
469
- Automatically detects when a user already exists (via email or other signals) and offers secure linking instead of creating duplicates.
470
-
471
- • **Profile Normalization**
472
- Converts provider-specific data (Google profile, GitHub profile, etc.) into your app’s clean, unified user schema.
473
-
474
- • **Secure Token Lifecycle Management**
475
- Handles JWT access + refresh tokens with rotation, JTI-based replay protection, and Redis-backed stateful control.
476
-
477
- - **Consistent Security Layer**
478
- Applies the same high-security rules (rate limiting, IP awareness, session controls) across all authentication methods.
479
-
480
- ---
481
-
482
- ### OAuth Through the Identity Engine
483
-
484
- OAuth (Google, GitHub, and more coming) is **not** implemented as direct Passport.js-style routes. Instead:
485
-
486
- 1. The provider callback is received
487
-
488
- 2. The Identity Engine resolves/normalizes the profile
489
-
490
- 3. It decides: login existing user, link to existing account, or create new identity
491
-
492
- 4. Returns consistent tokens and user data
493
-
494
- This design makes adding new providers or authentication methods much cleaner and more secure.
495
-
496
- ---
497
-
498
- ## Authenik8 vs Passport.js
499
-
500
- | Aspect | **Authenik8** | **Passport.js** |
501
- |---------------------------|----------------------------------------------------|------------------------------------------|
502
- | **Purpose** | Full auth system generator | Authentication middleware |
503
- | **Setup Time** | \~30 seconds (complete project) | Hours to days |
504
- | **JWT + Refresh Tokens** | Secure rotation + replay protection built-in | Manual implementation required |
505
- | **OAuth** | Unified via Identity Engine (smart linking) | Separate strategies per provider |
506
- | **RBAC** | Built-in middleware | Not included |
507
- | **Production Features** | PM2, Helmet, rate limiting, memory guards | None (you add them) |
508
- | **Identity Management** | Centralized Identity Engine | None |
509
- | **Flexibility** | Medium (opinionated & extensible) | Very high |
510
- | **Best For** | Fast, secure, consistent backends | Maximum customization |
511
-
512
-
513
- Passport.js is a great flexible tool, but it leaves you to build secure JWT, refresh logic, OAuth linking, and RBAC yourself.
514
- Authenik8 gives you a complete, production-ready authentication system from day one.
515
-
516
-
517
- ---
518
-
519
- ### Benefits for you
520
-
521
- • No more duplicate user headaches
522
-
523
- • Consistent security behavior across all login methods
524
-
525
- • Easier future-proofing (MFA, WebAuthn, enterprise SSO, etc.)
526
-
527
- • Cleaner, more maintainable codebase in your generated project
528
-
529
- The Identity Engine is what makes Authenik8 feel like a coherent **authentication system** rather than a collection of routes and middleware.
26
+ Choose the fullstack application for a connected React and Express project, or select one of the focused Express API presets. Start with the [quickstart guide](docs/quickstart.md) for requirements and generated-project commands.
530
27
 
531
- ---
28
+ ## Documentation
532
29
 
533
- ## Notes
534
30
 
535
- This generates a starter project, not a full framework
31
+ ### Create and maintain projects
536
32
 
537
- Redis is mandatory for security features
33
+ - [Quickstart](docs/quickstart.md)
34
+ - [Non-interactive generation](docs/non-interactive-generation.md)
35
+ - [Project diagnostics](docs/project-diagnostics.md)
36
+ - [Post-generation recipes](docs/post-generation-recipes.md)
37
+ - [Upgrade planning and CI policy](docs/upgrades-and-ci.md)
38
+ - [CLI reference](docs/cli-reference.md)
538
39
 
539
- authenik8-core is closed-source for security reasons (implementation details)
40
+ ### Choose an application shape
540
41
 
42
+ - [Presets](docs/presets.md)
43
+ - [Fullstack App Kit](docs/fullstack-app-kit.md)
44
+ - [Core authentication features](docs/authentication-features.md)
45
+ - [Why create-authenik8-app](docs/why-create-authenik8-app.md)
46
+ - [Requirements](docs/requirements.md)
541
47
 
48
+ ### Configure authentication
542
49
 
543
- ---
50
+ - [Environment variables](docs/environment-variables.md)
51
+ - [RBAC example](docs/rbac-example.md)
52
+ - [Identity Engine](docs/identity-engine.md)
53
+ - [Using authenik8-core](docs/authenik8-core.md)
54
+ - [Threat model](docs/threat-model.md)
544
55
 
545
- ## Roadmap
546
56
 
547
- Advanced RBAC (custom roles/permissions)
57
+ ### Operate and evaluate
548
58
 
549
- webAuthn
59
+ - [Testing](docs/testing.md)
60
+ - [Production enhancements](docs/production-enhancements.md)
61
+ - [Authenik8 compared with Passport.js](docs/authenik8-vs-passport.md)
62
+ - [Project notes](docs/notes.md)
63
+ - [Roadmap](docs/roadmap.md)
550
64
 
551
- MFA
65
+ ## Contributing and support
552
66
 
553
- Production presets
67
+ - [Contributing](CONTRIBUTING.md)
68
+ - [Testing this repository](TESTING.md)
69
+ - [Security policy](SECURITY.md)
70
+ - [Code of conduct](CODE_OF_CONDUCT.md)
71
+ - [Changelog](CHANGELOG.md)
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../src/bin/cli.ts"],"names":[],"mappings":""}
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+ import { doctorHelp, formatDoctorReport } from "../commands/doctor/output.js";
3
+ import { DoctorUsageError, parseDoctorArguments, runDoctor, } from "../commands/doctor/index.js";
4
+ import { DoctorProjectError } from "../commands/doctor/context.js";
5
+ import { addHelp, formatAddResult, formatRecipeList } from "../commands/add/output.js";
6
+ import { AddUsageError, parseAddArguments, runAdd } from "../commands/add/index.js";
7
+ import { AddProjectError } from "../commands/add/context.js";
8
+ import { AddRecipeError } from "../commands/add/plan.js";
9
+ import { formatUpgradePlan, upgradeHelp } from "../commands/upgrade/output.js";
10
+ import { parseUpgradeArguments, runUpgrade, upgradeCheckExitCode, UpgradeUsageError, } from "../commands/upgrade/index.js";
11
+ import { UpgradeProjectError } from "../commands/upgrade/context.js";
12
+ import { resolveRootCommand } from "../lib/rootCommand.js";
13
+ function write(stream, value) {
14
+ stream.write(value.endsWith("\n") ? value : `${value}\n`);
15
+ }
16
+ async function main() {
17
+ const route = resolveRootCommand(process.argv.slice(2));
18
+ if (route.name === "create") {
19
+ process.argv = [process.argv[0] ?? "node", process.argv[1] ?? "create-authenik8-app", ...route.args];
20
+ await import("./index.js");
21
+ return;
22
+ }
23
+ if (route.name === "add") {
24
+ try {
25
+ const options = parseAddArguments(route.args);
26
+ if (options.help)
27
+ write(process.stdout, addHelp());
28
+ else if (options.list)
29
+ write(process.stdout, formatRecipeList());
30
+ else
31
+ write(process.stdout, formatAddResult(await runAdd(options)));
32
+ }
33
+ catch (error) {
34
+ if (error instanceof AddUsageError) {
35
+ write(process.stderr, `Error: ${error.message}\n${addHelp()}`);
36
+ process.exitCode = 2;
37
+ }
38
+ else if (error instanceof AddProjectError || error instanceof AddRecipeError) {
39
+ write(process.stderr, `Error: ${error.message}`);
40
+ process.exitCode = 1;
41
+ }
42
+ else {
43
+ const detail = error instanceof Error ? error.message : String(error);
44
+ write(process.stderr, `Error: Recipe could not complete: ${detail}`);
45
+ process.exitCode = 1;
46
+ }
47
+ }
48
+ return;
49
+ }
50
+ if (route.name === "upgrade") {
51
+ try {
52
+ const options = parseUpgradeArguments(route.args);
53
+ if (options.help)
54
+ write(process.stdout, upgradeHelp());
55
+ else {
56
+ const plan = await runUpgrade(options);
57
+ write(process.stdout, formatUpgradePlan(plan, options.json));
58
+ if (options.check)
59
+ process.exitCode = upgradeCheckExitCode(plan);
60
+ }
61
+ }
62
+ catch (error) {
63
+ if (error instanceof UpgradeUsageError) {
64
+ write(process.stderr, `Error: ${error.message}\n${upgradeHelp()}`);
65
+ process.exitCode = 2;
66
+ }
67
+ else if (error instanceof UpgradeProjectError) {
68
+ write(process.stderr, `Error: ${error.message}`);
69
+ process.exitCode = 1;
70
+ }
71
+ else {
72
+ const detail = error instanceof Error ? error.message : String(error);
73
+ write(process.stderr, `Error: Upgrade plan could not complete: ${detail}`);
74
+ process.exitCode = 1;
75
+ }
76
+ }
77
+ return;
78
+ }
79
+ try {
80
+ const options = parseDoctorArguments(route.args);
81
+ if (options.help) {
82
+ write(process.stdout, doctorHelp());
83
+ }
84
+ else {
85
+ const report = await runDoctor(options);
86
+ write(process.stdout, formatDoctorReport(report, options.json));
87
+ if (report.summary.failed > 0)
88
+ process.exitCode = 1;
89
+ }
90
+ }
91
+ catch (error) {
92
+ if (error instanceof DoctorUsageError) {
93
+ write(process.stderr, `Error: ${error.message}\n${doctorHelp()}`);
94
+ process.exitCode = 2;
95
+ }
96
+ else if (error instanceof DoctorProjectError) {
97
+ write(process.stderr, `Error: ${error.message}`);
98
+ process.exitCode = 1;
99
+ }
100
+ else {
101
+ const detail = error instanceof Error ? error.message : String(error);
102
+ write(process.stderr, `Error: Doctor could not complete: ${detail}`);
103
+ process.exitCode = 1;
104
+ }
105
+ }
106
+ }
107
+ void main().catch((error) => {
108
+ const detail = error instanceof Error ? error.message : String(error);
109
+ write(process.stderr, `Error: ${detail}`);
110
+ process.exitCode = 1;
111
+ });
112
+ //# sourceMappingURL=cli.js.map