@server/next 0.20.10 → 0.20.11
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/package.json +1 -1
- package/src/auth/auth-token.test.js +90 -23
- package/src/auth/auth.js +5 -0
- package/src/auth/findUser.js +5 -0
- package/src/auth/index.js +2 -0
- package/src/auth/providers/email.js +39 -1
- package/src/auth/updateUser.js +6 -0
- package/src/auth/user.js +3 -14
package/package.json
CHANGED
|
@@ -11,32 +11,99 @@ describe("user creation flow", () => {
|
|
|
11
11
|
const CREDENTIALS = { email: EMAIL, password: PASS };
|
|
12
12
|
|
|
13
13
|
const store = kv(new Map());
|
|
14
|
+
const sessions = () => store.prefix("auth:").keys();
|
|
15
|
+
const users = () => store.prefix("user:").keys();
|
|
14
16
|
const api = server({ store, auth: "token:email" })
|
|
15
17
|
.get("/me", (ctx) => ctx.user || "No data")
|
|
16
18
|
.test();
|
|
17
19
|
|
|
18
|
-
it("
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
20
|
+
it("tests a long user flow with tokens", async () => {
|
|
21
|
+
// The latest updated token
|
|
22
|
+
let token;
|
|
23
|
+
|
|
24
|
+
// REGISTER A NEW USER
|
|
25
|
+
token = await (async () => {
|
|
26
|
+
const register = await api.post("/auth/register/email", CREDENTIALS);
|
|
27
|
+
expect(register).toSucceed();
|
|
28
|
+
|
|
29
|
+
expect(await users()).toEqual(["abc@test.com"]);
|
|
30
|
+
expect(await sessions()).toEqual([register.data.token]);
|
|
31
|
+
return register.data.token;
|
|
32
|
+
})();
|
|
33
|
+
|
|
34
|
+
// CAN GET MY OWN INFO
|
|
35
|
+
await (async () => {
|
|
36
|
+
const headers = { authorization: "Bearer " + token };
|
|
37
|
+
const me = await api.get("/me", { headers });
|
|
38
|
+
expect(me).toSucceed();
|
|
39
|
+
expect(me.data.email).toEqual(EMAIL);
|
|
40
|
+
|
|
41
|
+
expect(await users()).toEqual(["abc@test.com"]);
|
|
42
|
+
expect(await sessions()).toEqual([token]);
|
|
43
|
+
})();
|
|
44
|
+
|
|
45
|
+
// LOGOUT TEST
|
|
46
|
+
await (async () => {
|
|
47
|
+
const headers = { authorization: "Bearer " + token };
|
|
48
|
+
const logout = await api.post("/auth/logout", {}, { headers });
|
|
49
|
+
expect(logout).toSucceed();
|
|
50
|
+
expect(await users()).toEqual(["abc@test.com"]);
|
|
51
|
+
expect(await sessions()).toEqual([]);
|
|
52
|
+
})();
|
|
53
|
+
|
|
54
|
+
// LOGIN FOR THE FIRST TIME
|
|
55
|
+
token = await (async () => {
|
|
56
|
+
const login = await api.post("/auth/login/email", CREDENTIALS);
|
|
57
|
+
expect(login).toSucceed();
|
|
58
|
+
expect(await users()).toEqual(["abc@test.com"]);
|
|
59
|
+
expect(await sessions()).toEqual([login.data.token]);
|
|
60
|
+
return login.data.token;
|
|
61
|
+
})();
|
|
62
|
+
|
|
63
|
+
// CAN GET MY OWN INFO
|
|
64
|
+
await (async () => {
|
|
65
|
+
const headers = { authorization: "Bearer " + token };
|
|
66
|
+
const me = await api.get("/me", { headers });
|
|
67
|
+
expect(me).toSucceed();
|
|
68
|
+
expect(me.data.email).toEqual(EMAIL);
|
|
69
|
+
})();
|
|
70
|
+
|
|
71
|
+
// UPDATE PASSWORD
|
|
72
|
+
await (async () => {
|
|
73
|
+
const headers = { authorization: "Bearer " + token };
|
|
74
|
+
const body = { previous: PASS, updated: "22222222" };
|
|
75
|
+
const update = await api.put("/auth/password/email", body, { headers });
|
|
76
|
+
expect(update).toSucceed();
|
|
77
|
+
expect(await users()).toEqual(["abc@test.com"]);
|
|
78
|
+
expect(await sessions()).toEqual([token]);
|
|
79
|
+
})();
|
|
80
|
+
|
|
81
|
+
// LOGOUT AGAIN
|
|
82
|
+
await (async () => {
|
|
83
|
+
const headers = { authorization: "Bearer " + token };
|
|
84
|
+
const logout = await api.post("/auth/logout", {}, { headers });
|
|
85
|
+
expect(logout).toSucceed();
|
|
86
|
+
expect(await users()).toEqual(["abc@test.com"]);
|
|
87
|
+
expect(await sessions()).toEqual([]);
|
|
88
|
+
})();
|
|
89
|
+
|
|
90
|
+
// LOGIN WITH OLD PASSWORD
|
|
91
|
+
await (async () => {
|
|
92
|
+
const login = await api.post("/auth/login/email", CREDENTIALS);
|
|
93
|
+
expect(login).not.toSucceed();
|
|
94
|
+
expect(await users()).toEqual(["abc@test.com"]);
|
|
95
|
+
})();
|
|
96
|
+
|
|
97
|
+
// LOGIN WITH NEW PASSWORD
|
|
98
|
+
token = await (async () => {
|
|
99
|
+
const login = await api.post("/auth/login/email", {
|
|
100
|
+
...CREDENTIALS,
|
|
101
|
+
password: "22222222",
|
|
102
|
+
});
|
|
103
|
+
expect(login).toSucceed();
|
|
104
|
+
expect(await users()).toEqual(["abc@test.com"]);
|
|
105
|
+
expect(await sessions()).toEqual([login.data.token]);
|
|
106
|
+
return login.data.token;
|
|
107
|
+
})();
|
|
41
108
|
});
|
|
42
109
|
});
|
package/src/auth/auth.js
CHANGED
|
@@ -48,5 +48,10 @@ export default async function findAuth(ctx) {
|
|
|
48
48
|
|
|
49
49
|
const auth = await options.session.get(sessionId);
|
|
50
50
|
if (!auth) throw ServerError.AUTH_NO_SESSION();
|
|
51
|
+
if (!auth.provider) throw ServerError.AUTH_NO_PROVIDER();
|
|
52
|
+
if (!options.provider.includes(auth.provider)) {
|
|
53
|
+
const valid = JSON.stringify(options.provider);
|
|
54
|
+
throw ServerError.AUTH_INVALID_PROVIDER({ provider: auth.provider, valid });
|
|
55
|
+
}
|
|
51
56
|
return auth;
|
|
52
57
|
}
|
package/src/auth/index.js
CHANGED
|
@@ -17,6 +17,8 @@ const middle = async (ctx) => {
|
|
|
17
17
|
if (ctx.options.auth.provider.includes("email")) {
|
|
18
18
|
ctx.app.post("/auth/register/email", providers.email.register);
|
|
19
19
|
ctx.app.post("/auth/login/email", providers.email.login);
|
|
20
|
+
ctx.app.put("/auth/password/email", providers.email.password);
|
|
21
|
+
ctx.app.put("/auth/reset/email", providers.email.password);
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
24
|
};
|
|
@@ -2,6 +2,8 @@ import argon2 from "argon2";
|
|
|
2
2
|
|
|
3
3
|
import { createId } from "../../helpers/index.js";
|
|
4
4
|
import { ServerError, status } from "../../index.js";
|
|
5
|
+
import findUser from "../findUser.js";
|
|
6
|
+
import updateUser from "../updateUser.js";
|
|
5
7
|
|
|
6
8
|
const createSession = async (user, ctx) => {
|
|
7
9
|
const { type, session, cleanUser } = ctx.options.auth;
|
|
@@ -70,4 +72,40 @@ async function register(ctx) {
|
|
|
70
72
|
return createSession(user, ctx);
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
|
|
75
|
+
async function reset(ctx) {
|
|
76
|
+
// const reset = ctx.options.store.prefix("reset:");
|
|
77
|
+
// // Already resetting
|
|
78
|
+
// if (ctx.body.token) {
|
|
79
|
+
// const { token, password } = ctx.body;
|
|
80
|
+
// const secret = sha256(token);
|
|
81
|
+
// const auth = await reset.get(secret);
|
|
82
|
+
// const user = await store.get(auth.email);
|
|
83
|
+
// } else {
|
|
84
|
+
// const email = ctx.body.email;
|
|
85
|
+
// const user = await store.get(email);
|
|
86
|
+
// const token = createId();
|
|
87
|
+
// const secret = sha256(token);
|
|
88
|
+
// await reset.set(secret, { email }, { expires: "2h" });
|
|
89
|
+
// //
|
|
90
|
+
// ctx.email.send(
|
|
91
|
+
// user.email,
|
|
92
|
+
// `Reset email: <a href="${domain}/auth/reset/email?token=${token}">`
|
|
93
|
+
// );
|
|
94
|
+
// }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function password(ctx) {
|
|
98
|
+
const { previous, updated } = ctx.body;
|
|
99
|
+
|
|
100
|
+
const fullUser = await findUser(ctx.auth, ctx.options.auth.store);
|
|
101
|
+
|
|
102
|
+
const isValid = await argon2.verify(fullUser.password, previous);
|
|
103
|
+
if (!isValid) throw ServerError.LOGIN_WRONG_PASSWORD();
|
|
104
|
+
|
|
105
|
+
fullUser.password = await argon2.hash(updated);
|
|
106
|
+
await updateUser(fullUser, ctx.auth, ctx.options.auth.store);
|
|
107
|
+
|
|
108
|
+
return 200;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export default { login, register, reset, password };
|
package/src/auth/user.js
CHANGED
|
@@ -1,21 +1,10 @@
|
|
|
1
|
-
import ServerError from "../ServerError";
|
|
2
|
-
|
|
3
|
-
const findUser = async (auth, options) => {
|
|
4
|
-
if (!auth.provider) throw ServerError.AUTH_NO_PROVIDER();
|
|
5
|
-
if (!options.provider.includes(auth.provider)) {
|
|
6
|
-
const valid = JSON.stringify(options.provider);
|
|
7
|
-
throw ServerError.AUTH_INVALID_PROVIDER({ provider: auth.provider, valid });
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
if (auth.provider === "email") {
|
|
11
|
-
return await options.store.get(auth.email);
|
|
12
|
-
}
|
|
13
|
-
};
|
|
1
|
+
import ServerError from "../ServerError.js";
|
|
2
|
+
import findUser from "./findUser.js";
|
|
14
3
|
|
|
15
4
|
export default async function user(ctx) {
|
|
16
5
|
if (!ctx.auth) return;
|
|
17
6
|
|
|
18
|
-
const user = await findUser(ctx.auth, ctx.options.auth);
|
|
7
|
+
const user = await findUser(ctx.auth, ctx.options.auth.store);
|
|
19
8
|
if (!user) throw ServerError.AUTH_NO_USER();
|
|
20
9
|
return ctx.options.auth.cleanUser(user);
|
|
21
10
|
}
|