Files
notesnook/packages/core/api/user-manager.js

244 lines
6.4 KiB
JavaScript
Raw Normal View History

2020-12-16 12:06:25 +05:00
import http from "../utils/http";
import constants from "../utils/constants";
import TokenManager from "./token-manager";
2021-01-18 23:29:49 +05:00
import { EV, NOTESNOOK_CORE_VERSION } from "../common";
2020-12-16 12:06:25 +05:00
const ENDPOINTS = {
signup: "/users",
token: "/connect/token",
user: "/users",
deleteUser: "/users/delete",
patchUser: "/account",
verifyUser: "/account/verify",
2020-12-16 12:06:25 +05:00
revoke: "/connect/revocation",
2020-12-22 09:36:35 +05:00
recoverAccount: "/account/recover",
2021-01-19 22:27:13 +05:00
migrateStatus: "/migrate/status",
2021-01-18 23:29:49 +05:00
hcliMigrate: "/migrate/hcli",
2020-12-16 12:06:25 +05:00
};
class UserManager {
/**
*
* @param {import("./index").default} db
*/
constructor(db) {
this._db = db;
this.tokenManager = new TokenManager(db);
}
2020-12-22 16:13:01 +05:00
async init() {
const user = await this.getUser();
if (!user) return;
if (!user.remember) await this.logout(true, "Session expired.");
}
2020-12-16 12:06:25 +05:00
async signup(email, password) {
2021-01-18 23:29:49 +05:00
const hashedPassword = await this._db.context.hash(password, email);
2020-12-16 12:06:25 +05:00
await http.post(`${constants.API_HOST}${ENDPOINTS.signup}`, {
email,
2021-01-18 23:29:49 +05:00
password: hashedPassword,
client_id: "notesnook",
2020-12-16 12:06:25 +05:00
});
return await this.login(email, password, true, hashedPassword);
2021-01-18 23:29:49 +05:00
}
async _getUserStatus(email) {
2021-01-19 22:27:13 +05:00
return await http.post(`${constants.AUTH_HOST}${ENDPOINTS.migrateStatus}`, {
email,
});
2021-01-18 23:29:49 +05:00
}
async _hcliMigrate(email, plaintextPassword, hashedPassword) {
return await http.post(`${constants.AUTH_HOST}${ENDPOINTS.hcliMigrate}`, {
username: email,
plaintext_password: plaintextPassword,
hashed_password: hashedPassword,
});
2020-12-16 12:06:25 +05:00
}
2021-01-18 23:29:49 +05:00
async _migrateUser(email, plaintextPassword) {
const status = await this._getUserStatus(email);
var hashedPassword = await this._db.context.hash(plaintextPassword, email);
if (!status.hcli) {
await this._hcliMigrate(email, plaintextPassword, hashedPassword);
}
return hashedPassword;
}
async login(email, password, remember, hashedPassword) {
if (!hashedPassword) {
hashedPassword = await this._migrateUser(email, password);
2021-01-18 23:29:49 +05:00
}
2020-12-16 12:06:25 +05:00
await this.tokenManager.saveToken(
await http.post(`${constants.AUTH_HOST}${ENDPOINTS.token}`, {
username: email,
password: hashedPassword,
2020-12-16 12:06:25 +05:00
grant_type: "password",
scope: "notesnook.sync offline_access openid IdentityServerApi",
client_id: "notesnook",
})
);
const user = await this.fetchUser(remember);
await this._db.context.deriveCryptoKey(`_uk_@${user.email}`, {
password,
salt: user.salt,
});
EV.publish("user:loggedIn", user);
}
async getSessions() {
const token = await this.tokenManager.getAccessToken();
if (!token) return;
await http.get(`${constants.AUTH_HOST}/account/sessions`, token);
}
async logout(revoke = true, reason) {
2020-12-22 16:13:01 +05:00
try {
if (revoke) await this.tokenManager.revokeToken();
} catch (e) {
console.error(e);
} finally {
await this._db.context.clear();
EV.publish("user:loggedOut", reason);
}
2020-12-16 12:06:25 +05:00
}
setUser(user) {
if (!user) return;
return this._db.context.write("user", user);
}
getUser() {
return this._db.context.read("user");
}
async deleteUser(password) {
let token = await this.tokenManager.getAccessToken();
if (!token) return;
const user = await this.getUser();
2020-12-16 12:06:25 +05:00
await http.post(
`${constants.API_HOST}${ENDPOINTS.deleteUser}`,
{ password: await this._db.context.hash(password, user.email) },
2020-12-16 12:06:25 +05:00
token
);
await this.logout(false, "Account deleted.");
return true;
}
2020-12-22 16:13:01 +05:00
async fetchUser(remember = false) {
2020-12-16 12:06:25 +05:00
try {
let token = await this.tokenManager.getAccessToken();
if (!token) return;
const user = await http.get(
`${constants.API_HOST}${ENDPOINTS.user}`,
token
);
if (user) {
const oldUser = await this.getUser();
if (!!oldUser && !oldUser.isEmailConfirmed && user.isEmailConfirmed) {
// generate new token
const token = await this.tokenManager.getToken(false);
await this.tokenManager._refreshToken(token);
}
2020-12-16 12:06:25 +05:00
await this.setUser({ ...user, remember });
EV.publish("user:fetched", user);
return user;
}
} catch (e) {
if (e.message === "invalid_grant") {
await this.logout(
false,
"You were logged out. Either your session expired or your account was deleted. Please try logging in again."
);
} else {
return await this.getUser();
2020-12-16 12:06:25 +05:00
}
}
}
2020-12-22 13:13:18 +05:00
changePassword(oldPassword, newPassword) {
return this._updatePassword("change_password", {
old_password: oldPassword,
new_password: newPassword,
});
}
resetPassword(newPassword) {
return this._updatePassword("reset_password", {
new_password: newPassword,
});
}
2020-12-16 12:06:25 +05:00
async getEncryptionKey() {
const user = await this.getUser();
if (!user) return;
const key = await this._db.context.getCryptoKey(`_uk_@${user.email}`);
return { key, salt: user.salt };
}
2020-12-22 13:13:18 +05:00
async sendVerificationEmail() {
let token = await this.tokenManager.getAccessToken();
if (!token) return;
await http.post(
`${constants.AUTH_HOST}${ENDPOINTS.verifyUser}`,
null,
token
);
}
recoverAccount(email) {
return http.post(`${constants.AUTH_HOST}${ENDPOINTS.recoverAccount}`, {
email,
client_id: "notesnook",
});
}
async _updatePassword(type, data) {
2020-12-16 12:06:25 +05:00
let token = await this.tokenManager.getAccessToken();
if (!token) return;
// we hash the passwords beforehand
const { email } = await this.getUser();
var hashedData = {};
if (data.old_password)
hashedData.old_password = await this._db.context.hash(
data.old_password,
email
);
if (data.new_password)
hashedData.new_password = await this._db.context.hash(
data.new_password,
email
);
2020-12-16 12:06:25 +05:00
await http.patch(
2020-12-22 14:18:53 +05:00
`${constants.AUTH_HOST}${ENDPOINTS.patchUser}`,
2020-12-16 12:06:25 +05:00
{
2020-12-22 13:13:18 +05:00
type,
...hashedData,
2020-12-16 12:06:25 +05:00
},
token
);
2020-12-22 14:18:53 +05:00
await this._db.outbox.add(
type,
2020-12-22 14:25:01 +05:00
{ newPassword: data.new_password },
2020-12-22 14:18:53 +05:00
async () => {
const key = await this.getEncryptionKey();
const { email } = await this.getUser();
await this._db.context.deriveCryptoKey(`_uk_@${email}`, {
2020-12-22 14:25:01 +05:00
password: data.new_password,
2020-12-22 14:18:53 +05:00
salt: key.salt,
});
2020-12-22 16:13:01 +05:00
await this._db.sync(true, true);
2020-12-22 14:18:53 +05:00
}
);
2020-12-16 12:06:25 +05:00
return true;
}
}
export default UserManager;