create-authenik8-app 2.4.12 → 2.4.13
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 +160 -57
- package/assets/logo.svg +53 -0
- package/dist/src/bin/index.js +199 -125
- package/dist/src/bin/index.js.map +1 -1
- package/dist/src/lib/args.d.ts +18 -0
- package/dist/src/lib/args.d.ts.map +1 -0
- package/dist/src/lib/args.js +82 -0
- package/dist/src/lib/args.js.map +1 -0
- package/dist/src/lib/constants.d.ts +1 -0
- package/dist/src/lib/constants.d.ts.map +1 -1
- package/dist/src/lib/constants.js +10 -11
- package/dist/src/lib/constants.js.map +1 -1
- package/dist/src/lib/preflight.d.ts +3 -0
- package/dist/src/lib/preflight.d.ts.map +1 -0
- package/dist/src/lib/preflight.js +21 -0
- package/dist/src/lib/preflight.js.map +1 -0
- package/dist/src/lib/process.d.ts +6 -2
- package/dist/src/lib/process.d.ts.map +1 -1
- package/dist/src/lib/process.js +66 -8
- package/dist/src/lib/process.js.map +1 -1
- package/dist/src/lib/projectName.d.ts +2 -0
- package/dist/src/lib/projectName.d.ts.map +1 -0
- package/dist/src/lib/projectName.js +6 -0
- package/dist/src/lib/projectName.js.map +1 -0
- package/dist/src/lib/schemas.d.ts +192 -0
- package/dist/src/lib/schemas.d.ts.map +1 -0
- package/dist/src/lib/schemas.js +171 -0
- package/dist/src/lib/schemas.js.map +1 -0
- package/dist/src/lib/state.d.ts.map +1 -1
- package/dist/src/lib/state.js +20 -3
- package/dist/src/lib/state.js.map +1 -1
- package/dist/src/lib/types.d.ts +6 -13
- package/dist/src/lib/types.d.ts.map +1 -1
- package/dist/src/lib/ui.d.ts +8 -3
- package/dist/src/lib/ui.d.ts.map +1 -1
- package/dist/src/lib/ui.js +76 -112
- package/dist/src/lib/ui.js.map +1 -1
- package/dist/src/steps/configurePrisma.d.ts.map +1 -1
- package/dist/src/steps/configurePrisma.js +83 -9
- package/dist/src/steps/configurePrisma.js.map +1 -1
- package/dist/src/steps/createProject.d.ts +2 -1
- package/dist/src/steps/createProject.d.ts.map +1 -1
- package/dist/src/steps/createProject.js +149 -28
- package/dist/src/steps/createProject.js.map +1 -1
- package/dist/src/steps/finalSetup.d.ts +3 -3
- package/dist/src/steps/finalSetup.d.ts.map +1 -1
- package/dist/src/steps/finalSetup.js +24 -28
- package/dist/src/steps/finalSetup.js.map +1 -1
- package/dist/src/steps/installAuth.d.ts +1 -1
- package/dist/src/steps/installAuth.d.ts.map +1 -1
- package/dist/src/steps/installAuth.js +5 -4
- package/dist/src/steps/installAuth.js.map +1 -1
- package/dist/src/steps/installDeps.d.ts +11 -4
- package/dist/src/steps/installDeps.d.ts.map +1 -1
- package/dist/src/steps/installDeps.js +45 -77
- package/dist/src/steps/installDeps.js.map +1 -1
- package/dist/src/steps/prompts.d.ts +1 -1
- package/dist/src/steps/prompts.d.ts.map +1 -1
- package/dist/src/steps/prompts.js +42 -30
- package/dist/src/steps/prompts.js.map +1 -1
- package/dist/src/utils/jwk.d.ts +2 -0
- package/dist/src/utils/jwk.d.ts.map +1 -0
- package/dist/src/utils/jwk.js +44 -0
- package/dist/src/utils/jwk.js.map +1 -0
- package/dist/src/utils/output.d.ts +1 -1
- package/dist/src/utils/output.d.ts.map +1 -1
- package/dist/src/utils/output.js +75 -68
- package/dist/src/utils/output.js.map +1 -1
- package/package.json +17 -9
- package/templates/AGENT_IDENTITY.md +112 -0
- package/templates/THREAT_MODEL.md +27 -5
- package/templates/express-auth/README.md +13 -10
- package/templates/express-auth/docker-compose.yml +10 -0
- package/templates/express-auth/gitignore.template +6 -0
- package/templates/express-auth/package.json +13 -6
- package/templates/express-auth/src/app.ts +2 -0
- package/templates/express-auth/src/controllers/auth.controller.ts +2 -6
- package/templates/express-auth/src/controllers/protected.controller.ts +25 -10
- package/templates/express-auth/src/middleware/auth.middleware.ts +1 -0
- package/templates/express-auth/src/routes/protected.routes.ts +3 -1
- package/templates/express-auth/src/server.ts +6 -12
- package/templates/express-auth/src/utils/security.ts +119 -32
- package/templates/express-auth+/README.md +13 -10
- package/templates/express-auth+/docker-compose.yml +10 -0
- package/templates/express-auth+/gitignore.template +6 -0
- package/templates/express-auth+/package.json +12 -5
- package/templates/express-auth+/src/auth/auth.ts +3 -2
- package/templates/express-auth+/src/auth/controllers/password.controller.ts +3 -7
- package/templates/express-auth+/src/auth/controllers/protected.controller.ts +26 -11
- package/templates/express-auth+/src/auth/identity.adapter.ts +90 -0
- package/templates/express-auth+/src/auth/middleware/auth.middleware.ts +3 -4
- package/templates/express-auth+/src/auth/routes/protected.routes.ts +2 -2
- package/templates/express-auth+/src/oauth-providers/github/src/auth/auth.ts +3 -2
- package/templates/express-auth+/src/oauth-providers/google/src/auth/auth.ts +3 -2
- package/templates/express-auth+/src/oauth-providers/google-github/src/auth/auth.ts +3 -2
- package/templates/express-auth+/src/server.ts +5 -10
- package/templates/express-auth+/src/utils/security.ts +109 -28
- package/templates/express-base/README.md +13 -6
- package/templates/express-base/app.ts +2 -0
- package/templates/express-base/controllers/base.controller.ts +33 -19
- package/templates/express-base/docker-compose.yml +10 -0
- package/templates/express-base/gitignore.template +6 -0
- package/templates/express-base/package.json +11 -5
- package/templates/express-base/routes/base.routes.ts +1 -1
- package/templates/express-base/src/.env.example +7 -2
- package/templates/express-base/src/server.ts +6 -12
- package/templates/express-base/tsconfig.json +7 -40
- package/templates/express-base/utils/security.ts +111 -15
- package/templates/fullstack/.env.example +24 -0
- package/templates/fullstack/PRESET_CONTRACT.md +46 -0
- package/templates/fullstack/README.md +65 -0
- package/templates/fullstack/THREAT_MODEL.md +30 -0
- package/templates/fullstack/apps/api/package.json +48 -0
- package/templates/fullstack/apps/api/prisma/schema.prisma +124 -0
- package/templates/fullstack/apps/api/prisma/seed.ts +36 -0
- package/templates/fullstack/apps/api/prisma.config.ts +18 -0
- package/templates/fullstack/apps/api/src/app.ts +68 -0
- package/templates/fullstack/apps/api/src/auth/auth.controller.ts +125 -0
- package/templates/fullstack/apps/api/src/auth/auth.routes.ts +50 -0
- package/templates/fullstack/apps/api/src/auth/auth.service.ts +257 -0
- package/templates/fullstack/apps/api/src/auth/authenik8.ts +67 -0
- package/templates/fullstack/apps/api/src/auth/cookies.ts +36 -0
- package/templates/fullstack/apps/api/src/config/env.ts +72 -0
- package/templates/fullstack/apps/api/src/config/logger.ts +17 -0
- package/templates/fullstack/apps/api/src/config/mailer.ts +32 -0
- package/templates/fullstack/apps/api/src/config/origins.ts +27 -0
- package/templates/fullstack/apps/api/src/config/prisma.ts +9 -0
- package/templates/fullstack/apps/api/src/middleware/authenticate.ts +35 -0
- package/templates/fullstack/apps/api/src/middleware/csrf.ts +60 -0
- package/templates/fullstack/apps/api/src/middleware/origin.ts +12 -0
- package/templates/fullstack/apps/api/src/middleware/request-id.ts +9 -0
- package/templates/fullstack/apps/api/src/modules/admin/admin.controller.ts +17 -0
- package/templates/fullstack/apps/api/src/modules/admin/admin.routes.ts +14 -0
- package/templates/fullstack/apps/api/src/modules/admin/admin.service.ts +54 -0
- package/templates/fullstack/apps/api/src/modules/projects/project.controller.ts +22 -0
- package/templates/fullstack/apps/api/src/modules/projects/project.policy.ts +14 -0
- package/templates/fullstack/apps/api/src/modules/projects/project.repository.ts +20 -0
- package/templates/fullstack/apps/api/src/modules/projects/project.routes.ts +21 -0
- package/templates/fullstack/apps/api/src/modules/projects/project.service.ts +41 -0
- package/templates/fullstack/apps/api/src/modules/users/user.controller.ts +26 -0
- package/templates/fullstack/apps/api/src/modules/users/user.presenter.ts +13 -0
- package/templates/fullstack/apps/api/src/modules/users/user.routes.ts +21 -0
- package/templates/fullstack/apps/api/src/modules/users/user.service.ts +71 -0
- package/templates/fullstack/apps/api/src/openapi.ts +32 -0
- package/templates/fullstack/apps/api/src/server.ts +29 -0
- package/templates/fullstack/apps/api/src/types.ts +15 -0
- package/templates/fullstack/apps/api/src/utils/crypto.ts +4 -0
- package/templates/fullstack/apps/api/src/utils/http.ts +33 -0
- package/templates/fullstack/apps/api/src/utils/sealed-value.ts +37 -0
- package/templates/fullstack/apps/api/tests/project.policy.test.ts +26 -0
- package/templates/fullstack/apps/api/tests/registration.test.ts +69 -0
- package/templates/fullstack/apps/api/tests/security.test.ts +98 -0
- package/templates/fullstack/apps/api/tests/validation.test.ts +37 -0
- package/templates/fullstack/apps/api/tsconfig.json +16 -0
- package/templates/fullstack/apps/api/vitest.config.ts +9 -0
- package/templates/fullstack/apps/web/index.html +14 -0
- package/templates/fullstack/apps/web/package.json +31 -0
- package/templates/fullstack/apps/web/public/authenik8-logo.svg +53 -0
- package/templates/fullstack/apps/web/src/App.tsx +45 -0
- package/templates/fullstack/apps/web/src/auth/AuthProvider.tsx +53 -0
- package/templates/fullstack/apps/web/src/auth/RouteGuards.tsx +16 -0
- package/templates/fullstack/apps/web/src/auth/providers.ts +3 -0
- package/templates/fullstack/apps/web/src/components/AppShell.tsx +80 -0
- package/templates/fullstack/apps/web/src/components/AuthShell.tsx +26 -0
- package/templates/fullstack/apps/web/src/components/Page.tsx +30 -0
- package/templates/fullstack/apps/web/src/features/projects/ProjectPages.tsx +79 -0
- package/templates/fullstack/apps/web/src/main.tsx +24 -0
- package/templates/fullstack/apps/web/src/pages/DashboardPage.tsx +48 -0
- package/templates/fullstack/apps/web/src/pages/StatusPages.tsx +8 -0
- package/templates/fullstack/apps/web/src/pages/admin/AuditPage.tsx +19 -0
- package/templates/fullstack/apps/web/src/pages/admin/UsersPage.tsx +24 -0
- package/templates/fullstack/apps/web/src/pages/auth/LoginPage.tsx +51 -0
- package/templates/fullstack/apps/web/src/pages/auth/OAuthCallbackPage.tsx +26 -0
- package/templates/fullstack/apps/web/src/pages/auth/RecoveryPages.tsx +53 -0
- package/templates/fullstack/apps/web/src/pages/auth/RegisterPage.tsx +86 -0
- package/templates/fullstack/apps/web/src/pages/auth/VerifyEmailPage.tsx +24 -0
- package/templates/fullstack/apps/web/src/pages/settings/ProfilePage.tsx +34 -0
- package/templates/fullstack/apps/web/src/pages/settings/SecurityPage.tsx +57 -0
- package/templates/fullstack/apps/web/src/styles.css +289 -0
- package/templates/fullstack/apps/web/src/token-storage.test.ts +12 -0
- package/templates/fullstack/apps/web/tsconfig.app.json +19 -0
- package/templates/fullstack/apps/web/tsconfig.json +7 -0
- package/templates/fullstack/apps/web/tsconfig.node.json +10 -0
- package/templates/fullstack/apps/web/vite.config.ts +18 -0
- package/templates/fullstack/docker-compose.yml +35 -0
- package/templates/fullstack/docs/PRODUCTION.md +30 -0
- package/templates/fullstack/gitignore.template +8 -0
- package/templates/fullstack/package.json +39 -0
- package/templates/fullstack/packages/api-client/package.json +20 -0
- package/templates/fullstack/packages/api-client/src/index.ts +203 -0
- package/templates/fullstack/packages/api-client/tsconfig.json +14 -0
- package/templates/fullstack/packages/contracts/package.json +20 -0
- package/templates/fullstack/packages/contracts/src/index.ts +146 -0
- package/templates/fullstack/packages/contracts/tsconfig.json +13 -0
- package/templates/fullstack/packages/ui/package.json +22 -0
- package/templates/fullstack/packages/ui/src/index.tsx +33 -0
- package/templates/fullstack/packages/ui/tsconfig.json +14 -0
- package/templates/prisma/postgresql/.env.example +6 -2
- package/templates/prisma/postgresql/client.ts +17 -0
- package/templates/prisma/postgresql/prisma.config.ts +12 -0
- package/templates/prisma/postgresql/schema.prisma +0 -1
- package/templates/prisma/sqlite/.env.example +6 -2
- package/templates/prisma/sqlite/client.ts +16 -0
- package/templates/prisma/sqlite/prisma.config.ts +12 -0
- package/templates/prisma/sqlite/schema.prisma +0 -1
- package/dist/src/tests/templated-routes.test.d.ts +0 -2
- package/dist/src/tests/templated-routes.test.d.ts.map +0 -1
- package/dist/src/tests/templated-routes.test.js +0 -325
- package/dist/src/tests/templated-routes.test.js.map +0 -1
- package/dist/templates/THREAT_MODEL.md +0 -138
- package/dist/templates/express-auth/src/controllers/protected.controller.d.ts +0 -8
- package/dist/templates/express-auth/src/controllers/protected.controller.d.ts.map +0 -1
- package/dist/templates/express-auth/src/controllers/protected.controller.js +0 -50
- package/dist/templates/express-auth/src/controllers/protected.controller.js.map +0 -1
- package/dist/templates/express-auth/src/routes/protected.routes.d.ts +0 -2
- package/dist/templates/express-auth/src/routes/protected.routes.d.ts.map +0 -1
- package/dist/templates/express-auth/src/routes/protected.routes.js +0 -16
- package/dist/templates/express-auth/src/routes/protected.routes.js.map +0 -1
- package/dist/templates/express-auth/src/utils/security.d.ts +0 -8
- package/dist/templates/express-auth/src/utils/security.d.ts.map +0 -1
- package/dist/templates/express-auth/src/utils/security.js +0 -55
- package/dist/templates/express-auth/src/utils/security.js.map +0 -1
- package/dist/templates/express-base/controllers/base.controller.d.ts +0 -12
- package/dist/templates/express-base/controllers/base.controller.d.ts.map +0 -1
- package/dist/templates/express-base/controllers/base.controller.js +0 -77
- package/dist/templates/express-base/controllers/base.controller.js.map +0 -1
- package/dist/templates/express-base/routes/base.routes.d.ts +0 -2
- package/dist/templates/express-base/routes/base.routes.d.ts.map +0 -1
- package/dist/templates/express-base/routes/base.routes.js +0 -20
- package/dist/templates/express-base/routes/base.routes.js.map +0 -1
- package/dist/templates/express-base/utils/security.d.ts +0 -5
- package/dist/templates/express-base/utils/security.d.ts.map +0 -1
- package/dist/templates/express-base/utils/security.js +0 -40
- package/dist/templates/express-base/utils/security.js.map +0 -1
- package/dist/templates/package.json +0 -27
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
import express from "express";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
|
-
import request from "supertest";
|
|
4
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
const authenik8CoreMock = vi.hoisted(() => ({
|
|
6
|
-
createAuthenik8: vi.fn(),
|
|
7
|
-
}));
|
|
8
|
-
const dotenvMock = vi.hoisted(() => ({
|
|
9
|
-
config: vi.fn(),
|
|
10
|
-
}));
|
|
11
|
-
vi.mock("authenik8-core", () => ({
|
|
12
|
-
createAuthenik8: authenik8CoreMock.createAuthenik8,
|
|
13
|
-
}));
|
|
14
|
-
vi.mock("dotenv", () => ({
|
|
15
|
-
default: dotenvMock,
|
|
16
|
-
config: dotenvMock.config,
|
|
17
|
-
}));
|
|
18
|
-
const require = createRequire(import.meta.url);
|
|
19
|
-
const jwt = require("jsonwebtoken");
|
|
20
|
-
const jwtSecret = "templated-route-test-secret";
|
|
21
|
-
class InMemoryRedis {
|
|
22
|
-
hashes = new Map();
|
|
23
|
-
async hset(key, field, value) {
|
|
24
|
-
const hash = this.hashes.get(key) ?? new Map();
|
|
25
|
-
hash.set(field, value);
|
|
26
|
-
this.hashes.set(key, hash);
|
|
27
|
-
return 1;
|
|
28
|
-
}
|
|
29
|
-
async hgetall(key) {
|
|
30
|
-
return Object.fromEntries(this.hashes.get(key) ?? []);
|
|
31
|
-
}
|
|
32
|
-
async hdel(key, field) {
|
|
33
|
-
const hash = this.hashes.get(key);
|
|
34
|
-
if (!hash)
|
|
35
|
-
return 0;
|
|
36
|
-
return hash.delete(field) ? 1 : 0;
|
|
37
|
-
}
|
|
38
|
-
async del(key) {
|
|
39
|
-
return this.hashes.delete(key) ? 1 : 0;
|
|
40
|
-
}
|
|
41
|
-
async expire(_key, _ttl) {
|
|
42
|
-
return 1;
|
|
43
|
-
}
|
|
44
|
-
clear() {
|
|
45
|
-
this.hashes.clear();
|
|
46
|
-
}
|
|
47
|
-
async seedSession(userId, session) {
|
|
48
|
-
const storedSession = {
|
|
49
|
-
token: session.token ?? `stored-token-${session.sessionId}`,
|
|
50
|
-
device: session.device ?? "Chrome on Linux",
|
|
51
|
-
ip: session.ip ?? "127.0.0.1",
|
|
52
|
-
sessionId: session.sessionId,
|
|
53
|
-
createdAt: session.createdAt ?? "2026-05-02T00:00:00.000Z",
|
|
54
|
-
};
|
|
55
|
-
await this.hset(`sessions:${userId}`, session.sessionId, JSON.stringify(storedSession));
|
|
56
|
-
await this.expire(`sessions:${userId}`, 60 * 60);
|
|
57
|
-
return storedSession;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const redis = new InMemoryRedis();
|
|
61
|
-
let redisForAuthFactory;
|
|
62
|
-
const signTestToken = (payload) => jwt.sign(payload, jwtSecret);
|
|
63
|
-
const adminToken = () => signTestToken({ id: "admin-user", role: "admin" });
|
|
64
|
-
const userToken = () => signTestToken({ id: "regular-user", role: "user" });
|
|
65
|
-
const getBearerToken = (authorization) => {
|
|
66
|
-
if (typeof authorization !== "string")
|
|
67
|
-
return undefined;
|
|
68
|
-
const [scheme, token] = authorization.split(" ");
|
|
69
|
-
return scheme === "Bearer" ? token : undefined;
|
|
70
|
-
};
|
|
71
|
-
const createAdminActions = (sessionStore) => ({
|
|
72
|
-
async listSessions(userId) {
|
|
73
|
-
const rawSessions = await sessionStore.hgetall(`sessions:${userId}`);
|
|
74
|
-
return Object.values(rawSessions).map((serializedSession) => JSON.parse(serializedSession));
|
|
75
|
-
},
|
|
76
|
-
async revokeSession(userId, sessionId) {
|
|
77
|
-
await sessionStore.hdel(`sessions:${userId}`, sessionId);
|
|
78
|
-
},
|
|
79
|
-
async revokeAllSessions(userId) {
|
|
80
|
-
await sessionStore.del(`sessions:${userId}`);
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
const createMockAuth = (sessionStore) => ({
|
|
84
|
-
requireAdmin(req, res, next) {
|
|
85
|
-
const token = getBearerToken(req.headers.authorization);
|
|
86
|
-
if (!token) {
|
|
87
|
-
return res.status(401).json({ error: "Unauthorized" });
|
|
88
|
-
}
|
|
89
|
-
try {
|
|
90
|
-
const decoded = jwt.verify(token, jwtSecret);
|
|
91
|
-
if (decoded.role !== "admin") {
|
|
92
|
-
return res.status(403).json({ error: "Forbidden" });
|
|
93
|
-
}
|
|
94
|
-
if (sessionStore) {
|
|
95
|
-
req.adminActions =
|
|
96
|
-
createAdminActions(sessionStore);
|
|
97
|
-
}
|
|
98
|
-
next();
|
|
99
|
-
}
|
|
100
|
-
catch {
|
|
101
|
-
res.status(403).json({ error: "Forbidden" });
|
|
102
|
-
}
|
|
103
|
-
},
|
|
104
|
-
authenticateJWT(req, res, next) {
|
|
105
|
-
const token = getBearerToken(req.headers.authorization);
|
|
106
|
-
if (!token) {
|
|
107
|
-
return res.status(401).json({ error: "Unauthorized" });
|
|
108
|
-
}
|
|
109
|
-
try {
|
|
110
|
-
jwt.verify(token, jwtSecret);
|
|
111
|
-
next();
|
|
112
|
-
}
|
|
113
|
-
catch {
|
|
114
|
-
res.status(403).json({ error: "Forbidden" });
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
signToken: (payload) => jwt.sign(payload, jwtSecret),
|
|
118
|
-
verifyToken: async (token) => jwt.verify(token, jwtSecret),
|
|
119
|
-
guestToken: async (payload) => jwt.sign(payload, jwtSecret),
|
|
120
|
-
refreshToken: async () => ({
|
|
121
|
-
accessToken: jwt.sign({ id: "refreshed-user", role: "user" }, jwtSecret),
|
|
122
|
-
refreshToken: "next-refresh-token",
|
|
123
|
-
}),
|
|
124
|
-
incognito: vi.fn(),
|
|
125
|
-
rateLimit: (_req, _res, next) => next(),
|
|
126
|
-
ipWhitelist: (_req, _res, next) => next(),
|
|
127
|
-
helmet: (_req, _res, next) => next(),
|
|
128
|
-
});
|
|
129
|
-
const appWithRouter = (router) => {
|
|
130
|
-
const app = express();
|
|
131
|
-
app.use(express.json());
|
|
132
|
-
app.use(router);
|
|
133
|
-
return app;
|
|
134
|
-
};
|
|
135
|
-
const send = (app, method, path) => {
|
|
136
|
-
if (method === "delete") {
|
|
137
|
-
return request(app).delete(path);
|
|
138
|
-
}
|
|
139
|
-
return request(app).get(path);
|
|
140
|
-
};
|
|
141
|
-
const routeTemplates = [
|
|
142
|
-
{
|
|
143
|
-
name: "templates/express-auth/src/routes/protected.routes.ts",
|
|
144
|
-
guardedRoute: {
|
|
145
|
-
method: "get",
|
|
146
|
-
path: "/protected",
|
|
147
|
-
expectedBody: { message: "Protected route" },
|
|
148
|
-
},
|
|
149
|
-
async createApp(sessionStore) {
|
|
150
|
-
const { createProtectedRoutes } = await import("../../templates/express-auth/src/routes/protected.routes.js");
|
|
151
|
-
return appWithRouter(createProtectedRoutes(createMockAuth(sessionStore)));
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
{
|
|
155
|
-
name: "templates/express-base/routes/base.routes.ts",
|
|
156
|
-
guardedRoute: {
|
|
157
|
-
method: "get",
|
|
158
|
-
path: "/admin",
|
|
159
|
-
expectedBody: { message: "Admin only" },
|
|
160
|
-
},
|
|
161
|
-
async createApp(sessionStore) {
|
|
162
|
-
const { createBaseRoutes } = await import("../../templates/express-base/routes/base.routes.js");
|
|
163
|
-
return appWithRouter(createBaseRoutes(createMockAuth(sessionStore)));
|
|
164
|
-
},
|
|
165
|
-
},
|
|
166
|
-
//{
|
|
167
|
-
//name: "templates/express-auth+/src/auth/routes/protected.routes.ts",
|
|
168
|
-
// guardedRoute: {
|
|
169
|
-
// method: "get",
|
|
170
|
-
//path: "/protected",
|
|
171
|
-
//expectedBody: { message: "Protected route" },
|
|
172
|
-
//},
|
|
173
|
-
//async createApp(sessionStore?: InMemoryRedis) {
|
|
174
|
-
//redisForAuthFactory = sessionStore;
|
|
175
|
-
//const [ { default: protectedRoutes }] = await Promise.all([
|
|
176
|
-
//import("../../templates/express-auth+/src/auth/auth.js"),
|
|
177
|
-
//import("../../templates/express-auth+/src/auth/routes/protected.routes.js"),
|
|
178
|
-
//]);
|
|
179
|
-
//const authModule = await import("../../templates/express-auth+/src/auth/auth.js");
|
|
180
|
-
//const routesModule = await import("../../templates/express-auth+/src/auth/routes/protected.routes.js");
|
|
181
|
-
//await authModule.initAuth();
|
|
182
|
-
// await initAuth();
|
|
183
|
-
// /const protectedRoutes = routesModule.default as unknown as Router ;
|
|
184
|
-
// return appWithRouter(protectedRoutes);
|
|
185
|
-
//},
|
|
186
|
-
//},
|
|
187
|
-
];
|
|
188
|
-
const sessionRoutes = [
|
|
189
|
-
{
|
|
190
|
-
name: "list sessions",
|
|
191
|
-
method: "get",
|
|
192
|
-
path: "/admin/sessions/user-a",
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
name: "revoke one session",
|
|
196
|
-
method: "delete",
|
|
197
|
-
path: "/admin/sessions/user-a/session-a",
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
name: "revoke all sessions",
|
|
201
|
-
method: "delete",
|
|
202
|
-
path: "/admin/sessions/user-a",
|
|
203
|
-
},
|
|
204
|
-
];
|
|
205
|
-
beforeEach(() => {
|
|
206
|
-
vi.resetModules();
|
|
207
|
-
vi.stubEnv("JWT_SECRET", "templated-route-test-jwt-secret-32");
|
|
208
|
-
vi.stubEnv("REFRESH_SECRET", "templated-route-test-refresh-secret-32");
|
|
209
|
-
vi.stubEnv("GOOGLE_CLIENT_ID", "google-client-id");
|
|
210
|
-
vi.stubEnv("GOOGLE_CLIENT_SECRET", "google-client-secret");
|
|
211
|
-
vi.stubEnv("GOOGLE_REDIRECT_URI", "https://example.com/auth/google/callback");
|
|
212
|
-
vi.stubEnv("GITHUB_CLIENT_ID", "github-client-id");
|
|
213
|
-
vi.stubEnv("GITHUB_CLIENT_SECRET", "github-client-secret");
|
|
214
|
-
vi.stubEnv("GITHUB_REDIRECT_URI", "https://example.com/auth/github/callback");
|
|
215
|
-
redis.clear();
|
|
216
|
-
redisForAuthFactory = undefined;
|
|
217
|
-
dotenvMock.config.mockReset();
|
|
218
|
-
authenik8CoreMock.createAuthenik8.mockReset();
|
|
219
|
-
authenik8CoreMock.createAuthenik8.mockImplementation(async () => createMockAuth(redisForAuthFactory));
|
|
220
|
-
});
|
|
221
|
-
describe.each(routeTemplates)("$name", (template) => {
|
|
222
|
-
describe("admin guard", () => {
|
|
223
|
-
const guardedRoutes = [template.guardedRoute, ...sessionRoutes];
|
|
224
|
-
it.each(guardedRoutes)("returns 401 with no token for $method $path", async (route) => {
|
|
225
|
-
const app = await template.createApp(redis);
|
|
226
|
-
const response = await send(app, route.method, route.path);
|
|
227
|
-
expect(response.status).toBe(401);
|
|
228
|
-
});
|
|
229
|
-
it.each(guardedRoutes)("returns 403 for non-admin users on $method $path", async (route) => {
|
|
230
|
-
const app = await template.createApp(redis);
|
|
231
|
-
const response = await send(app, route.method, route.path).set("Authorization", `Bearer ${userToken()}`);
|
|
232
|
-
expect(response.status).toBe(403);
|
|
233
|
-
});
|
|
234
|
-
it.each(guardedRoutes)("returns 403 for unauthenticated invalid tokens on $method $path", async (route) => {
|
|
235
|
-
const app = await template.createApp(redis);
|
|
236
|
-
const response = await send(app, route.method, route.path).set("Authorization", "Bearer not-a-valid-token");
|
|
237
|
-
expect(response.status).toBe(403);
|
|
238
|
-
});
|
|
239
|
-
it(`returns the happy path response for ${template.guardedRoute.method} ${template.guardedRoute.path}`, async () => {
|
|
240
|
-
const app = await template.createApp(redis);
|
|
241
|
-
const response = await send(app, template.guardedRoute.method, template.guardedRoute.path).set("Authorization", `Bearer ${adminToken()}`);
|
|
242
|
-
expect(response.status).toBe(200);
|
|
243
|
-
expect(response.body).toEqual(template.guardedRoute.expectedBody);
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
describe("session management", () => {
|
|
247
|
-
it.each(sessionRoutes)("returns 503 for $method $path when Redis is not configured", async (route) => {
|
|
248
|
-
const app = await template.createApp(undefined);
|
|
249
|
-
const response = await send(app, route.method, route.path).set("Authorization", `Bearer ${adminToken()}`);
|
|
250
|
-
expect(response.status).toBe(503);
|
|
251
|
-
expect(response.body).toEqual({
|
|
252
|
-
success: false,
|
|
253
|
-
message: "Session management unavailable",
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
it("lists sessions without exposing stored tokens", async () => {
|
|
257
|
-
const app = await template.createApp(redis);
|
|
258
|
-
await redis.seedSession("user-a", {
|
|
259
|
-
sessionId: "session-a",
|
|
260
|
-
token: "sensitive-token-a",
|
|
261
|
-
device: "Firefox on macOS",
|
|
262
|
-
ip: "10.0.0.1",
|
|
263
|
-
});
|
|
264
|
-
await redis.seedSession("user-a", {
|
|
265
|
-
sessionId: "session-b",
|
|
266
|
-
token: "sensitive-token-b",
|
|
267
|
-
device: "Safari on iOS",
|
|
268
|
-
ip: "10.0.0.2",
|
|
269
|
-
});
|
|
270
|
-
const response = await request(app)
|
|
271
|
-
.get("/admin/sessions/user-a")
|
|
272
|
-
.set("Authorization", `Bearer ${adminToken()}`);
|
|
273
|
-
expect(response.status).toBe(200);
|
|
274
|
-
expect(response.body.sessions).toEqual([
|
|
275
|
-
{
|
|
276
|
-
device: "Firefox on macOS",
|
|
277
|
-
ip: "10.0.0.1",
|
|
278
|
-
sessionId: "session-a",
|
|
279
|
-
createdAt: "2026-05-02T00:00:00.000Z",
|
|
280
|
-
},
|
|
281
|
-
{
|
|
282
|
-
device: "Safari on iOS",
|
|
283
|
-
ip: "10.0.0.2",
|
|
284
|
-
sessionId: "session-b",
|
|
285
|
-
createdAt: "2026-05-02T00:00:00.000Z",
|
|
286
|
-
},
|
|
287
|
-
]);
|
|
288
|
-
expect(JSON.stringify(response.body)).not.toContain("sensitive-token");
|
|
289
|
-
expect(response.body.sessions).toEqual(expect.arrayContaining([expect.not.objectContaining({ token: expect.anything() })]));
|
|
290
|
-
});
|
|
291
|
-
it("revokes one session without affecting other sessions or users", async () => {
|
|
292
|
-
const app = await template.createApp(redis);
|
|
293
|
-
await redis.seedSession("user-a", { sessionId: "session-a", token: "user-a-token-a" });
|
|
294
|
-
await redis.seedSession("user-a", { sessionId: "session-b", token: "user-a-token-b" });
|
|
295
|
-
await redis.seedSession("user-b", { sessionId: "session-c", token: "user-b-token-c" });
|
|
296
|
-
const response = await request(app)
|
|
297
|
-
.delete("/admin/sessions/user-a/session-a")
|
|
298
|
-
.set("Authorization", `Bearer ${adminToken()}`);
|
|
299
|
-
expect(response.status).toBe(200);
|
|
300
|
-
expect(response.body).toEqual({ success: true, message: "Session revoked" });
|
|
301
|
-
expect(await redis.hgetall("sessions:user-a")).toEqual({
|
|
302
|
-
"session-b": expect.any(String),
|
|
303
|
-
});
|
|
304
|
-
expect(await redis.hgetall("sessions:user-b")).toEqual({
|
|
305
|
-
"session-c": expect.any(String),
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
it("revokes all sessions for only the targeted user", async () => {
|
|
309
|
-
const app = await template.createApp(redis);
|
|
310
|
-
await redis.seedSession("user-a", { sessionId: "session-a", token: "user-a-token-a" });
|
|
311
|
-
await redis.seedSession("user-a", { sessionId: "session-b", token: "user-a-token-b" });
|
|
312
|
-
await redis.seedSession("user-b", { sessionId: "session-c", token: "user-b-token-c" });
|
|
313
|
-
const response = await request(app)
|
|
314
|
-
.delete("/admin/sessions/user-a")
|
|
315
|
-
.set("Authorization", `Bearer ${adminToken()}`);
|
|
316
|
-
expect(response.status).toBe(200);
|
|
317
|
-
expect(response.body).toEqual({ success: true, message: "All sessions revoked" });
|
|
318
|
-
expect(await redis.hgetall("sessions:user-a")).toEqual({});
|
|
319
|
-
expect(await redis.hgetall("sessions:user-b")).toEqual({
|
|
320
|
-
"session-c": expect.any(String),
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
});
|
|
325
|
-
//# sourceMappingURL=templated-routes.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"templated-routes.test.js","sourceRoot":"","sources":["../../../src/tests/templated-routes.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,138 +0,0 @@
|
|
|
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.
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Request, Response } from "express";
|
|
2
|
-
export declare const createProtectedController: () => {
|
|
3
|
-
protected(req: Request, res: Response): void;
|
|
4
|
-
listSessions(req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
|
|
5
|
-
revokeSession(req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
|
|
6
|
-
revokeAllSessions(req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
|
|
7
|
-
};
|
|
8
|
-
//# sourceMappingURL=protected.controller.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protected.controller.d.ts","sourceRoot":"","sources":["../../../../../templates/express-auth/src/controllers/protected.controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG5C,eAAO,MAAM,yBAAyB;mBACrB,OAAO,OAAO,QAAQ;sBAIb,OAAO,OAAO,QAAQ;uBAcrB,OAAO,OAAO,QAAQ;2BAclB,OAAO,OAAO,QAAQ;CAanD,CAAC"}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createProtectedController = void 0;
|
|
4
|
-
const security_1 = require("../utils/security");
|
|
5
|
-
const createProtectedController = () => ({
|
|
6
|
-
protected(req, res) {
|
|
7
|
-
res.json({ message: "Protected route" });
|
|
8
|
-
},
|
|
9
|
-
async listSessions(req, res) {
|
|
10
|
-
try {
|
|
11
|
-
const actions = req.adminActions;
|
|
12
|
-
if (!actions) {
|
|
13
|
-
return res.status(503).json({ success: false, message: "Session management unavailable" });
|
|
14
|
-
}
|
|
15
|
-
const sessions = await actions.listSessions(req.params.userId);
|
|
16
|
-
res.json({ sessions: (0, security_1.sanitizeSessionResponse)(sessions) });
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
res.status(500).json({ success: false, message: "Failed to retrieve sessions" });
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
async revokeSession(req, res) {
|
|
23
|
-
try {
|
|
24
|
-
const actions = req.adminActions;
|
|
25
|
-
if (!actions) {
|
|
26
|
-
return res.status(503).json({ success: false, message: "Session management unavailable" });
|
|
27
|
-
}
|
|
28
|
-
await actions.revokeSession(req.params.userId, req.params.sessionId);
|
|
29
|
-
res.json({ success: true, message: "Session revoked" });
|
|
30
|
-
}
|
|
31
|
-
catch {
|
|
32
|
-
res.status(500).json({ success: false, message: "Failed to revoke session" });
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
async revokeAllSessions(req, res) {
|
|
36
|
-
try {
|
|
37
|
-
const actions = req.adminActions;
|
|
38
|
-
if (!actions) {
|
|
39
|
-
return res.status(503).json({ success: false, message: "Session management unavailable" });
|
|
40
|
-
}
|
|
41
|
-
await actions.revokeAllSessions(req.params.userId);
|
|
42
|
-
res.json({ success: true, message: "All sessions revoked" });
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
res.status(500).json({ success: false, message: "Failed to revoke sessions" });
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
exports.createProtectedController = createProtectedController;
|
|
50
|
-
//# sourceMappingURL=protected.controller.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protected.controller.js","sourceRoot":"","sources":["../../../../../templates/express-auth/src/controllers/protected.controller.ts"],"names":[],"mappings":";;;AACA,gDAA4D;AAErD,MAAM,yBAAyB,GAAG,GAAG,EAAE,CAAC,CAAC;IAC9C,SAAS,CAAC,GAAY,EAAE,GAAa;QACnC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,GAAY,EAAE,GAAa;QAC5C,IAAI,CAAC;YACH,MAAM,OAAO,GAAI,GAAW,CAAC,YAAY,CAAC;YAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,gCAAgC,EAAE,CAAC,CAAC;YAC7F,CAAC;YAED,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC/D,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAA,kCAAuB,EAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC5D,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,6BAA6B,EAAE,CAAC,CAAC;QACnF,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,GAAY,EAAE,GAAa;QAC7C,IAAI,CAAC;YACH,MAAM,OAAO,GAAI,GAAW,CAAC,YAAY,CAAC;YAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,gCAAgC,EAAE,CAAC,CAAC;YAC7F,CAAC;YAED,MAAM,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACrE,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC,CAAC;QAC1D,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC,CAAC;QAChF,CAAC;IACH,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,GAAY,EAAE,GAAa;QACjD,IAAI,CAAC;YACH,MAAM,OAAO,GAAI,GAAW,CAAC,YAAY,CAAC;YAC1C,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,gCAAgC,EAAE,CAAC,CAAC;YAC7F,CAAC;YAED,MAAM,OAAO,CAAC,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACnD,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAC;QAC/D,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,2BAA2B,EAAE,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;CACF,CAAC,CAAC;AA9CU,QAAA,yBAAyB,6BA8CnC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protected.routes.d.ts","sourceRoot":"","sources":["../../../../../templates/express-auth/src/routes/protected.routes.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,qBAAqB,GAAI,MAAM,GAAG,+CAU9C,CAAC"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createProtectedRoutes = void 0;
|
|
4
|
-
const express_1 = require("express");
|
|
5
|
-
const protected_controller_1 = require("../controllers/protected.controller");
|
|
6
|
-
const createProtectedRoutes = (auth) => {
|
|
7
|
-
const router = (0, express_1.Router)();
|
|
8
|
-
const controller = (0, protected_controller_1.createProtectedController)();
|
|
9
|
-
router.get("/protected", auth.requireAdmin, controller.protected);
|
|
10
|
-
router.get("/admin/sessions/:userId", auth.requireAdmin, controller.listSessions);
|
|
11
|
-
router.delete("/admin/sessions/:userId/:sessionId", auth.requireAdmin, controller.revokeSession);
|
|
12
|
-
router.delete("/admin/sessions/:userId", auth.requireAdmin, controller.revokeAllSessions);
|
|
13
|
-
return router;
|
|
14
|
-
};
|
|
15
|
-
exports.createProtectedRoutes = createProtectedRoutes;
|
|
16
|
-
//# sourceMappingURL=protected.routes.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protected.routes.js","sourceRoot":"","sources":["../../../../../templates/express-auth/src/routes/protected.routes.ts"],"names":[],"mappings":";;;AAAA,qCAAiC;AACjC,8EAAgF;AAEzE,MAAM,qBAAqB,GAAG,CAAC,IAAS,EAAE,EAAE;IACjD,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;IACxB,MAAM,UAAU,GAAG,IAAA,gDAAyB,GAAE,CAAC;IAE/C,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;IAClE,MAAM,CAAC,GAAG,CAAC,yBAAyB,EAAE,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC;IAClF,MAAM,CAAC,MAAM,CAAC,oCAAoC,EAAE,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,aAAa,CAAC,CAAC;IACjG,MAAM,CAAC,MAAM,CAAC,yBAAyB,EAAE,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAE1F,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAVW,QAAA,qBAAqB,yBAUhC"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export declare function requiredSecret(name: string): string;
|
|
2
|
-
export declare function parseCredentials(body: unknown): {
|
|
3
|
-
email: string;
|
|
4
|
-
password: string;
|
|
5
|
-
};
|
|
6
|
-
export declare function parseRefreshToken(body: unknown): string;
|
|
7
|
-
export declare function sanitizeSessionResponse(value: unknown): unknown;
|
|
8
|
-
//# sourceMappingURL=security.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../../../../templates/express-auth/src/utils/security.ts"],"names":[],"mappings":"AAEA,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQnD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO;;;EAyB7C;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,UAY9C;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAc/D"}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.requiredSecret = requiredSecret;
|
|
4
|
-
exports.parseCredentials = parseCredentials;
|
|
5
|
-
exports.parseRefreshToken = parseRefreshToken;
|
|
6
|
-
exports.sanitizeSessionResponse = sanitizeSessionResponse;
|
|
7
|
-
const sensitiveKeys = new Set(["token", "accessToken", "refreshToken"]);
|
|
8
|
-
function requiredSecret(name) {
|
|
9
|
-
const value = process.env[name];
|
|
10
|
-
if (typeof value !== "string" || value.trim().length < 32) {
|
|
11
|
-
throw new Error(`${name} must be set to at least 32 characters`);
|
|
12
|
-
}
|
|
13
|
-
return value;
|
|
14
|
-
}
|
|
15
|
-
function parseCredentials(body) {
|
|
16
|
-
if (!body || typeof body !== "object") {
|
|
17
|
-
throw new Error("Email and password are required");
|
|
18
|
-
}
|
|
19
|
-
const { email, password } = body;
|
|
20
|
-
if (typeof email !== "string" || typeof password !== "string") {
|
|
21
|
-
throw new Error("Email and password are required");
|
|
22
|
-
}
|
|
23
|
-
const normalizedEmail = email.trim().toLowerCase();
|
|
24
|
-
const atIndex = normalizedEmail.indexOf("@");
|
|
25
|
-
const dotIndex = normalizedEmail.lastIndexOf(".");
|
|
26
|
-
if (atIndex < 1 || atIndex !== normalizedEmail.lastIndexOf("@") || dotIndex < atIndex + 2 || dotIndex === normalizedEmail.length - 1) {
|
|
27
|
-
throw new Error("A valid email is required");
|
|
28
|
-
}
|
|
29
|
-
if (password.length < 8 || password.length > 1024) {
|
|
30
|
-
throw new Error("Password must be between 8 and 1024 characters");
|
|
31
|
-
}
|
|
32
|
-
return { email: normalizedEmail, password };
|
|
33
|
-
}
|
|
34
|
-
function parseRefreshToken(body) {
|
|
35
|
-
if (!body || typeof body !== "object") {
|
|
36
|
-
throw new Error("Refresh token is required");
|
|
37
|
-
}
|
|
38
|
-
const { refreshToken } = body;
|
|
39
|
-
if (typeof refreshToken !== "string" || refreshToken.trim().length < 16) {
|
|
40
|
-
throw new Error("Refresh token is required");
|
|
41
|
-
}
|
|
42
|
-
return refreshToken;
|
|
43
|
-
}
|
|
44
|
-
function sanitizeSessionResponse(value) {
|
|
45
|
-
if (Array.isArray(value)) {
|
|
46
|
-
return value.map(sanitizeSessionResponse);
|
|
47
|
-
}
|
|
48
|
-
if (!value || typeof value !== "object") {
|
|
49
|
-
return value;
|
|
50
|
-
}
|
|
51
|
-
return Object.fromEntries(Object.entries(value)
|
|
52
|
-
.filter(([key]) => !sensitiveKeys.has(key))
|
|
53
|
-
.map(([key, nestedValue]) => [key, sanitizeSessionResponse(nestedValue)]));
|
|
54
|
-
}
|
|
55
|
-
//# sourceMappingURL=security.js.map
|