authenik8-core 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -57
- package/dist/auth/refreshService.d.ts.map +1 -1
- package/dist/auth/refreshService.js +0 -4
- package/dist/auth/refreshService.js.map +1 -1
- package/dist/tests/full.intergration.test.js +5 -5
- package/dist/tests/full.intergration.test.js.map +1 -1
- package/package.json +8 -4
- package/src/auth/refreshService.ts +4 -5
- package/src/tests/full.intergration.test.ts +5 -5
- package/authenik8-core-0.1.0.tgz +0 -0
- package/dist/creatAuthenik8.d.ts +0 -2
- package/dist/creatAuthenik8.d.ts.map +0 -1
- package/dist/creatAuthenik8.js +0 -3
- package/dist/creatAuthenik8.js.map +0 -1
- package/src/1 +0 -22
- package/src/creatAuthenik8.ts +0 -0
package/README.md
CHANGED
|
@@ -1,93 +1,139 @@
|
|
|
1
1
|
# Authenik8-core
|
|
2
2
|
|
|
3
|
+
JWT rotation without uniqueness is fake security — Authenik8 fixes that.
|
|
4
|
+
Authenik8 is a modular authentication and security SDK for Node.js.
|
|
5
|
+
It combines:
|
|
3
6
|
|
|
7
|
+
JWT authentication
|
|
8
|
+
Secure refresh token rotation
|
|
9
|
+
Redis-backed session control
|
|
10
|
+
Built-in security middleware
|
|
4
11
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
|
|
12
|
+
***
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
## Getting started
|
|
15
|
+
```
|
|
16
|
+
import { createAuthenik8 } from "authenik8";
|
|
10
17
|
|
|
11
|
-
|
|
18
|
+
const auth = await createAuthenik8({
|
|
19
|
+
jwtSecret: "ACCESS_SECRET",
|
|
20
|
+
refreshSecret: "REFRESH_SECRET"
|
|
21
|
+
});
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
|
|
23
|
+
// generate tokens
|
|
24
|
+
const refreshToken = await auth.generateRefreshToken({
|
|
25
|
+
userId: "user_1",
|
|
26
|
+
email: "test@test.com"
|
|
27
|
+
});
|
|
15
28
|
|
|
29
|
+
// refresh tokens
|
|
30
|
+
const result = await auth.refresh(refreshToken);
|
|
16
31
|
```
|
|
17
|
-
|
|
18
|
-
git remote add origin https://gitlab.com/COD434/authenik8-core.git
|
|
19
|
-
git branch -M main
|
|
20
|
-
git push -uf origin main
|
|
21
|
-
```
|
|
32
|
+
***
|
|
22
33
|
|
|
23
|
-
##
|
|
34
|
+
## Why Authenik8-core?
|
|
24
35
|
|
|
25
|
-
* [Set up project integrations](https://gitlab.com/COD434/authenik8-core/-/settings/integrations)
|
|
26
36
|
|
|
27
|
-
|
|
37
|
+
JWT makes authentication look simple…
|
|
38
|
+
…but introduces hidden problems:
|
|
28
39
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
* [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
|
|
40
|
+
Refresh token reuse (replay attacks)
|
|
41
|
+
Stateless logout issues
|
|
42
|
+
Broken token rotation
|
|
43
|
+
Scattered security logic
|
|
34
44
|
|
|
35
|
-
|
|
45
|
+
Authenik8 solves this with:
|
|
36
46
|
|
|
37
|
-
|
|
47
|
+
Refresh token rotation (with uniqueness via jti)
|
|
48
|
+
Stateful session control (Redis)
|
|
49
|
+
Built-in security (rate limit, IP whitelist, helmet)
|
|
50
|
+
Clean, unified API
|
|
38
51
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
***
|
|
53
|
+
## Secure Refresh Flow
|
|
54
|
+
```
|
|
55
|
+
// first use → valid
|
|
56
|
+
await auth.refresh(token);
|
|
44
57
|
|
|
58
|
+
// reuse same token → rejected
|
|
59
|
+
await auth.refresh(token); // ❌ throws
|
|
60
|
+
```
|
|
45
61
|
***
|
|
46
62
|
|
|
47
|
-
|
|
63
|
+
## API Overview
|
|
64
|
+
```
|
|
65
|
+
const auth = await createAuthenik8(config);
|
|
66
|
+
|
|
67
|
+
// auth
|
|
68
|
+
auth.signToken(payload);
|
|
69
|
+
auth.verifyToken(token);
|
|
48
70
|
|
|
49
|
-
|
|
71
|
+
// refresh
|
|
72
|
+
auth.refresh(refreshToken);
|
|
73
|
+
auth.generateRefreshToken(payload);
|
|
50
74
|
|
|
51
|
-
|
|
75
|
+
// security
|
|
76
|
+
auth.rateLimit;
|
|
77
|
+
auth.ipWhitelist;
|
|
78
|
+
auth.helmet;
|
|
52
79
|
|
|
53
|
-
|
|
80
|
+
// middleware
|
|
81
|
+
auth.requireAdmin;
|
|
82
|
+
```
|
|
83
|
+
***
|
|
84
|
+
## Architecture
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
Client
|
|
88
|
+
│
|
|
89
|
+
▼
|
|
90
|
+
Authenik8
|
|
91
|
+
├── JWTService
|
|
92
|
+
├── RefreshService (rotation)
|
|
93
|
+
├── SecurityModule
|
|
94
|
+
▼
|
|
95
|
+
Redis
|
|
54
96
|
|
|
55
|
-
|
|
56
|
-
Choose a self-explaining name for your project.
|
|
97
|
+
***
|
|
57
98
|
|
|
58
|
-
##
|
|
59
|
-
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
|
99
|
+
## Important
|
|
60
100
|
|
|
61
|
-
|
|
62
|
-
|
|
101
|
+
Authenik8-core uses stateful JWT authentication.
|
|
102
|
+
This means:
|
|
103
|
+
Requires Redis (or compatible store)
|
|
104
|
+
Provides better control over sessions and security
|
|
63
105
|
|
|
64
|
-
##
|
|
65
|
-
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
|
106
|
+
## Add your files
|
|
66
107
|
|
|
67
|
-
|
|
68
|
-
|
|
108
|
+
```
|
|
109
|
+
cd existing_repo
|
|
110
|
+
git remote add origin https://gitlab.com/COD434/authenik8-core.git
|
|
111
|
+
git branch -M main
|
|
112
|
+
git push -uf origin main
|
|
113
|
+
```
|
|
69
114
|
|
|
70
|
-
|
|
71
|
-
|
|
115
|
+
***
|
|
116
|
+
## Built with Real Testing
|
|
72
117
|
|
|
73
|
-
|
|
74
|
-
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
|
118
|
+
Authenik8-core includes integration-tested flows for:
|
|
75
119
|
|
|
76
|
-
|
|
77
|
-
|
|
120
|
+
Token rotation
|
|
121
|
+
Replay attack prevention
|
|
122
|
+
Secure refresh logic
|
|
78
123
|
|
|
79
|
-
|
|
80
|
-
State if you are open to contributions and what your requirements are for accepting them.
|
|
124
|
+
***
|
|
81
125
|
|
|
82
|
-
|
|
126
|
+
## Use Cases
|
|
83
127
|
|
|
84
|
-
|
|
128
|
+
SaaS backends
|
|
129
|
+
APIs with authentication
|
|
130
|
+
Secure admin systems
|
|
131
|
+
Systems requiring session control
|
|
85
132
|
|
|
86
|
-
|
|
87
|
-
Show your appreciation to those who have contributed to the project.
|
|
133
|
+
***
|
|
88
134
|
|
|
89
|
-
##
|
|
90
|
-
For open source projects, say how it is licensed.
|
|
135
|
+
## Final Thought
|
|
91
136
|
|
|
92
|
-
|
|
93
|
-
|
|
137
|
+
JWT alone is not an authentication system.
|
|
138
|
+
Authenik8 makes it one.
|
|
139
|
+
***
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refreshService.d.ts","sourceRoot":"","sources":["../../src/auth/refreshService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAI3C,qBAAa,iBAAkB,SAAQ,KAAK;gBAChC,OAAO,SAAgB;CAKlC;AAED,qBAAa,iBAAkB,SAC/B,KAAK;gBACO,OAAO,SAA0B;CAI5C;AAGD,UAAU,YAAY;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACb;AAGD,MAAM,WAAW,UAAU;IAC3B,GAAG,CAAC,GAAG,EAAC,MAAM,GAAE,OAAO,CAAC,MAAM,GAAE,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAC,MAAM,GAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,GAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CAE/B;AACD,MAAM,WAAW,qBAAqB;IACrC,UAAU,EAAC,UAAU,CAAC;IACtB,iBAAiB,EAAC,MAAM,CAAC;IACzB,kBAAkB,EAAC,MAAM,CAAC;IAC1B,iBAAiB,EAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAC3C,mBAAmB,CAAC,EAAC,OAAO,CAAC;IAC7B,kBAAkB,CAAC,EAAC,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC9B,WAAW,EAAC,MAAM,CAAC;IACnB,YAAY,CAAC,EAAC,MAAM,CAAC;CACpB;AAGD,qBAAa,cAAc;IAC3B,OAAO,CAAC,UAAU,CAAY;IAC9B,OAAO,CAAC,iBAAiB,CAAQ;IACjC,OAAO,CAAC,kBAAkB,CAAQ;IAClC,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,kBAAkB,CAAQ;gBAGtB,OAAO,EAAC,qBAAqB;IASlC,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAmB5D,OAAO,CAAC,YAAY,CAAC,EAAC,MAAM,GAAE,OAAO,CAAC,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"refreshService.d.ts","sourceRoot":"","sources":["../../src/auth/refreshService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAI3C,qBAAa,iBAAkB,SAAQ,KAAK;gBAChC,OAAO,SAAgB;CAKlC;AAED,qBAAa,iBAAkB,SAC/B,KAAK;gBACO,OAAO,SAA0B;CAI5C;AAGD,UAAU,YAAY;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACb;AAGD,MAAM,WAAW,UAAU;IAC3B,GAAG,CAAC,GAAG,EAAC,MAAM,GAAE,OAAO,CAAC,MAAM,GAAE,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAC,MAAM,GAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,GAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CAE/B;AACD,MAAM,WAAW,qBAAqB;IACrC,UAAU,EAAC,UAAU,CAAC;IACtB,iBAAiB,EAAC,MAAM,CAAC;IACzB,kBAAkB,EAAC,MAAM,CAAC;IAC1B,iBAAiB,EAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAC3C,mBAAmB,CAAC,EAAC,OAAO,CAAC;IAC7B,kBAAkB,CAAC,EAAC,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC9B,WAAW,EAAC,MAAM,CAAC;IACnB,YAAY,CAAC,EAAC,MAAM,CAAC;CACpB;AAGD,qBAAa,cAAc;IAC3B,OAAO,CAAC,UAAU,CAAY;IAC9B,OAAO,CAAC,iBAAiB,CAAQ;IACjC,OAAO,CAAC,kBAAkB,CAAQ;IAClC,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,kBAAkB,CAAQ;gBAGtB,OAAO,EAAC,qBAAqB;IASlC,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAmB5D,OAAO,CAAC,YAAY,CAAC,EAAC,MAAM,GAAE,OAAO,CAAC,aAAa,CAAC;CA6C1D"}
|
|
@@ -39,7 +39,6 @@ class RefreshService {
|
|
|
39
39
|
if (this.tokenStore.set) {
|
|
40
40
|
await this.tokenStore.set(`refresh:${payload.userId}`, token, 60 * 60 * 24 * 7);
|
|
41
41
|
}
|
|
42
|
-
console.log("Saving token to Redis:", token);
|
|
43
42
|
return token;
|
|
44
43
|
}
|
|
45
44
|
async refresh(refreshToken) {
|
|
@@ -54,8 +53,6 @@ class RefreshService {
|
|
|
54
53
|
throw new InvalidTokenError();
|
|
55
54
|
}
|
|
56
55
|
const storedToken = await this.tokenStore.get(`refresh:${decoded.userId}`);
|
|
57
|
-
console.log("Stored token:", storedToken);
|
|
58
|
-
console.log("Match:", storedToken === refreshToken);
|
|
59
56
|
if (storedToken !== refreshToken) {
|
|
60
57
|
throw new InvalidTokenError();
|
|
61
58
|
}
|
|
@@ -65,7 +62,6 @@ class RefreshService {
|
|
|
65
62
|
const key = `refresh:${decoded.userId}`;
|
|
66
63
|
newRefreshToken = jsonwebtoken_1.default.sign({ userId: decoded.userId, email: decoded.email, jti: (0, crypto_1.randomUUID)(), }, this.refreshTokenSecret, { expiresIn: this.refreshTokenExpiry });
|
|
67
64
|
await this.tokenStore.set(key, newRefreshToken, 60 * 60 * 24 * 7);
|
|
68
|
-
console.log("Stored new refresh token:", newRefreshToken);
|
|
69
65
|
}
|
|
70
66
|
return {
|
|
71
67
|
accessToken: newAccessToken,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refreshService.js","sourceRoot":"","sources":["../../src/auth/refreshService.ts"],"names":[],"mappings":";;;;;;AAAA,gEAA+B;AAE/B,mCAAiC;AAGjC,MAAa,iBAAkB,SAAQ,KAAK;IAC5C,YAAY,OAAO,GAAC,eAAe;QAEnC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAE,mBAAmB,CAAC;IAC/B,CAAC;CACA;AAND,8CAMC;AAED,MAAa,iBAAkB,SAC/B,KAAK;IACL,YAAY,OAAO,GAAG,uBAAuB;QAC7C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAChC,CAAC;CACA;AAND,8CAMC;AA8BD,MAAa,cAAc;IAS3B,YAAY,OAA6B;;QACzC,IAAI,CAAC,UAAU,GAAI,OAAO,CAAC,UAAU,CAAC;QACtC,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;QACnD,IAAI,CAAC,kBAAkB,GAAE,OAAO,CAAC,kBAAkB,CAAC;QACpD,IAAI,CAAC,iBAAiB,GAAE,MAAA,OAAO,CAAC,iBAAiB,mCAAI,KAAK,CAAC;QAC3D,IAAI,CAAC,mBAAmB,GAAG,MAAA,OAAO,CAAC,mBAAmB,mCAAI,KAAK,CAAC;QAChE,IAAI,CAAC,kBAAkB,GAAG,MAAA,OAAO,CAAC,kBAAkB,mCAAI,IAAI,CAAA;IAC3D,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,OAAqB;QAE/C,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QAExF,MAAM,KAAK,GAAG,sBAAG,CAAC,IAAI,CAAC,EAAC,GAAG,OAAO,EAAC,GAAG,EAAC,IAAA,mBAAU,GAAE,GAAE,EAAC,IAAI,CAAC,kBAAkB,EAAE;YAC7E,SAAS,EAAE,IAAI,CAAC,kBAA8C;SAC/D,CAAC,CAAC;QAEH,IAAG,IAAI,CAAC,UAAU,CAAC,GAAG,EAAC,CAAC;YACxB,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CACzB,WAAW,OAAO,CAAC,MAAM,EAAE,EAAC,KAAK,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAClD,CAAC;QACF,CAAC;
|
|
1
|
+
{"version":3,"file":"refreshService.js","sourceRoot":"","sources":["../../src/auth/refreshService.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,7 +18,7 @@ describe("Authenik8 Full Integration", () => {
|
|
|
18
18
|
await auth.redis.quit();
|
|
19
19
|
}
|
|
20
20
|
});
|
|
21
|
-
//
|
|
21
|
+
// LOGIN
|
|
22
22
|
test("should login and receive tokens", async () => {
|
|
23
23
|
const res = await request.post("/login");
|
|
24
24
|
expect(res.status).toBe(200);
|
|
@@ -27,7 +27,7 @@ describe("Authenik8 Full Integration", () => {
|
|
|
27
27
|
accessToken = res.body.accessToken;
|
|
28
28
|
refreshToken = res.body.refreshToken;
|
|
29
29
|
});
|
|
30
|
-
//
|
|
30
|
+
// PROTECTED ROUTE
|
|
31
31
|
test("should access protected route with valid token", async () => {
|
|
32
32
|
const res = await request
|
|
33
33
|
.get("/protected")
|
|
@@ -35,12 +35,12 @@ describe("Authenik8 Full Integration", () => {
|
|
|
35
35
|
expect(res.status).toBe(200);
|
|
36
36
|
expect(res.body).toHaveProperty("data", "secure data");
|
|
37
37
|
});
|
|
38
|
-
//
|
|
38
|
+
// NO TOKEN
|
|
39
39
|
test("should reject request without token", async () => {
|
|
40
40
|
const res = await request.get("/protected");
|
|
41
41
|
expect(res.status).toBe(401);
|
|
42
42
|
});
|
|
43
|
-
//
|
|
43
|
+
// REFRESH TOKEN
|
|
44
44
|
test("should refresh access token", async () => {
|
|
45
45
|
const res = await request
|
|
46
46
|
.post("/refresh")
|
|
@@ -55,7 +55,7 @@ describe("Authenik8 Full Integration", () => {
|
|
|
55
55
|
refreshToken = res.body.refreshToken;
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
|
-
//
|
|
58
|
+
// ROTATION (IMPORTANT)
|
|
59
59
|
test("should NOT allow reuse of old refresh token", async () => {
|
|
60
60
|
const originalToken = refreshToken;
|
|
61
61
|
// first use → rotates token
|
|
@@ -1 +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,
|
|
1
|
+
{"version":3,"file":"full.intergration.test.js","sourceRoot":"","sources":["../../src/tests/full.intergration.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "authenik8-core",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "A modular Node.js authentication SDK with JWT, secure refresh token rotation, and built-in security middleware.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"authentication",
|
|
7
7
|
"jwt",
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
"redis",
|
|
11
11
|
"express",
|
|
12
12
|
"nodejs",
|
|
13
|
-
"auth"
|
|
13
|
+
"auth",
|
|
14
|
+
"backend",
|
|
15
|
+
"helmet"
|
|
14
16
|
],
|
|
15
17
|
"homepage": "https://gitlab.com/COD434/authenik8-core#readme",
|
|
16
18
|
"bugs": {
|
|
@@ -29,7 +31,9 @@
|
|
|
29
31
|
"test": "jest",
|
|
30
32
|
"build": "tsc",
|
|
31
33
|
"files": [
|
|
32
|
-
"dist"
|
|
34
|
+
"dist",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE"
|
|
33
37
|
]
|
|
34
38
|
},
|
|
35
39
|
"devDependencies": {
|
|
@@ -79,7 +79,7 @@ this.refreshTokenExpiry = options.refreshTokenExpiry ?? "7d"
|
|
|
79
79
|
);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
return token
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -97,8 +97,7 @@ throw new InvalidTokenError()
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
const storedToken= await this.tokenStore.get(`refresh:${decoded.userId}`)
|
|
100
|
-
|
|
101
|
-
console.log("Match:", storedToken === refreshToken);
|
|
100
|
+
|
|
102
101
|
|
|
103
102
|
|
|
104
103
|
if (storedToken !== refreshToken){
|
|
@@ -116,13 +115,13 @@ let newRefreshToken:string | undefined;
|
|
|
116
115
|
if(this.rotateRefreshTokens && this.tokenStore.set){
|
|
117
116
|
const key = `refresh:${decoded.userId}`
|
|
118
117
|
|
|
119
|
-
|
|
118
|
+
;
|
|
120
119
|
newRefreshToken =jwt.sign({userId:decoded.userId,email : decoded.email,jti:randomUUID(),},
|
|
121
120
|
this.refreshTokenSecret,
|
|
122
121
|
{expiresIn:this.refreshTokenExpiry as jwt.SignOptions["expiresIn"]});
|
|
123
122
|
|
|
124
123
|
await this.tokenStore.set(key,newRefreshToken,60 * 60 * 24 * 7)
|
|
125
|
-
|
|
124
|
+
|
|
126
125
|
}
|
|
127
126
|
|
|
128
127
|
return{
|
|
@@ -21,7 +21,7 @@ describe("Authenik8 Full Integration", () => {
|
|
|
21
21
|
}
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
//
|
|
24
|
+
// LOGIN
|
|
25
25
|
test("should login and receive tokens", async () => {
|
|
26
26
|
const res = await request.post("/login");
|
|
27
27
|
|
|
@@ -33,7 +33,7 @@ describe("Authenik8 Full Integration", () => {
|
|
|
33
33
|
refreshToken = res.body.refreshToken;
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
//
|
|
36
|
+
// PROTECTED ROUTE
|
|
37
37
|
test("should access protected route with valid token", async () => {
|
|
38
38
|
const res = await request
|
|
39
39
|
.get("/protected")
|
|
@@ -43,14 +43,14 @@ describe("Authenik8 Full Integration", () => {
|
|
|
43
43
|
expect(res.body).toHaveProperty("data", "secure data");
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
//
|
|
46
|
+
// NO TOKEN
|
|
47
47
|
test("should reject request without token", async () => {
|
|
48
48
|
const res = await request.get("/protected");
|
|
49
49
|
|
|
50
50
|
expect(res.status).toBe(401);
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
//
|
|
53
|
+
// REFRESH TOKEN
|
|
54
54
|
test("should refresh access token", async () => {
|
|
55
55
|
const res = await request
|
|
56
56
|
.post("/refresh")
|
|
@@ -70,7 +70,7 @@ describe("Authenik8 Full Integration", () => {
|
|
|
70
70
|
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
//
|
|
73
|
+
// ROTATION (IMPORTANT)
|
|
74
74
|
test("should NOT allow reuse of old refresh token", async () => {
|
|
75
75
|
const originalToken = refreshToken;
|
|
76
76
|
|
package/authenik8-core-0.1.0.tgz
DELETED
|
Binary file
|
package/dist/creatAuthenik8.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"creatAuthenik8.d.ts","sourceRoot":"","sources":["../src/creatAuthenik8.ts"],"names":[],"mappings":""}
|
package/dist/creatAuthenik8.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"creatAuthenik8.js","sourceRoot":"","sources":["../src/creatAuthenik8.ts"],"names":[],"mappings":""}
|
package/src/1
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
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
|
-
}
|
package/src/creatAuthenik8.ts
DELETED
|
File without changes
|