notherbase-fs 4.1.4 → 4.2.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.
@@ -11,6 +11,8 @@ export default class User {
11
11
 
12
12
  this.router.post("/logout", this.logout);
13
13
  this.router.post("/changePassword", this.changePassword);
14
+ this.router.post("/sendOTP", this.sendOneTimePassword);
15
+ this.router.post("/changeEmail", this.changeEmail);
14
16
  this.router.post("/register", this.register);
15
17
  this.router.post("/login", this.login);
16
18
  this.router.post("/deletePermanently", this.deletePermanently);
@@ -20,6 +22,7 @@ export default class User {
20
22
  this.router.post("/downloadData", this.downloadData);
21
23
  this.router.post("/deleteAlldata", this.deleteAlldata);
22
24
  this.router.post("/importData", this.importData);
25
+
23
26
  }
24
27
 
25
28
  /**
@@ -28,9 +31,13 @@ export default class User {
28
31
  * @param {Object} res An Express.js response.
29
32
  */
30
33
  logout = async (req, res) => {
31
- await req.session.destroy();
34
+ if (loginCheck(req, res)) {
35
+ delete req.user.memory?.data?.sessions[req.session.id];
36
+ await req.user.commit();
37
+ await req.session?.destroy();
32
38
 
33
- success(res, "Logged out.");
39
+ success(res, "Logged out.");
40
+ }
34
41
  }
35
42
 
36
43
  /**
@@ -65,6 +72,86 @@ export default class User {
65
72
  }
66
73
  }
67
74
 
75
+ validatePassword = async (req, password, user) => {
76
+ if (password && user?.memory?.data?.otp) {
77
+ if (password == user.memory.data.otp.code) {
78
+ if (Date.now() < user.memory.data.otp.expires) {
79
+ user.memory.data.otp.expires = 0;
80
+ await user.commit();
81
+ return "Authenticated.";
82
+ }
83
+ else return "One-time password expired.";
84
+ }
85
+ else {
86
+ let passResult = await bcrypt.compare(req.body.password, user.memory.data.password);
87
+ if (passResult) return "Authenticated.";
88
+ else return "Password doesn't match the username.";
89
+ }
90
+ }
91
+ else return "Password error.";
92
+ }
93
+
94
+ /**
95
+ * Change a user's email.
96
+ * @param {Object} req An Express.js request.
97
+ * @param {Object} res An Express.js response.
98
+ */
99
+ changeEmail = async (req, res) => {
100
+ if (loginCheck(req, res)) {
101
+ let spirit = await req.db.Spirit.recallOne("user", null, { username: req.session.currentUser });
102
+
103
+ if (check(res, spirit, "User not found!") &&
104
+ check(res, req.body.email, "New email must be provided."))
105
+ {
106
+ let result = await this.validatePassword(req, req.body.password, spirit);
107
+ if (result == "Authenticated.") {
108
+ let other = await req.db.Spirit.recallOne("user", null, { email: req.body.email });
109
+
110
+ if (check(res, !other, "Email already in use!")) {
111
+ spirit.addBackup({
112
+ ...spirit.memory.data,
113
+ email: req.body.email
114
+ });
115
+
116
+ await spirit.commit();
117
+
118
+ success(res, "Email changed successfully!");
119
+ }
120
+ }
121
+ else fail(res, result);
122
+ }
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Send a one-time password to the user's email.
128
+ * @param {Object} req An Express.js request.
129
+ * @param {Object} res An Express.js response.
130
+ */
131
+ sendOneTimePassword = async (req, res) => {
132
+ if (loginCheck(req, res)) {
133
+ let spirit = await req.db.Spirit.recallOne("user", null, { username: req.session.currentUser });
134
+
135
+ if (check(res, spirit, "User not found!")) {
136
+ let otp = Math.floor(100000 + Math.random() * 900000);
137
+ spirit.memory.data.otp = {
138
+ code: otp,
139
+ expires: Date.now() + 1000 * 60 * 15
140
+ }
141
+
142
+ await spirit.commit();
143
+
144
+ await req.db.SendMail.send(spirit.memory.data.email, 'One Time Password for NotherBase',
145
+ `<h1>Your One-Time Password:<h1>
146
+ <h2>${otp}<h2>
147
+ <p>Visit <a href="https://www.notherbase.com/the-front/keeper">notherbase.com/the-front/keeper</a> to use your one-time password.</p>
148
+ <p>This one-time password expires in 15 minutes.<p>`);
149
+
150
+ success(res, "One-time password sent.");
151
+ }
152
+ }
153
+ }
154
+
68
155
  /**
69
156
  * Register a new user account.
70
157
  * @param {Object} req An Express.js request.
@@ -84,7 +171,13 @@ export default class User {
84
171
  username: req.body.username,
85
172
  password: hash,
86
173
  authLevels: [ "Basic" ],
87
- view: "compact"
174
+ view: "compact",
175
+ email: "",
176
+ otp: {
177
+ code: "",
178
+ expires: 0
179
+ },
180
+ sessions: []
88
181
  });
89
182
 
90
183
  success(res, "Registration successful!");
@@ -99,15 +192,30 @@ export default class User {
99
192
  */
100
193
  login = async (req, res) => {
101
194
  let spirit = await req.db.Spirit.recallOne("user", null, { username: req.body.username });
102
-
103
195
  if (check(res, spirit, "User not found.")) {
104
- let passResult = await bcrypt.compare(req.body.password, spirit.memory.data.password);
196
+ spirit.memory.data = {
197
+ username: "",
198
+ password: "",
199
+ authLevels: [ "Basic" ],
200
+ view: "compact",
201
+ email: "",
202
+ otp: {
203
+ code: "",
204
+ expires: 0
205
+ },
206
+ sessions: {},
207
+ ...spirit.memory.data
208
+ }
209
+ await spirit.commit();
105
210
 
106
- if (check(res, passResult, "Password doesn't match the username.")) {
211
+ let result = await this.validatePassword(req, req.body.password, spirit);
212
+ if (result === "Authenticated.") {
107
213
  req.session.currentUser = req.body.username;
108
-
109
- success(res, "Logged in.", req.body.username);
214
+ spirit.memory.data.sessions[req.session.id] = Date.now() + 1000 * 60 * 60 * 24 * 28; // 28 days
215
+ await spirit.commit();
216
+ success(res, "Login successful!", req.body.username);
110
217
  }
218
+ else fail(res, result);
111
219
  }
112
220
  }
113
221
 
@@ -7,19 +7,6 @@ const OAuth2 = google.auth.OAuth2;
7
7
  const OAuth2Client = new OAuth2(process.env.CLIENTID, process.env.CLIENTSECRET);
8
8
  OAuth2Client.setCredentials({ refresh_token: process.env.CLIENTREFRESH });
9
9
 
10
-
11
- /**
12
- * Sends an email with a password reset code. WIP
13
- * @param {String} toEmail Email address to send to.
14
- * @param {Number} resetToken Token to reset by.
15
- */
16
- const passwordReset = async (toEmail, resetToken) => {
17
- return await send(toEmail, 'Password Reset for NotherBase',
18
- `<h1>Your One-Time Password Reset Code:<h1>
19
- <h2>${resetToken}<h2>
20
- <p>Visit <a href="https://www.notherbase.com/the-front/keeper">notherbase.com/the-front/keeper</a> to finish changing your password.</p>`);
21
- };
22
-
23
10
  /**
24
11
  * Sends an email. WIP
25
12
  * @param {String} toEmail Email Address to send to.
@@ -56,4 +43,4 @@ const send = async (toEmail, subject, html, name = "NotherBase") => {
56
43
  return sent;
57
44
  }
58
45
 
59
- export default { passwordReset, send };
46
+ export default { send };
package/notherbase-fs.js CHANGED
@@ -81,8 +81,8 @@ class NotherBaseFS {
81
81
  //enable cookies
82
82
  this.app.use((req, res, next) => {
83
83
  this.bases[req.hosting].session(req, res, next);
84
- });
85
-
84
+ });
85
+
86
86
  // allows us to get static files like css
87
87
  this.app.use((req, res, next) => {
88
88
  this.bases[req.hosting].static(req, res, next);
@@ -90,15 +90,32 @@ class NotherBaseFS {
90
90
  this.app.use(express.static(`${__dirname}/public`));
91
91
 
92
92
  //provide database access and etc to use in routes
93
- this.app.use((req, res, next) => {
93
+ this.app.use(async (req, res, next) => {
94
94
  req.globals = globals;
95
95
  req.db = Models;
96
+ req.user = req.session?.currentUser ? await req.db.Spirit.recallOne("user", null, { username: req.session.currentUser }) : null;
96
97
  req.lock = false;
97
98
  // enables sessions only if the protocol is https and we are in production, or if we are in development
98
99
  req.sessionsEnabled = (process.env.PRODUCTION == "true" && req.secure) || process.env.PRODUCTION == "false";
99
100
  next();
100
101
  });
101
102
 
103
+ //destroy session if it is not authorized
104
+ this.app.use(async (req, res, next) => {
105
+ if (req.session.currentUser) {
106
+ if (req.user?.memory?.data?.sessions?.[req.session.id]) {
107
+ if (req.user.memory.data.sessions[req.session.id] < Date.now()) {
108
+ req.session.regenerate(() => {});
109
+ delete req.user.memory.data.sessions[req.session.id];
110
+ await req.user.commit();
111
+ }
112
+ }
113
+ else req.session.regenerate(() => {});
114
+ }
115
+
116
+ next();
117
+ });
118
+
102
119
  //spirit world routes
103
120
  this.app.use("/s", this.spiritWorld.router);
104
121
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "notherbase-fs",
3
- "version": "4.1.4",
3
+ "version": "4.2.1",
4
4
  "description": "Functions to help make developing for NotherBase easier.",
5
5
  "exports": "./notherbase-fs.js",
6
6
  "scripts": {
package/public/js/base.js CHANGED
@@ -220,4 +220,16 @@ class Base {
220
220
  let response = await Base.commune("importData", { password, data: text });
221
221
  return response;
222
222
  }
223
+
224
+ sendOTP = async (email) => {
225
+ let response = await Base.commune("sendOTP");
226
+
227
+ return response;
228
+ }
229
+
230
+ changeEmail = async (password, email) => {
231
+ let response = await Base.commune("changeEmail", { password, email });
232
+
233
+ return response;
234
+ }
223
235
  }
@@ -2,7 +2,7 @@ export default async (req, user, io) => {
2
2
  if (user) {
3
3
  let spirit = await req.db.Spirit.recallOne("test-save3", user.memory._id);
4
4
 
5
- return spirit.memory.data.text;
5
+ return spirit?.memory?.data?.text;
6
6
  }
7
7
 
8
8
  return "Not Logged In";
@@ -25,6 +25,9 @@
25
25
  <input type="file" id="fileInput">
26
26
  <!-- password input -->
27
27
  <input type="test" id="password" placeholder="pass">
28
+ <button onclick="sendOTP()">Get OTP</button>
29
+ <input type="text" id="email">
30
+ <button onclick="changeEmail()">Change Email</button>
28
31
 
29
32
  <hr>
30
33
 
@@ -114,4 +117,16 @@
114
117
  let res = await base.importData(pass, file);
115
118
  console.log(res.message);
116
119
  }
120
+
121
+ let sendOTP = async () => {
122
+ let res = await base.sendOTP();
123
+ console.log(res.message);
124
+ }
125
+
126
+ let changeEmail = async () => {
127
+ let pass = $('#password').val();
128
+ let email = $('#email').val();
129
+ let res = await base.changeEmail(pass, email);
130
+ console.log(res.message);
131
+ }
117
132
  </script>