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.
Files changed (98) hide show
  1. package/.env +2 -0
  2. package/README.md +93 -0
  3. package/authenik8-core-0.1.0.tgz +0 -0
  4. package/dist/auth/guestModeService.d.ts +3 -0
  5. package/dist/auth/guestModeService.d.ts.map +1 -0
  6. package/dist/auth/guestModeService.js +24 -0
  7. package/dist/auth/guestModeService.js.map +1 -0
  8. package/dist/auth/jwtAuth.d.ts +28 -0
  9. package/dist/auth/jwtAuth.d.ts.map +1 -0
  10. package/dist/auth/jwtAuth.js +67 -0
  11. package/dist/auth/jwtAuth.js.map +1 -0
  12. package/dist/auth/refreshService.d.ts +41 -0
  13. package/dist/auth/refreshService.d.ts.map +1 -0
  14. package/dist/auth/refreshService.js +77 -0
  15. package/dist/auth/refreshService.js.map +1 -0
  16. package/dist/creatAuthenik8.d.ts +2 -0
  17. package/dist/creatAuthenik8.d.ts.map +1 -0
  18. package/dist/creatAuthenik8.js +3 -0
  19. package/dist/creatAuthenik8.js.map +1 -0
  20. package/dist/createAuthenik8.d.ts +4 -0
  21. package/dist/createAuthenik8.d.ts.map +1 -0
  22. package/dist/createAuthenik8.js +58 -0
  23. package/dist/createAuthenik8.js.map +1 -0
  24. package/dist/index.d.ts +2 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +6 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/middleware/adminService.d.ts +4 -0
  29. package/dist/middleware/adminService.d.ts.map +1 -0
  30. package/dist/middleware/adminService.js +40 -0
  31. package/dist/middleware/adminService.js.map +1 -0
  32. package/dist/redis/redisService.d.ts +26 -0
  33. package/dist/redis/redisService.d.ts.map +1 -0
  34. package/dist/redis/redisService.js +104 -0
  35. package/dist/redis/redisService.js.map +1 -0
  36. package/dist/security/ipService.d.ts +36 -0
  37. package/dist/security/ipService.d.ts.map +1 -0
  38. package/dist/security/ipService.js +160 -0
  39. package/dist/security/ipService.js.map +1 -0
  40. package/dist/security/limiter.d.ts +5 -0
  41. package/dist/security/limiter.d.ts.map +1 -0
  42. package/dist/security/limiter.js +93 -0
  43. package/dist/security/limiter.js.map +1 -0
  44. package/dist/storage/RedisTokenStore.d.ts +21 -0
  45. package/dist/storage/RedisTokenStore.d.ts.map +1 -0
  46. package/dist/storage/RedisTokenStore.js +86 -0
  47. package/dist/storage/RedisTokenStore.js.map +1 -0
  48. package/dist/storage/userStorage.d.ts +7 -0
  49. package/dist/storage/userStorage.d.ts.map +1 -0
  50. package/dist/storage/userStorage.js +18 -0
  51. package/dist/storage/userStorage.js.map +1 -0
  52. package/dist/tests/full.intergration.test.d.ts +2 -0
  53. package/dist/tests/full.intergration.test.d.ts.map +1 -0
  54. package/dist/tests/full.intergration.test.js +79 -0
  55. package/dist/tests/full.intergration.test.js.map +1 -0
  56. package/dist/tests/testApp.d.ts +7 -0
  57. package/dist/tests/testApp.d.ts.map +1 -0
  58. package/dist/tests/testApp.js +53 -0
  59. package/dist/tests/testApp.js.map +1 -0
  60. package/dist/types/admin.d.ts +6 -0
  61. package/dist/types/admin.d.ts.map +1 -0
  62. package/dist/types/admin.js +3 -0
  63. package/dist/types/admin.js.map +1 -0
  64. package/dist/types/config.d.ts +9 -0
  65. package/dist/types/config.d.ts.map +1 -0
  66. package/dist/types/config.js +3 -0
  67. package/dist/types/config.js.map +1 -0
  68. package/dist/types/public.d.ts +17 -0
  69. package/dist/types/public.d.ts.map +1 -0
  70. package/dist/types/public.js +3 -0
  71. package/dist/types/public.js.map +1 -0
  72. package/dist/types/storage.d.ts +14 -0
  73. package/dist/types/storage.d.ts.map +1 -0
  74. package/dist/types/storage.js +3 -0
  75. package/dist/types/storage.js.map +1 -0
  76. package/dump.rdb +0 -0
  77. package/jest.config.js +11 -0
  78. package/package.json +56 -0
  79. package/src/1 +22 -0
  80. package/src/auth/guestModeService.ts +31 -0
  81. package/src/auth/jwtAuth.ts +99 -0
  82. package/src/auth/refreshService.ts +134 -0
  83. package/src/creatAuthenik8.ts +0 -0
  84. package/src/createAuthenik8.ts +66 -0
  85. package/src/index.ts +1 -0
  86. package/src/middleware/adminService.ts +50 -0
  87. package/src/redis/redisService.ts +137 -0
  88. package/src/security/ipService.ts +180 -0
  89. package/src/security/limiter.ts +116 -0
  90. package/src/storage/RedisTokenStore.ts +99 -0
  91. package/src/storage/userStorage.ts +16 -0
  92. package/src/tests/full.intergration.test.ts +100 -0
  93. package/src/tests/testApp.ts +56 -0
  94. package/src/types/admin.ts +7 -0
  95. package/src/types/config.ts +11 -0
  96. package/src/types/public.ts +22 -0
  97. package/src/types/storage.ts +15 -0
  98. 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":";;AAAA,iCAAiC;AACjC,uCAA0C;AAE1C,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,IAAI,OAAY,CAAC;IACjB,IAAI,IAAS,CAAC;IAEd,IAAI,WAAmB,CAAC;IACxB,IAAI,YAAoB,CAAC;IAEzB,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,KAAK,GAAG,MAAM,IAAA,uBAAa,GAAE,CAAC;QACpC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QACxB,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YAC3B,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAC1B,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,WAAW;IACX,IAAI,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEzC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;QAC/C,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;QAEhD,WAAW,GAAG,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC;QACnC,YAAY,GAAG,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,qBAAqB;IACrB,IAAI,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,GAAG,GAAG,MAAM,OAAO;aACtB,GAAG,CAAC,YAAY,CAAC;aACjB,GAAG,CAAC,eAAe,EAAE,UAAU,WAAW,EAAE,CAAC,CAAC;QAEjD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,cAAc;IACd,IAAI,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAE5C,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,mBAAmB;IACnB,IAAI,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;QAC7C,MAAM,GAAG,GAAG,MAAM,OAAO;aACtB,IAAI,CAAC,UAAU,CAAC;aAChB,IAAI,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC;QAE1B,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;QAC/C,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;QAEhD,gBAAgB;QAChB,WAAW,GAAG,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC;QACnC,YAAY,GAAG,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC;QAErC,IAAI,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YAC9B,YAAY,GAAG,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC;QACvC,CAAC;IAEC,CAAC,CAAC,CAAC;IAEH,0BAA0B;IAC1B,IAAI,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,aAAa,GAAG,YAAY,CAAC;QAEnC,4BAA4B;QAC5B,MAAM,QAAQ,GAAG,MAAM,OAAO;aAC3B,IAAI,CAAC,UAAU,CAAC;aAChB,IAAI,CAAC,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC,CAAC;QAEzC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAElC,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC;QAE5C,0CAA0C;QAC1C,MAAM,GAAG,GAAG,MAAM,OAAO;aACtB,IAAI,CAAC,UAAU,CAAC;aAChB,IAAI,CAAC,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC,CAAC;QAEzC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAE7B,sCAAsC;QACtC,MAAM,QAAQ,GAAG,MAAM,OAAO;aAC3B,IAAI,CAAC,UAAU,CAAC;aAChB,IAAI,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,CAAC;QAEpC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACD,CAAC,CAAC,CAAC"}
@@ -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,6 @@
1
+ import { Redis } from "ioredis";
2
+ export interface RequireAdminOptions {
3
+ jwtSecret: string;
4
+ redis?: Redis;
5
+ }
6
+ //# sourceMappingURL=admin.d.ts.map
@@ -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,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/types/admin.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ import { SignOptions } from "jsonwebtoken";
2
+ import { Redis } from "ioredis";
3
+ export interface Authenik8Config {
4
+ jwtSecret: string;
5
+ jwtExpiry: SignOptions["expiresIn"];
6
+ refreshSecret: string;
7
+ redis?: Redis;
8
+ }
9
+ //# sourceMappingURL=config.d.ts.map
@@ -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,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=config.js.map
@@ -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,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=public.js.map
@@ -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,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=storage.js.map
@@ -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
@@ -0,0 +1,11 @@
1
+ const { createDefaultPreset } = require("ts-jest");
2
+
3
+ const tsJestTransformCfg = createDefaultPreset().transform;
4
+
5
+ /** @type {import("jest").Config} **/
6
+ module.exports = {
7
+ testEnvironment: "node",
8
+ transform: {
9
+ ...tsJestTransformCfg,
10
+ },
11
+ };
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