create-authenik8-app 1.0.7 → 2.0.1
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.
- package/LICENSE +22 -0
- package/README.md +18 -27
- package/dist/bin/index.js +93 -25
- package/dist/bin/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/express-auth/package.json +24 -0
- package/templates/express-auth/src/prisma/client.ts +15 -0
- package/templates/express-auth/src/server.ts +78 -0
- package/templates/express-auth/src/utils/hash.ts +12 -0
- package/templates/express-auth/tsconfig.json +16 -0
- package/templates/express-base/src/server.ts +56 -0
- package/templates/prisma/sqlite/schema.prisma +1 -6
- package/templates/express-ts/src/server.ts +0 -36
- /package/templates/{express-ts → express-base}/package.json +0 -0
- /package/templates/{express-ts → express-base}/src/.env.example +0 -0
- /package/templates/{express-ts → express-base}/src/package-lock.json +0 -0
- /package/templates/{express-ts → express-base}/tsconfig.json +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
MIT License
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2026 TheSBD
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
---
|
|
5
|
-
|
|
6
2
|
# create-authenik8-app
|
|
7
3
|
|
|
8
4
|
<p align="center">
|
|
@@ -15,7 +11,7 @@
|
|
|
15
11
|
|
|
16
12
|
---
|
|
17
13
|
|
|
18
|
-
##
|
|
14
|
+
## Usage
|
|
19
15
|
|
|
20
16
|
Create a new project:
|
|
21
17
|
|
|
@@ -27,8 +23,8 @@ Then:
|
|
|
27
23
|
cd my-app
|
|
28
24
|
npm install
|
|
29
25
|
npm run dev
|
|
30
|
-
```
|
|
31
26
|
|
|
27
|
+
```
|
|
32
28
|
---
|
|
33
29
|
|
|
34
30
|
## What you get instantly
|
|
@@ -37,19 +33,19 @@ A fully working Express authentication starter with:
|
|
|
37
33
|
|
|
38
34
|
JWT authentication (access + refresh tokens)
|
|
39
35
|
|
|
40
|
-
Secure refresh token rotation
|
|
36
|
+
Secure refresh token rotation
|
|
41
37
|
|
|
42
|
-
Redis-based token storage
|
|
38
|
+
Redis-based token storage
|
|
43
39
|
|
|
44
|
-
Role-Based Access Control (RBAC)
|
|
40
|
+
Role-Based Access Control (RBAC)
|
|
45
41
|
|
|
46
42
|
TypeScript setup
|
|
47
43
|
|
|
48
|
-
Express server preconfigured
|
|
44
|
+
Express server preconfigured
|
|
49
45
|
|
|
50
46
|
Clean scalable folder structure
|
|
51
47
|
|
|
52
|
-
.env file generated automatically
|
|
48
|
+
.env file generated automatically
|
|
53
49
|
|
|
54
50
|
|
|
55
51
|
|
|
@@ -83,8 +79,8 @@ Redis (required for refresh tokens)
|
|
|
83
79
|
|
|
84
80
|
---
|
|
85
81
|
|
|
86
|
-
## Redis Setup
|
|
87
|
-
```
|
|
82
|
+
## Redis Setup
|
|
83
|
+
```
|
|
88
84
|
|
|
89
85
|
Local
|
|
90
86
|
|
|
@@ -93,11 +89,10 @@ redis-server
|
|
|
93
89
|
Docker
|
|
94
90
|
|
|
95
91
|
docker run -p 6379:6379 redis
|
|
96
|
-
|
|
97
92
|
```
|
|
98
93
|
---
|
|
99
94
|
|
|
100
|
-
|
|
95
|
+
Environment Variables
|
|
101
96
|
|
|
102
97
|
Generated automatically:
|
|
103
98
|
```
|
|
@@ -106,22 +101,22 @@ REFRESH_SECRET=your-refresh-secret
|
|
|
106
101
|
|
|
107
102
|
REDIS_HOST=127.0.0.1
|
|
108
103
|
REDIS_PORT=6379
|
|
109
|
-
|
|
110
104
|
```
|
|
105
|
+
|
|
111
106
|
---
|
|
112
107
|
|
|
113
|
-
|
|
114
|
-
```
|
|
115
|
-
Example of a protected route:
|
|
108
|
+
RBAC Example
|
|
116
109
|
|
|
110
|
+
Example of a protected route:
|
|
111
|
+
```
|
|
117
112
|
app.get("/admin", auth.requireAdmin, (req, res) => {
|
|
118
113
|
res.json({ message: "Admin only route" });
|
|
119
114
|
});
|
|
120
|
-
|
|
121
115
|
```
|
|
116
|
+
|
|
122
117
|
---
|
|
123
118
|
|
|
124
|
-
|
|
119
|
+
## Powered by
|
|
125
120
|
|
|
126
121
|
authenik8-core
|
|
127
122
|
|
|
@@ -131,10 +126,9 @@ authenik8-core
|
|
|
131
126
|
|
|
132
127
|
## Project Structure
|
|
133
128
|
```
|
|
134
|
-
|
|
135
129
|
my-app/
|
|
136
130
|
├── src/
|
|
137
|
-
│
|
|
131
|
+
│ ├
|
|
138
132
|
│ ├
|
|
139
133
|
│ └── server.ts
|
|
140
134
|
├── .env
|
|
@@ -156,7 +150,7 @@ RBAC is included via middleware (e.g. requireAdmin)
|
|
|
156
150
|
|
|
157
151
|
---
|
|
158
152
|
|
|
159
|
-
|
|
153
|
+
## Roadmap
|
|
160
154
|
|
|
161
155
|
OAuth providers (Google, GitHub)
|
|
162
156
|
|
|
@@ -168,6 +162,3 @@ Admin dashboard
|
|
|
168
162
|
|
|
169
163
|
Production presets
|
|
170
164
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
---
|
package/dist/bin/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import fs from "fs-extra";
|
|
|
3
3
|
import path from "path";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import inquirer from "inquirer";
|
|
6
|
+
import { ExitPromptError } from "@inquirer/core";
|
|
6
7
|
import ora from "ora";
|
|
7
8
|
import { execSync } from "child_process";
|
|
8
9
|
import { fileURLToPath } from "url";
|
|
@@ -14,6 +15,7 @@ if (!projectName) {
|
|
|
14
15
|
process.exit(1);
|
|
15
16
|
}
|
|
16
17
|
const targetDir = path.join(process.cwd(), projectName);
|
|
18
|
+
let projectCreated = false;
|
|
17
19
|
async function main() {
|
|
18
20
|
console.log(chalk.blue.bold("\n🚀 Authenik8 App Generator\n"));
|
|
19
21
|
if (process.argv.includes("--help")) {
|
|
@@ -72,8 +74,8 @@ Available options:
|
|
|
72
74
|
name: "database",
|
|
73
75
|
message: "Choose database:",
|
|
74
76
|
choices: [
|
|
75
|
-
{ name: "PostgreSQL", value: "postgresql" },
|
|
76
|
-
{ name: "SQLite (quick start)", value: "sqlite" }
|
|
77
|
+
{ name: "PostgreSQL", value: "postgresql(recommended for auth)" },
|
|
78
|
+
{ name: "SQLite (quick start, limited features)", value: "sqlite" }
|
|
77
79
|
],
|
|
78
80
|
when: (answers) => answers.usePrisma,
|
|
79
81
|
},
|
|
@@ -83,6 +85,13 @@ Available options:
|
|
|
83
85
|
message: "Initialize git?",
|
|
84
86
|
default: true,
|
|
85
87
|
},
|
|
88
|
+
{
|
|
89
|
+
type: "confirm",
|
|
90
|
+
name: "usePasswordAuth",
|
|
91
|
+
message: "Include email/password authentication?",
|
|
92
|
+
default: true,
|
|
93
|
+
when: (answers) => answers.usePrisma && answers.database === "postgresql"
|
|
94
|
+
},
|
|
86
95
|
]);
|
|
87
96
|
// 🚫 Prevent overwrite
|
|
88
97
|
if (fs.existsSync(targetDir)) {
|
|
@@ -91,13 +100,16 @@ Available options:
|
|
|
91
100
|
}
|
|
92
101
|
console.log(chalk.cyan("\n⚙️ Setting things up...\n"));
|
|
93
102
|
const templateRoot = path.resolve(__dirname, "../../templates");
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
103
|
+
let templateName = "express-base";
|
|
104
|
+
if (answers.usePasswordAuth && answers.usePrisma) {
|
|
105
|
+
templateName = "express-auth";
|
|
106
|
+
}
|
|
107
|
+
const templatePath = path.join(templateRoot, templateName);
|
|
97
108
|
// 📁 Create project (SPINNER)
|
|
98
109
|
const createSpinner = ora("Creating project structure...").start();
|
|
99
110
|
try {
|
|
100
111
|
await fs.copy(templatePath, targetDir);
|
|
112
|
+
projectCreated = true;
|
|
101
113
|
createSpinner.succeed("Project files created");
|
|
102
114
|
}
|
|
103
115
|
catch (err) {
|
|
@@ -108,8 +120,8 @@ Available options:
|
|
|
108
120
|
if (answers.usePrisma) {
|
|
109
121
|
const prismaSpinner = ora("Adding Prisma setup...").start();
|
|
110
122
|
try {
|
|
111
|
-
const dbType = answers.database
|
|
112
|
-
? "postgresql"
|
|
123
|
+
const dbType = answers.database ===
|
|
124
|
+
"postgresql" ? "postgresql"
|
|
113
125
|
: "sqlite";
|
|
114
126
|
const prismaTemplatePath = path.join(templateRoot, `prisma/${dbType}`);
|
|
115
127
|
// Copy prisma schema
|
|
@@ -118,14 +130,15 @@ Available options:
|
|
|
118
130
|
await fs.copy(path.join(prismaTemplatePath, ".env"), path.join(targetDir, ".env"));
|
|
119
131
|
const pkgPath = path.join(targetDir, "package.json");
|
|
120
132
|
const pkg = await fs.readJson(pkgPath);
|
|
133
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
121
134
|
// Inject dependencies
|
|
122
135
|
pkg.dependencies = {
|
|
123
136
|
...pkg.dependencies,
|
|
124
|
-
"@prisma/client": "
|
|
137
|
+
"@prisma/client": "5.22.0",
|
|
125
138
|
};
|
|
126
139
|
pkg.devDependencies = {
|
|
127
140
|
...pkg.devDependencies,
|
|
128
|
-
prisma: "
|
|
141
|
+
prisma: "5.22.0",
|
|
129
142
|
};
|
|
130
143
|
// Add scripts
|
|
131
144
|
pkg.scripts = {
|
|
@@ -133,26 +146,52 @@ Available options:
|
|
|
133
146
|
"prisma:generate": "prisma generate",
|
|
134
147
|
"prisma:migrate": "prisma migrate dev",
|
|
135
148
|
};
|
|
136
|
-
prismaSpinner.succeed(`Prisma (${
|
|
149
|
+
prismaSpinner.succeed(`Prisma (${dbType}) configured`);
|
|
137
150
|
}
|
|
138
151
|
catch (err) {
|
|
139
152
|
prismaSpinner.fail("Failed to setup Prisma");
|
|
140
153
|
console.error(err);
|
|
141
154
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
const installSpinner = ora("Installing dependencies...(this may take a few minutes)").start();
|
|
156
|
+
try {
|
|
157
|
+
execSync("npm install", {
|
|
158
|
+
cwd: targetDir,
|
|
159
|
+
stdio: "ignore",
|
|
160
|
+
});
|
|
161
|
+
installSpinner.succeed("Dependencies installed");
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
installSpinner.fail("Failed to install dependencies");
|
|
165
|
+
console.error(err);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
if (answers.usePrisma) {
|
|
169
|
+
const prismaGenSpinner = ora("Generating Prisma client...").start();
|
|
170
|
+
try {
|
|
171
|
+
execSync("npx prisma@5.22.0 generate", {
|
|
172
|
+
cwd: targetDir,
|
|
173
|
+
stdio: "ignore"
|
|
174
|
+
});
|
|
175
|
+
prismaGenSpinner.succeed("Prisma client generated");
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
prismaGenSpinner.fail("Failed to generate Prisma client");
|
|
179
|
+
console.error(err);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const authSpinner = ora("Installing password auth...").start();
|
|
183
|
+
try {
|
|
184
|
+
execSync("npm install argon2", {
|
|
185
|
+
cwd: targetDir,
|
|
186
|
+
stdio: "ignore",
|
|
187
|
+
});
|
|
188
|
+
authSpinner.succeed("Password auth ready (argon2)");
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
authSpinner.fail("Failed to install password auth");
|
|
192
|
+
console.error(err);
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
156
195
|
}
|
|
157
196
|
if (answers.useGit) {
|
|
158
197
|
const gitSpinner = ora("Initializing git...").start();
|
|
@@ -177,6 +216,35 @@ Next steps:
|
|
|
177
216
|
|
|
178
217
|
🔥 Your Authenik8 server is ready to go!
|
|
179
218
|
`));
|
|
219
|
+
console.log(`
|
|
220
|
+
✔ Auth: ${answers.usePasswordAuth ? "JWT + Password" : "JWT only"}
|
|
221
|
+
✔ Database: ${answers.usePrisma ? answers.database : "None"}
|
|
222
|
+
✔ ORM: ${answers.usePrisma ? "Prisma" : "None"}
|
|
223
|
+
`);
|
|
180
224
|
}
|
|
181
|
-
|
|
225
|
+
process.on("SIGINT", async () => {
|
|
226
|
+
console.log("\n👋 Authenik8 setup cancelled.");
|
|
227
|
+
if (projectCreated && fs.existsSync(targetDir)) {
|
|
228
|
+
await fs.remove(targetDir);
|
|
229
|
+
console.log("🧹 Cleaned up incomplete project.");
|
|
230
|
+
}
|
|
231
|
+
process.exit(0);
|
|
232
|
+
});
|
|
233
|
+
main().catch(async (err) => {
|
|
234
|
+
if (err instanceof ExitPromptError) {
|
|
235
|
+
console.log("\n👋 Authenik8 setup cancelled.");
|
|
236
|
+
if (projectCreated && fs.existsSync(targetDir)) {
|
|
237
|
+
await fs.remove(targetDir);
|
|
238
|
+
console.log("🧹 Cleaned up incomplete project.");
|
|
239
|
+
}
|
|
240
|
+
process.exit(0);
|
|
241
|
+
}
|
|
242
|
+
console.error("\n❌ Unexpected error:");
|
|
243
|
+
console.error(err);
|
|
244
|
+
if (projectCreated && fs.existsSync(targetDir)) {
|
|
245
|
+
await fs.remove(targetDir);
|
|
246
|
+
console.log("🧹 Cleaned up incomplete project.");
|
|
247
|
+
}
|
|
248
|
+
process.exit(1);
|
|
249
|
+
});
|
|
182
250
|
//# sourceMappingURL=index.js.map
|
package/dist/bin/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bin/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAGpC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAG3C,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEpC,IAAI,CAAC,WAAW,EAAE,CAAC;IACjB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC,CAAC;IAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bin/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-authenik8-app",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description":"Create production-ready backend APIs in seconds. Authenik8 scaffolds Express + Prisma projects with JWT authentication, database setup (PostgreSQL or SQLite), and scalable architecture out of the box.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-authenik8-app": "dist/bin/index.js"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "authenik8-app",
|
|
3
|
+
"version": "1.0.6",
|
|
4
|
+
"description": "Authenik8 generated Express auth app",
|
|
5
|
+
"main": "dist/server.js",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "ts-node-dev --respawn --transpile-only src/server.ts",
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"start": "node dist/server.js",
|
|
11
|
+
"prisma:generate": "prisma generate",
|
|
12
|
+
"prisma:migrate": "prisma migrate dev"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"authenik8-core": "0.1.5",
|
|
16
|
+
"express": "^4.19.2"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/express": "^4.17.21",
|
|
20
|
+
"@types/node": "^20.0.0",
|
|
21
|
+
"ts-node-dev": "^2.0.0",
|
|
22
|
+
"typescript": "^5.0.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PrismaClient } from "@prisma/client";
|
|
2
|
+
|
|
3
|
+
const globalForPrisma = globalThis as unknown as {
|
|
4
|
+
prisma: PrismaClient | undefined;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const prisma =
|
|
8
|
+
globalForPrisma.prisma ??
|
|
9
|
+
new PrismaClient({
|
|
10
|
+
log: ["error", "warn"],
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
if (process.env.NODE_ENV !== "production") {
|
|
14
|
+
globalForPrisma.prisma = prisma;
|
|
15
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { createAuthenik8 } from "authenik8-core";
|
|
3
|
+
import { hashPassword, comparePassword } from "./utils/hash";
|
|
4
|
+
import {prisma} from "./prisma/client"
|
|
5
|
+
|
|
6
|
+
const app = express();
|
|
7
|
+
app.use(express.json());
|
|
8
|
+
|
|
9
|
+
async function start() {
|
|
10
|
+
const auth = await createAuthenik8({
|
|
11
|
+
jwtSecret: process.env.JWT_SECRET!,
|
|
12
|
+
refreshSecret: process.env.REFRESH_SECRET!,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
app.post("/register", async (req, res) => {
|
|
17
|
+
const { email, password } = req.body;
|
|
18
|
+
|
|
19
|
+
const hashedPassword = await hashPassword(password);
|
|
20
|
+
|
|
21
|
+
const user = await prisma.user.create({
|
|
22
|
+
data: {
|
|
23
|
+
email,
|
|
24
|
+
password: hashedPassword,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
res.json({ message: "User created", userId: user.id });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
app.post("/login", async (req, res) => {
|
|
33
|
+
const { email, password } = req.body;
|
|
34
|
+
|
|
35
|
+
const user = await prisma.user.findUnique({
|
|
36
|
+
where: { email },
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (!user) {
|
|
40
|
+
return res.status(401).json({ error: "Invalid credentials" });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const isValid = await comparePassword(password, user.password);
|
|
44
|
+
|
|
45
|
+
if (!isValid) {
|
|
46
|
+
return res.status(401).json({ error: "Invalid credentials" });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const accessToken = auth.signToken({
|
|
50
|
+
id: user.id,
|
|
51
|
+
email: user.email,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const refreshToken = await auth.generateRefreshToken({
|
|
55
|
+
id: user.id,
|
|
56
|
+
email: user.email,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
res.json({ accessToken, refreshToken });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
app.post("/refresh", async (req, res) => {
|
|
64
|
+
const tokens = await auth.refreshToken(req.body.refreshToken);
|
|
65
|
+
res.json(tokens);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// ✅ PROTECTED
|
|
69
|
+
app.get("/protected", auth.requireAdmin, (req, res) => {
|
|
70
|
+
res.json({ message: "Protected route" });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
app.listen(3000, () => {
|
|
74
|
+
console.log("🚀 Server running on http://localhost:3000");
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
start();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import argon2 from "argon2";
|
|
2
|
+
|
|
3
|
+
export const hashPassword = async (password: string): Promise<string> => {
|
|
4
|
+
return argon2.hash(password);
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const comparePassword = async (
|
|
8
|
+
password: string,
|
|
9
|
+
hash: string
|
|
10
|
+
): Promise<boolean> => {
|
|
11
|
+
return argon2.verify(hash, password);
|
|
12
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
|
|
12
|
+
"forceConsistentCasingInFileNames": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { createAuthenik8 } from "authenik8-core";
|
|
3
|
+
|
|
4
|
+
const app = express();
|
|
5
|
+
app.use(express.json());
|
|
6
|
+
|
|
7
|
+
async function start() {
|
|
8
|
+
const auth = await createAuthenik8({
|
|
9
|
+
jwtSecret: process.env.JWT_SECRET!,
|
|
10
|
+
refreshSecret: process.env.REFRESH_SECRET!,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
app.use(auth.helmet);
|
|
14
|
+
app.use(auth.rateLimit);
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
app.get("/public", (req, res) => {
|
|
18
|
+
res.json({ message: "Public route" });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
app.get("/guest", async (req, res) => {
|
|
23
|
+
const token = await auth.guestToken({ role: "guest" });
|
|
24
|
+
res.json({ token });
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
app.get("/protected", async (req, res) => {
|
|
30
|
+
const token = req.headers.authorization?.split(" ")[1];
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const decoded = await auth.verifyToken(token);
|
|
34
|
+
res.json({ message: "Protected data", user: decoded });
|
|
35
|
+
} catch {
|
|
36
|
+
res.status(401).json({ error: "Unauthorized" });
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
app.post("/refresh", async (req, res) => {
|
|
42
|
+
const tokens = await auth.refreshToken(req.body.refreshToken);
|
|
43
|
+
res.json(tokens);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// 🛡️ Admin route
|
|
47
|
+
app.get("/admin", auth.requireAdmin, (req, res) => {
|
|
48
|
+
res.json({ message: "Admin only" });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
app.listen(3000, () => {
|
|
52
|
+
console.log("🚀 Server running on http://localhost:3000");
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
start();
|
|
@@ -11,7 +11,7 @@ model User {
|
|
|
11
11
|
id String @id @default(uuid())
|
|
12
12
|
email String @unique
|
|
13
13
|
password String
|
|
14
|
-
role
|
|
14
|
+
role String @default("USER")
|
|
15
15
|
verified Boolean @default(false)
|
|
16
16
|
|
|
17
17
|
createdAt DateTime @default(now())
|
|
@@ -30,8 +30,3 @@ model Session {
|
|
|
30
30
|
|
|
31
31
|
createdAt DateTime @default(now())
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
enum Role {
|
|
35
|
-
USER
|
|
36
|
-
ADMIN
|
|
37
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import express from "express";
|
|
2
|
-
import { createAuthenik8 } from "authenik8-core";
|
|
3
|
-
|
|
4
|
-
const app = express();
|
|
5
|
-
app.use(express.json());
|
|
6
|
-
|
|
7
|
-
async function start() {
|
|
8
|
-
const auth = await createAuthenik8({
|
|
9
|
-
jwtSecret: process.env.JWT_SECRET!,
|
|
10
|
-
refreshSecret: process.env.REFRESH_SECRET!,
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
app.post("/login", async (req, res) => {
|
|
14
|
-
const user = { id: "123", email: "test@test.com" };
|
|
15
|
-
|
|
16
|
-
const accessToken = auth.signToken(user);
|
|
17
|
-
const refreshToken = await auth.generateRefreshToken(user);
|
|
18
|
-
|
|
19
|
-
res.json({ accessToken, refreshToken });
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
app.post("/refresh", async (req, res) => {
|
|
23
|
-
const tokens = await auth.refreshToken(req.body.refreshToken);
|
|
24
|
-
res.json(tokens);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
app.get("/protected", auth.requireAdmin, (req, res) => {
|
|
28
|
-
res.json({ message: "Protected route" });
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
app.listen(3000, () => {
|
|
32
|
-
console.log("🚀 Server running on http://localhost:3000");
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
start();
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|