authenik8-core 0.1.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/.env +2 -0
- package/README.md +93 -0
- package/authenik8-core-0.1.0.tgz +0 -0
- package/dist/auth/guestModeService.d.ts +3 -0
- package/dist/auth/guestModeService.d.ts.map +1 -0
- package/dist/auth/guestModeService.js +24 -0
- package/dist/auth/guestModeService.js.map +1 -0
- package/dist/auth/jwtAuth.d.ts +28 -0
- package/dist/auth/jwtAuth.d.ts.map +1 -0
- package/dist/auth/jwtAuth.js +67 -0
- package/dist/auth/jwtAuth.js.map +1 -0
- package/dist/auth/refreshService.d.ts +41 -0
- package/dist/auth/refreshService.d.ts.map +1 -0
- package/dist/auth/refreshService.js +77 -0
- package/dist/auth/refreshService.js.map +1 -0
- package/dist/creatAuthenik8.d.ts +2 -0
- package/dist/creatAuthenik8.d.ts.map +1 -0
- package/dist/creatAuthenik8.js +3 -0
- package/dist/creatAuthenik8.js.map +1 -0
- package/dist/createAuthenik8.d.ts +4 -0
- package/dist/createAuthenik8.d.ts.map +1 -0
- package/dist/createAuthenik8.js +58 -0
- package/dist/createAuthenik8.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/adminService.d.ts +4 -0
- package/dist/middleware/adminService.d.ts.map +1 -0
- package/dist/middleware/adminService.js +40 -0
- package/dist/middleware/adminService.js.map +1 -0
- package/dist/redis/redisService.d.ts +26 -0
- package/dist/redis/redisService.d.ts.map +1 -0
- package/dist/redis/redisService.js +104 -0
- package/dist/redis/redisService.js.map +1 -0
- package/dist/security/ipService.d.ts +36 -0
- package/dist/security/ipService.d.ts.map +1 -0
- package/dist/security/ipService.js +160 -0
- package/dist/security/ipService.js.map +1 -0
- package/dist/security/limiter.d.ts +5 -0
- package/dist/security/limiter.d.ts.map +1 -0
- package/dist/security/limiter.js +93 -0
- package/dist/security/limiter.js.map +1 -0
- package/dist/storage/RedisTokenStore.d.ts +21 -0
- package/dist/storage/RedisTokenStore.d.ts.map +1 -0
- package/dist/storage/RedisTokenStore.js +86 -0
- package/dist/storage/RedisTokenStore.js.map +1 -0
- package/dist/storage/userStorage.d.ts +7 -0
- package/dist/storage/userStorage.d.ts.map +1 -0
- package/dist/storage/userStorage.js +18 -0
- package/dist/storage/userStorage.js.map +1 -0
- package/dist/tests/full.intergration.test.d.ts +2 -0
- package/dist/tests/full.intergration.test.d.ts.map +1 -0
- package/dist/tests/full.intergration.test.js +79 -0
- package/dist/tests/full.intergration.test.js.map +1 -0
- package/dist/tests/testApp.d.ts +7 -0
- package/dist/tests/testApp.d.ts.map +1 -0
- package/dist/tests/testApp.js +53 -0
- package/dist/tests/testApp.js.map +1 -0
- package/dist/types/admin.d.ts +6 -0
- package/dist/types/admin.d.ts.map +1 -0
- package/dist/types/admin.js +3 -0
- package/dist/types/admin.js.map +1 -0
- package/dist/types/config.d.ts +9 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +3 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/public.d.ts +17 -0
- package/dist/types/public.d.ts.map +1 -0
- package/dist/types/public.js +3 -0
- package/dist/types/public.js.map +1 -0
- package/dist/types/storage.d.ts +14 -0
- package/dist/types/storage.d.ts.map +1 -0
- package/dist/types/storage.js +3 -0
- package/dist/types/storage.js.map +1 -0
- package/dump.rdb +0 -0
- package/jest.config.js +11 -0
- package/package.json +56 -0
- package/src/1 +22 -0
- package/src/auth/guestModeService.ts +31 -0
- package/src/auth/jwtAuth.ts +99 -0
- package/src/auth/refreshService.ts +134 -0
- package/src/creatAuthenik8.ts +0 -0
- package/src/createAuthenik8.ts +66 -0
- package/src/index.ts +1 -0
- package/src/middleware/adminService.ts +50 -0
- package/src/redis/redisService.ts +137 -0
- package/src/security/ipService.ts +180 -0
- package/src/security/limiter.ts +116 -0
- package/src/storage/RedisTokenStore.ts +99 -0
- package/src/storage/userStorage.ts +16 -0
- package/src/tests/full.intergration.test.ts +100 -0
- package/src/tests/testApp.ts +56 -0
- package/src/types/admin.ts +7 -0
- package/src/types/config.ts +11 -0
- package/src/types/public.ts +22 -0
- package/src/types/storage.ts +15 -0
- package/tsconfig.json +51 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
// tests/full.integration.test.ts
|
|
4
|
+
const testApp_1 = require("./testApp");
|
|
5
|
+
describe("Authenik8 Full Integration", () => {
|
|
6
|
+
let request;
|
|
7
|
+
let auth;
|
|
8
|
+
let accessToken;
|
|
9
|
+
let refreshToken;
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
const setup = await (0, testApp_1.createTestApp)();
|
|
12
|
+
request = setup.request;
|
|
13
|
+
auth = setup.auth;
|
|
14
|
+
});
|
|
15
|
+
afterAll(async () => {
|
|
16
|
+
if (auth.redis) {
|
|
17
|
+
await auth.redis.flushdb();
|
|
18
|
+
await auth.redis.quit();
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
// 🔐 LOGIN
|
|
22
|
+
test("should login and receive tokens", async () => {
|
|
23
|
+
const res = await request.post("/login");
|
|
24
|
+
expect(res.status).toBe(200);
|
|
25
|
+
expect(res.body).toHaveProperty("accessToken");
|
|
26
|
+
expect(res.body).toHaveProperty("refreshToken");
|
|
27
|
+
accessToken = res.body.accessToken;
|
|
28
|
+
refreshToken = res.body.refreshToken;
|
|
29
|
+
});
|
|
30
|
+
// 🔒 PROTECTED ROUTE
|
|
31
|
+
test("should access protected route with valid token", async () => {
|
|
32
|
+
const res = await request
|
|
33
|
+
.get("/protected")
|
|
34
|
+
.set("Authorization", `Bearer ${accessToken}`);
|
|
35
|
+
expect(res.status).toBe(200);
|
|
36
|
+
expect(res.body).toHaveProperty("data", "secure data");
|
|
37
|
+
});
|
|
38
|
+
// 🚫 NO TOKEN
|
|
39
|
+
test("should reject request without token", async () => {
|
|
40
|
+
const res = await request.get("/protected");
|
|
41
|
+
expect(res.status).toBe(401);
|
|
42
|
+
});
|
|
43
|
+
// 🔄 REFRESH TOKEN
|
|
44
|
+
test("should refresh access token", async () => {
|
|
45
|
+
const res = await request
|
|
46
|
+
.post("/refresh")
|
|
47
|
+
.send({ refreshToken });
|
|
48
|
+
expect(res.status).toBe(200);
|
|
49
|
+
expect(res.body).toHaveProperty("accessToken");
|
|
50
|
+
expect(res.body).toHaveProperty("refreshToken");
|
|
51
|
+
// update tokens
|
|
52
|
+
accessToken = res.body.accessToken;
|
|
53
|
+
refreshToken = res.body.refreshToken;
|
|
54
|
+
if (res.body.refreshToken) {
|
|
55
|
+
refreshToken = res.body.refreshToken;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
// 🔥 ROTATION (IMPORTANT)
|
|
59
|
+
test("should NOT allow reuse of old refresh token", async () => {
|
|
60
|
+
const originalToken = refreshToken;
|
|
61
|
+
// first use → rotates token
|
|
62
|
+
const firstRes = await request
|
|
63
|
+
.post("/refresh")
|
|
64
|
+
.send({ refreshToken: originalToken });
|
|
65
|
+
expect(firstRes.status).toBe(200);
|
|
66
|
+
const newToken = firstRes.body.refreshToken;
|
|
67
|
+
// second use with OLD token → should fail
|
|
68
|
+
const res = await request
|
|
69
|
+
.post("/refresh")
|
|
70
|
+
.send({ refreshToken: originalToken });
|
|
71
|
+
expect(res.status).toBe(401);
|
|
72
|
+
// sanity check: new token should work
|
|
73
|
+
const validRes = await request
|
|
74
|
+
.post("/refresh")
|
|
75
|
+
.send({ refreshToken: newToken });
|
|
76
|
+
expect(validRes.status).toBe(200);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=full.intergration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"full.intergration.test.js","sourceRoot":"","sources":["../../src/tests/full.intergration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import request from "supertest";
|
|
2
|
+
export declare const createTestApp: () => Promise<{
|
|
3
|
+
app: import("express-serve-static-core").Express;
|
|
4
|
+
auth: import("../types/public").Authenik8Instance;
|
|
5
|
+
request: import("supertest/lib/agent")<request.SuperTestStatic.Test>;
|
|
6
|
+
}>;
|
|
7
|
+
//# sourceMappingURL=testApp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testApp.d.ts","sourceRoot":"","sources":["../../src/tests/testApp.ts"],"names":[],"mappings":"AAEA,OAAO,OAAO,MAAM,WAAW,CAAC;AAGhC,eAAO,MAAM,aAAa;;;;EAkDzB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createTestApp = void 0;
|
|
7
|
+
// tests/testApp.ts
|
|
8
|
+
const express_1 = __importDefault(require("express"));
|
|
9
|
+
const supertest_1 = __importDefault(require("supertest"));
|
|
10
|
+
const createAuthenik8_1 = require("../createAuthenik8");
|
|
11
|
+
const createTestApp = async () => {
|
|
12
|
+
const auth = await (0, createAuthenik8_1.createAuthenik8)({
|
|
13
|
+
jwtSecret: "test-secret",
|
|
14
|
+
refreshSecret: "refresh-secret",
|
|
15
|
+
jwtExpiry: "15m"
|
|
16
|
+
});
|
|
17
|
+
const app = (0, express_1.default)();
|
|
18
|
+
app.use(express_1.default.json());
|
|
19
|
+
// 🔐 Login (simulate user)
|
|
20
|
+
app.post("/login", async (req, res) => {
|
|
21
|
+
const user = { userId: "user_1", email: "test@test.com" };
|
|
22
|
+
const accessToken = auth.signToken(user);
|
|
23
|
+
const refreshToken = await auth.generateRefreshToken(user);
|
|
24
|
+
res.json({ accessToken, refreshToken });
|
|
25
|
+
console.log("Refresh Token", refreshToken);
|
|
26
|
+
});
|
|
27
|
+
// 🔒 Protected route
|
|
28
|
+
app.get("/protected", (req, res) => {
|
|
29
|
+
var _a;
|
|
30
|
+
const token = (_a = req.headers.authorization) === null || _a === void 0 ? void 0 : _a.split(" ")[1];
|
|
31
|
+
if (!token) {
|
|
32
|
+
return res.status(401).json({ error: "Invalid token" });
|
|
33
|
+
}
|
|
34
|
+
const decoded = auth.verifyToken(token);
|
|
35
|
+
if (!decoded) {
|
|
36
|
+
return res.status(401).json({ error: "Unauthorized" });
|
|
37
|
+
}
|
|
38
|
+
res.json({ data: "secure data", user: decoded });
|
|
39
|
+
});
|
|
40
|
+
// 🔄 Refresh
|
|
41
|
+
app.post("/refresh", async (req, res) => {
|
|
42
|
+
try {
|
|
43
|
+
const result = await auth.refreshToken(req.body.refreshToken);
|
|
44
|
+
res.json(result);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
res.status(401).json({ error: "Invalid refresh token" });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return { app, auth, request: (0, supertest_1.default)(app) };
|
|
51
|
+
};
|
|
52
|
+
exports.createTestApp = createTestApp;
|
|
53
|
+
//# sourceMappingURL=testApp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testApp.js","sourceRoot":"","sources":["../../src/tests/testApp.ts"],"names":[],"mappings":";;;;;;AAAA,mBAAmB;AACnB,sDAA8B;AAC9B,0DAAgC;AAChC,wDAAqD;AAE9C,MAAM,aAAa,GAAG,KAAK,IAAI,EAAE;IACtC,MAAM,IAAI,GAAG,MAAM,IAAA,iCAAe,EAAC;QACjC,SAAS,EAAE,aAAa;QACxB,aAAa,EAAE,gBAAgB;QAC/B,SAAS,EAAE,KAAK;KACjB,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,IAAA,iBAAO,GAAE,CAAC;IACtB,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAExB,2BAA2B;IAC3B,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACnC,MAAM,IAAI,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAC,eAAe,EAAE,CAAC;QAEzD,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAE3D,GAAG,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,eAAe,EAAC,YAAY,CAAC,CAAA;IAC3C,CAAC,CAAC,CAAC;IAEH,qBAAqB;IACrB,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;;QACjC,MAAM,KAAK,GAAG,MAAA,GAAG,CAAC,OAAO,CAAC,aAAa,0CAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAEvD,IAAG,CAAC,KAAK,EAAC,CAAC;YACZ,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAC,KAAK,EAAC,eAAe,EAAC,CAAC,CAAA;QACnD,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAExC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;QACzD,CAAC;QAED,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,aAAa;IACb,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACtC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC9D,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;QAC3D,CAAC;IAEH,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,IAAA,mBAAO,EAAC,GAAG,CAAC,EAAE,CAAC;AAC9C,CAAC,CAAC;AAlDW,QAAA,aAAa,iBAkDxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/types/admin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/types/admin.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IACpC,aAAa,EAAE,MAAM,CAAC;IAEtB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface Authenik8Instance {
|
|
2
|
+
signToken: (payload: any) => string;
|
|
3
|
+
verifyToken: (token: string) => any;
|
|
4
|
+
guestToken: () => string;
|
|
5
|
+
refreshToken: (token: string) => Promise<any>;
|
|
6
|
+
generateRefreshToken: (payload: any) => Promise<string>;
|
|
7
|
+
rateLimit: any;
|
|
8
|
+
ipWhitelist: any;
|
|
9
|
+
helmet: any;
|
|
10
|
+
addIP: (ip: string) => Promise<void>;
|
|
11
|
+
removeIP: (ip: string) => Promise<void>;
|
|
12
|
+
listIPs: () => Promise<string[]>;
|
|
13
|
+
requireAdmin: any;
|
|
14
|
+
incognito: any;
|
|
15
|
+
redis?: any;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=public.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/types/public.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,MAAM,CAAC;IACpC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,GAAG,CAAC;IACpC,UAAU,EAAE,MAAM,MAAM,CAAC;IAEzB,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IAC9C,oBAAoB,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAExD,SAAS,EAAE,GAAG,CAAC;IACf,WAAW,EAAE,GAAG,CAAC;IACjB,MAAM,EAAE,GAAG,CAAC;IAEZ,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEjC,YAAY,EAAE,GAAG,CAAC;IAClB,SAAS,EAAE,GAAG,CAAC;IAEf,KAAK,CAAC,EAAE,GAAG,CAAC;CACb"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public.js","sourceRoot":"","sources":["../../src/types/public.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface User {
|
|
2
|
+
id: string;
|
|
3
|
+
email: string;
|
|
4
|
+
password: string;
|
|
5
|
+
role?: string;
|
|
6
|
+
type?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface UserStore {
|
|
9
|
+
findByEmail(email: string): Promise<User | null>;
|
|
10
|
+
findById(id: string): Promise<User | null>;
|
|
11
|
+
create(user: Partial<User>): Promise<User>;
|
|
12
|
+
update(id: string, data: Partial<User>): Promise<User>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/types/storage.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,IAAI;IACrB,EAAE,EAAC,MAAM,CAAC;IACV,KAAK,EAAC,MAAM,CAAC;IACb,QAAQ,EAAC,MAAM,CAAC;IAChB,IAAI,CAAC,EAAC,MAAM,CAAC;IACb,IAAI,CAAC,EAAC,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,SAAS;IAC1B,WAAW,CAAC,KAAK,EAAC,MAAM,GAAE,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC/C,QAAQ,CAAC,EAAE,EAAC,MAAM,GAAE,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IACzC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,GAC1B,OAAO,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,CAAC,EAAE,EAAC,MAAM,EAAE,IAAI,EAAC,OAAO,CAAC,IAAI,CAAC,GAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/types/storage.ts"],"names":[],"mappings":""}
|
package/dump.rdb
ADDED
|
Binary file
|
package/jest.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "authenik8-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Security-First authentication toolkit for nodejs APIs",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"authentication",
|
|
7
|
+
"jwt",
|
|
8
|
+
"rate-limiter",
|
|
9
|
+
"api-security",
|
|
10
|
+
"redis",
|
|
11
|
+
"express",
|
|
12
|
+
"nodejs",
|
|
13
|
+
"auth"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://gitlab.com/COD434/authenik8-core#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://gitlab.com/COD434/authenik8-core/issues"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://gitlab.com/COD434/authenik8-core.git"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": "Karabo Seeisa",
|
|
25
|
+
"type": "commonjs",
|
|
26
|
+
"main": "dist/index.js",
|
|
27
|
+
"types": "dist/index.d.ts",
|
|
28
|
+
"scripts": {
|
|
29
|
+
"test": "jest",
|
|
30
|
+
"build": "tsc",
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/express": "^5.0.6",
|
|
37
|
+
"@types/jest": "^30.0.0",
|
|
38
|
+
"@types/jsonwebtoken": "^9.0.10",
|
|
39
|
+
"@types/node": "^25.5.0",
|
|
40
|
+
"@types/supertest": "^7.2.0",
|
|
41
|
+
"jest": "^30.3.0",
|
|
42
|
+
"supertest": "^7.2.2",
|
|
43
|
+
"ts-jest": "^29.4.6",
|
|
44
|
+
"typescript": "^5.9.3"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"connect-redis": "^9.0.0",
|
|
48
|
+
"dotenv": "^17.3.1",
|
|
49
|
+
"express": "^5.2.1",
|
|
50
|
+
"helmet": "^8.1.0",
|
|
51
|
+
"ioredis": "^5.10.0",
|
|
52
|
+
"ip-cidr": "^4.0.2",
|
|
53
|
+
"jsonwebtoken": "^9.0.3",
|
|
54
|
+
"rate-limiter-flexible": "^10.0.1"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/1
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {Authenik8Config} from "./types/config";
|
|
2
|
+
import {Incognito} from "./auth/guestModeService"
|
|
3
|
+
import {requireAdmin} from "./middleware/adminService";
|
|
4
|
+
|
|
5
|
+
export const createAuthenik8 =(config:Authenik8Config)=>{
|
|
6
|
+
const jwtService =new JWTService({
|
|
7
|
+
secret:config.jwtSecret,
|
|
8
|
+
expiry:config.jwtExpiry,
|
|
9
|
+
redisClient:config.redis
|
|
10
|
+
});
|
|
11
|
+
return{
|
|
12
|
+
//auth
|
|
13
|
+
signToken:jwtService.signToken.bind(jwtService),
|
|
14
|
+
verifyToken:jwtService.verifyToken.bind(jwtService),
|
|
15
|
+
guestToken:jwtService.guestToken.bind(jwtService)
|
|
16
|
+
|
|
17
|
+
//middleware
|
|
18
|
+
requireAdmin :requireAdmin(config),
|
|
19
|
+
incognito:Incognito
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
import { Request, Response, NextFunction } from "express";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
interface User{
|
|
6
|
+
id:string;
|
|
7
|
+
type?: 'guest-mode' | 'authenticated'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const verifyToken = (token: string):User => ({
|
|
11
|
+
id: "guest",
|
|
12
|
+
type:token ==="temp-token" ? "guest-mode" : "authenticated"});
|
|
13
|
+
const guestToken = () => "temp-token";
|
|
14
|
+
|
|
15
|
+
export const Incognito = (req:Request, res:Response, next:NextFunction)=>{
|
|
16
|
+
const authHeader = req.headers.authorization;
|
|
17
|
+
|
|
18
|
+
let token = authHeader?.split(" ")[1];
|
|
19
|
+
let user = token? verifyToken(token) : null;
|
|
20
|
+
|
|
21
|
+
if(!user){
|
|
22
|
+
const GToken = guestToken();
|
|
23
|
+
user = verifyToken(GToken);
|
|
24
|
+
res.setHeader("X-Guest-Token",GToken);
|
|
25
|
+
}
|
|
26
|
+
if(user?.type === "guest-mode"){
|
|
27
|
+
}
|
|
28
|
+
(res as any).user = user;
|
|
29
|
+
next();
|
|
30
|
+
}
|
|
31
|
+
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
|
|
2
|
+
import {Request, Response, NextFunction} from "express";
|
|
3
|
+
import jwt from "jsonwebtoken";
|
|
4
|
+
import { SignOptions } from "jsonwebtoken"
|
|
5
|
+
import crypto from "crypto"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
interface JwtPayload {
|
|
9
|
+
userId: string;
|
|
10
|
+
email: string;
|
|
11
|
+
type?:string;
|
|
12
|
+
id?:string;
|
|
13
|
+
createdAt?:number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface JWTOptions{
|
|
17
|
+
jwtSecret:string;
|
|
18
|
+
expiry?:SignOptions["expiresIn"];
|
|
19
|
+
redisClient?:any;
|
|
20
|
+
onGuestToken?: () => void;
|
|
21
|
+
}
|
|
22
|
+
export class JWTService{
|
|
23
|
+
|
|
24
|
+
private jwtSecret:string;
|
|
25
|
+
private expiry?:SignOptions["expiresIn"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
signToken(payload: object){
|
|
29
|
+
return jwt.sign(payload,this.jwtSecret,{
|
|
30
|
+
expiresIn:this.expiry || "1h"})
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
private redisclient?:any;
|
|
35
|
+
private onGuestToken?: () => void;
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
constructor(options:JWTOptions){
|
|
39
|
+
this.jwtSecret = options.jwtSecret;
|
|
40
|
+
this.expiry = options.expiry;
|
|
41
|
+
this.redisclient = options.redisClient;
|
|
42
|
+
this.onGuestToken = options.onGuestToken;
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
guestToken(): string{
|
|
48
|
+
const payload = {
|
|
49
|
+
type: "guest",
|
|
50
|
+
id:crypto.randomUUID(),
|
|
51
|
+
createdAt:Date.now()
|
|
52
|
+
}
|
|
53
|
+
if(this.onGuestToken)
|
|
54
|
+
this.onGuestToken();
|
|
55
|
+
|
|
56
|
+
return jwt.sign(payload,this.jwtSecret,{expiresIn:this.expiry});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
verifyToken(token:string):JwtPayload | null{
|
|
60
|
+
try{
|
|
61
|
+
return jwt.verify(token,this.jwtSecret)as JwtPayload;}catch{
|
|
62
|
+
return null
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
authenticateJWT = async(
|
|
68
|
+
req:Request,
|
|
69
|
+
res:Response,
|
|
70
|
+
next:NextFunction
|
|
71
|
+
) => {
|
|
72
|
+
const authHeader = req.headers.authorization;
|
|
73
|
+
const token = req.cookies?.token || (authHeader?.startsWith("Bearer ") ? authHeader.split(" ")[1]:null);
|
|
74
|
+
|
|
75
|
+
if(!token){
|
|
76
|
+
return res.status(401).json({message:"Unauthorized"})
|
|
77
|
+
}
|
|
78
|
+
try{
|
|
79
|
+
const decoded =jwt.verify(token,this.jwtSecret)as JwtPayload;
|
|
80
|
+
|
|
81
|
+
if(this.redisclient && decoded.userId){
|
|
82
|
+
const storedToken = await this.redisclient.get(`session:${decoded.userId}`);
|
|
83
|
+
|
|
84
|
+
if(storedToken !== token){
|
|
85
|
+
return res
|
|
86
|
+
.status(403)
|
|
87
|
+
.json({success:false,message:"invalid session",errors: []})
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
(req as any).user =decoded;
|
|
91
|
+
next();
|
|
92
|
+
|
|
93
|
+
}catch{
|
|
94
|
+
return res.status(403)
|
|
95
|
+
.json({success:false, message: "invalid or expired token"});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import jwt from "jsonwebtoken";
|
|
2
|
+
import { SignOptions } from "jsonwebtoken";
|
|
3
|
+
import {randomUUID} from "crypto"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export class MissingTokenError extends Error{
|
|
7
|
+
constructor(message="Missing Token")
|
|
8
|
+
{
|
|
9
|
+
super(message);
|
|
10
|
+
this.name ="MissingTokenError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class InvalidTokenError extends
|
|
15
|
+
Error{
|
|
16
|
+
constructor(message = "Invalid refresh token"){
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "InvalidTokenError";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
interface TokenPayload{
|
|
24
|
+
userId: string;
|
|
25
|
+
email: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
export interface TokenStore{
|
|
30
|
+
get(key:string):Promise<string| null>;
|
|
31
|
+
set?(key: string, value: string, expiry?:number):Promise<void>;
|
|
32
|
+
del?(key :string):Promise<void>;
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
export interface RefreshServiceOptions {
|
|
36
|
+
tokenStore:TokenStore;
|
|
37
|
+
accessTokenSecret:string;
|
|
38
|
+
refreshTokenSecret:string;
|
|
39
|
+
accessTokenExpiry:SignOptions["expiresIn"];
|
|
40
|
+
rotateRefreshTokens?:boolean;
|
|
41
|
+
refreshTokenExpiry?:string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface RefreshResult{
|
|
45
|
+
accessToken:string;
|
|
46
|
+
refreshToken?:string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
export class RefreshService{
|
|
51
|
+
private tokenStore:TokenStore;
|
|
52
|
+
private accessTokenSecret:string;
|
|
53
|
+
private refreshTokenSecret:string;
|
|
54
|
+
private accessTokenExpiry:SignOptions["expiresIn"];
|
|
55
|
+
private rotateRefreshTokens:boolean;
|
|
56
|
+
private refreshTokenExpiry:string;
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
constructor(options:RefreshServiceOptions){
|
|
60
|
+
this.tokenStore = options.tokenStore;
|
|
61
|
+
this.accessTokenSecret = options.accessTokenSecret;
|
|
62
|
+
this.refreshTokenSecret =options.refreshTokenSecret;
|
|
63
|
+
this.accessTokenExpiry= options.accessTokenExpiry ?? "15m";
|
|
64
|
+
this.rotateRefreshTokens = options.rotateRefreshTokens ?? false;
|
|
65
|
+
this.refreshTokenExpiry = options.refreshTokenExpiry ?? "7d"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async generateRefreshToken(payload: TokenPayload): Promise<string> {
|
|
69
|
+
|
|
70
|
+
if (!payload.userId) throw new Error("generateRefreshToken: payload.userId is missing");
|
|
71
|
+
|
|
72
|
+
const token = jwt.sign({...payload,jti:randomUUID(),},this.refreshTokenSecret, {
|
|
73
|
+
expiresIn: this.refreshTokenExpiry as SignOptions["expiresIn"],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if(this.tokenStore.set){
|
|
77
|
+
await this.tokenStore.set(
|
|
78
|
+
`refresh:${payload.userId}`,token, 60 * 60 * 24 * 7
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log("Saving token to Redis:", token);
|
|
83
|
+
return token
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
async refresh(refreshToken?:string):Promise<RefreshResult>{
|
|
88
|
+
if(!refreshToken){
|
|
89
|
+
throw new MissingTokenError()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let decoded:TokenPayload;
|
|
93
|
+
try {
|
|
94
|
+
decoded = jwt.verify(refreshToken,this.refreshTokenSecret) as TokenPayload;
|
|
95
|
+
}catch(err){
|
|
96
|
+
throw new InvalidTokenError()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const storedToken= await this.tokenStore.get(`refresh:${decoded.userId}`)
|
|
100
|
+
console.log("Stored token:", storedToken);
|
|
101
|
+
console.log("Match:", storedToken === refreshToken);
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if (storedToken !== refreshToken){
|
|
105
|
+
throw new InvalidTokenError();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const newAccessToken = jwt.sign(
|
|
109
|
+
{ userId: decoded.userId, email: decoded.email },
|
|
110
|
+
this.accessTokenSecret,
|
|
111
|
+
{ expiresIn: this.accessTokenExpiry as jwt.SignOptions["expiresIn"] }
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
let newRefreshToken:string | undefined;
|
|
115
|
+
|
|
116
|
+
if(this.rotateRefreshTokens && this.tokenStore.set){
|
|
117
|
+
const key = `refresh:${decoded.userId}`
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
newRefreshToken =jwt.sign({userId:decoded.userId,email : decoded.email,jti:randomUUID(),},
|
|
121
|
+
this.refreshTokenSecret,
|
|
122
|
+
{expiresIn:this.refreshTokenExpiry as jwt.SignOptions["expiresIn"]});
|
|
123
|
+
|
|
124
|
+
await this.tokenStore.set(key,newRefreshToken,60 * 60 * 24 * 7)
|
|
125
|
+
console.log("Stored new refresh token:", newRefreshToken)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return{
|
|
129
|
+
accessToken:newAccessToken,
|
|
130
|
+
refreshToken:newRefreshToken ?? refreshToken
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
File without changes
|