create-authenik8-app 1.0.6 → 2.0.0
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/README.md +27 -18
- package/dist/bin/index.d.ts +3 -0
- package/dist/bin/index.d.ts.map +1 -0
- package/dist/bin/index.js +250 -0
- package/dist/bin/index.js.map +1 -0
- 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/LICENSE +0 -22
- 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/README.md
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
---
|
|
5
|
+
|
|
2
6
|
# create-authenik8-app
|
|
3
7
|
|
|
4
8
|
<p align="center">
|
|
@@ -11,7 +15,7 @@
|
|
|
11
15
|
|
|
12
16
|
---
|
|
13
17
|
|
|
14
|
-
##
|
|
18
|
+
## 📦 Usage
|
|
15
19
|
|
|
16
20
|
Create a new project:
|
|
17
21
|
|
|
@@ -23,8 +27,8 @@ Then:
|
|
|
23
27
|
cd my-app
|
|
24
28
|
npm install
|
|
25
29
|
npm run dev
|
|
26
|
-
|
|
27
30
|
```
|
|
31
|
+
|
|
28
32
|
---
|
|
29
33
|
|
|
30
34
|
## What you get instantly
|
|
@@ -33,19 +37,19 @@ A fully working Express authentication starter with:
|
|
|
33
37
|
|
|
34
38
|
JWT authentication (access + refresh tokens)
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
Secure refresh token rotation
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
Redis-based token storage
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
Role-Based Access Control (RBAC)
|
|
41
45
|
|
|
42
46
|
TypeScript setup
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
Express server preconfigured
|
|
45
49
|
|
|
46
50
|
Clean scalable folder structure
|
|
47
51
|
|
|
48
|
-
|
|
52
|
+
.env file generated automatically
|
|
49
53
|
|
|
50
54
|
|
|
51
55
|
|
|
@@ -79,8 +83,8 @@ Redis (required for refresh tokens)
|
|
|
79
83
|
|
|
80
84
|
---
|
|
81
85
|
|
|
82
|
-
|
|
83
|
-
|
|
86
|
+
## Redis Setup
|
|
87
|
+
```
|
|
84
88
|
|
|
85
89
|
Local
|
|
86
90
|
|
|
@@ -89,10 +93,11 @@ redis-server
|
|
|
89
93
|
Docker
|
|
90
94
|
|
|
91
95
|
docker run -p 6379:6379 redis
|
|
96
|
+
|
|
92
97
|
```
|
|
93
98
|
---
|
|
94
99
|
|
|
95
|
-
Environment Variables
|
|
100
|
+
## Environment Variables
|
|
96
101
|
|
|
97
102
|
Generated automatically:
|
|
98
103
|
```
|
|
@@ -101,22 +106,22 @@ REFRESH_SECRET=your-refresh-secret
|
|
|
101
106
|
|
|
102
107
|
REDIS_HOST=127.0.0.1
|
|
103
108
|
REDIS_PORT=6379
|
|
104
|
-
```
|
|
105
109
|
|
|
110
|
+
```
|
|
106
111
|
---
|
|
107
112
|
|
|
108
|
-
RBAC Example
|
|
109
|
-
|
|
110
|
-
Example of a protected route:
|
|
113
|
+
## RBAC Example
|
|
111
114
|
```
|
|
115
|
+
Example of a protected route:
|
|
116
|
+
|
|
112
117
|
app.get("/admin", auth.requireAdmin, (req, res) => {
|
|
113
118
|
res.json({ message: "Admin only route" });
|
|
114
119
|
});
|
|
115
|
-
```
|
|
116
120
|
|
|
121
|
+
```
|
|
117
122
|
---
|
|
118
123
|
|
|
119
|
-
|
|
124
|
+
📦 Powered by
|
|
120
125
|
|
|
121
126
|
authenik8-core
|
|
122
127
|
|
|
@@ -126,9 +131,10 @@ authenik8-core
|
|
|
126
131
|
|
|
127
132
|
## Project Structure
|
|
128
133
|
```
|
|
134
|
+
|
|
129
135
|
my-app/
|
|
130
136
|
├── src/
|
|
131
|
-
│
|
|
137
|
+
│ |
|
|
132
138
|
│ ├
|
|
133
139
|
│ └── server.ts
|
|
134
140
|
├── .env
|
|
@@ -150,7 +156,7 @@ RBAC is included via middleware (e.g. requireAdmin)
|
|
|
150
156
|
|
|
151
157
|
---
|
|
152
158
|
|
|
153
|
-
## Roadmap
|
|
159
|
+
## Roadmap
|
|
154
160
|
|
|
155
161
|
OAuth providers (Google, GitHub)
|
|
156
162
|
|
|
@@ -162,3 +168,6 @@ Admin dashboard
|
|
|
162
168
|
|
|
163
169
|
Production presets
|
|
164
170
|
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
---
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bin/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import inquirer from "inquirer";
|
|
6
|
+
import { ExitPromptError } from "@inquirer/core";
|
|
7
|
+
import ora from "ora";
|
|
8
|
+
import { execSync } from "child_process";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
const projectName = process.argv[2];
|
|
13
|
+
if (!projectName) {
|
|
14
|
+
console.log(chalk.red("❌ Please provide a project name"));
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const targetDir = path.join(process.cwd(), projectName);
|
|
18
|
+
let projectCreated = false;
|
|
19
|
+
async function main() {
|
|
20
|
+
console.log(chalk.blue.bold("\n🚀 Authenik8 App Generator\n"));
|
|
21
|
+
if (process.argv.includes("--help")) {
|
|
22
|
+
console.log(`
|
|
23
|
+
Authenik8 CLI
|
|
24
|
+
|
|
25
|
+
Usage:
|
|
26
|
+
create-authenik8-app <project-name>
|
|
27
|
+
|
|
28
|
+
Options:
|
|
29
|
+
--help Show this help message
|
|
30
|
+
|
|
31
|
+
Features:
|
|
32
|
+
- Express backend (default)
|
|
33
|
+
- Optional Prisma ORM
|
|
34
|
+
- PostgreSQL (production)
|
|
35
|
+
- SQLite (quick start)
|
|
36
|
+
- Optional Git initialization
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
create-authenik8-app my-app
|
|
40
|
+
`);
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
console.log(chalk.gray(`
|
|
44
|
+
Available options:
|
|
45
|
+
|
|
46
|
+
Frameworks:
|
|
47
|
+
- Express
|
|
48
|
+
- Fastify(coming soon)
|
|
49
|
+
|
|
50
|
+
Database (if Prisma enabled):
|
|
51
|
+
- PostgreSQL
|
|
52
|
+
- SQLite (quick start)
|
|
53
|
+
|
|
54
|
+
Features:
|
|
55
|
+
- Prisma ORM (optional)
|
|
56
|
+
- Git initialization (optional)
|
|
57
|
+
`));
|
|
58
|
+
// 🔥 PROMPTS
|
|
59
|
+
const answers = await inquirer.prompt([
|
|
60
|
+
{
|
|
61
|
+
type: "list",
|
|
62
|
+
name: "framework",
|
|
63
|
+
message: "Choose framework:",
|
|
64
|
+
choices: ["Express", "Fastify (coming soon)"],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
type: "confirm",
|
|
68
|
+
name: "usePrisma",
|
|
69
|
+
message: "Use Prisma?",
|
|
70
|
+
default: true,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
type: "list",
|
|
74
|
+
name: "database",
|
|
75
|
+
message: "Choose database:",
|
|
76
|
+
choices: [
|
|
77
|
+
{ name: "PostgreSQL", value: "postgresql(recommended for auth)" },
|
|
78
|
+
{ name: "SQLite (quick start, limited features)", value: "sqlite" }
|
|
79
|
+
],
|
|
80
|
+
when: (answers) => answers.usePrisma,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: "confirm",
|
|
84
|
+
name: "useGit",
|
|
85
|
+
message: "Initialize git?",
|
|
86
|
+
default: true,
|
|
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
|
+
},
|
|
95
|
+
]);
|
|
96
|
+
// 🚫 Prevent overwrite
|
|
97
|
+
if (fs.existsSync(targetDir)) {
|
|
98
|
+
console.log(chalk.red("\n❌ Folder already exists"));
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
console.log(chalk.cyan("\n⚙️ Setting things up...\n"));
|
|
102
|
+
const templateRoot = path.resolve(__dirname, "../../templates");
|
|
103
|
+
let templateName = "express-base";
|
|
104
|
+
if (answers.usePasswordAuth && answers.usePrisma) {
|
|
105
|
+
templateName = "express-auth";
|
|
106
|
+
}
|
|
107
|
+
const templatePath = path.join(templateRoot, templateName);
|
|
108
|
+
// 📁 Create project (SPINNER)
|
|
109
|
+
const createSpinner = ora("Creating project structure...").start();
|
|
110
|
+
try {
|
|
111
|
+
await fs.copy(templatePath, targetDir);
|
|
112
|
+
projectCreated = true;
|
|
113
|
+
createSpinner.succeed("Project files created");
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
createSpinner.fail("Failed to create project");
|
|
117
|
+
console.error(err);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
if (answers.usePrisma) {
|
|
121
|
+
const prismaSpinner = ora("Adding Prisma setup...").start();
|
|
122
|
+
try {
|
|
123
|
+
const dbType = answers.database ===
|
|
124
|
+
"postgresql" ? "postgresql"
|
|
125
|
+
: "sqlite";
|
|
126
|
+
const prismaTemplatePath = path.join(templateRoot, `prisma/${dbType}`);
|
|
127
|
+
// Copy prisma schema
|
|
128
|
+
await fs.copy(path.join(prismaTemplatePath, "schema.prisma"), path.join(targetDir, "prisma/schema.prisma"));
|
|
129
|
+
// Copy env
|
|
130
|
+
await fs.copy(path.join(prismaTemplatePath, ".env"), path.join(targetDir, ".env"));
|
|
131
|
+
const pkgPath = path.join(targetDir, "package.json");
|
|
132
|
+
const pkg = await fs.readJson(pkgPath);
|
|
133
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
134
|
+
// Inject dependencies
|
|
135
|
+
pkg.dependencies = {
|
|
136
|
+
...pkg.dependencies,
|
|
137
|
+
"@prisma/client": "5.22.0",
|
|
138
|
+
};
|
|
139
|
+
pkg.devDependencies = {
|
|
140
|
+
...pkg.devDependencies,
|
|
141
|
+
prisma: "5.22.0",
|
|
142
|
+
};
|
|
143
|
+
// Add scripts
|
|
144
|
+
pkg.scripts = {
|
|
145
|
+
...pkg.scripts,
|
|
146
|
+
"prisma:generate": "prisma generate",
|
|
147
|
+
"prisma:migrate": "prisma migrate dev",
|
|
148
|
+
};
|
|
149
|
+
prismaSpinner.succeed(`Prisma (${dbType}) configured`);
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
prismaSpinner.fail("Failed to setup Prisma");
|
|
153
|
+
console.error(err);
|
|
154
|
+
}
|
|
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: "inherit"
|
|
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
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (answers.useGit) {
|
|
197
|
+
const gitSpinner = ora("Initializing git...").start();
|
|
198
|
+
try {
|
|
199
|
+
execSync("git init", {
|
|
200
|
+
cwd: targetDir,
|
|
201
|
+
stdio: "ignore",
|
|
202
|
+
});
|
|
203
|
+
gitSpinner.succeed("Git initialized");
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
gitSpinner.fail("Git init failed");
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
console.log(chalk.green.bold("\n🎉 Authenik8 app created successfully!\n"));
|
|
210
|
+
console.log(chalk.white(`
|
|
211
|
+
Next steps:
|
|
212
|
+
|
|
213
|
+
cd ${projectName}
|
|
214
|
+
cp .env.example .env
|
|
215
|
+
npm run dev
|
|
216
|
+
|
|
217
|
+
🔥 Your Authenik8 server is ready to go!
|
|
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
|
+
`);
|
|
224
|
+
}
|
|
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
|
+
});
|
|
250
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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.0",
|
|
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
|
-
}
|
package/LICENSE
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
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.
|
|
@@ -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
|