protonfile-auth 1.4.0 → 1.6.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.
@@ -0,0 +1,9 @@
1
+ import { BaseEntity } from 'typeorm';
2
+ import { User } from './User';
3
+ export declare class OTP extends BaseEntity {
4
+ code: string;
5
+ scope: string;
6
+ expiration: number;
7
+ user_id: string;
8
+ user: User;
9
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.OTP = void 0;
10
+ const typeorm_1 = require("typeorm");
11
+ const User_1 = require("./User");
12
+ let OTP = class OTP extends typeorm_1.BaseEntity {
13
+ };
14
+ __decorate([
15
+ (0, typeorm_1.PrimaryColumn)({
16
+ name: 'code',
17
+ type: 'text',
18
+ default: () => 'getNumericId(100000, 999999)',
19
+ })
20
+ ], OTP.prototype, "code", void 0);
21
+ __decorate([
22
+ (0, typeorm_1.Column)('text')
23
+ ], OTP.prototype, "scope", void 0);
24
+ __decorate([
25
+ (0, typeorm_1.Column)({
26
+ type: 'bigint',
27
+ default: () => 'extract(epoch from now())::int + 600',
28
+ })
29
+ ], OTP.prototype, "expiration", void 0);
30
+ __decorate([
31
+ (0, typeorm_1.Column)({ name: 'user_id', type: 'text' })
32
+ ], OTP.prototype, "user_id", void 0);
33
+ __decorate([
34
+ (0, typeorm_1.ManyToOne)(() => User_1.User, { nullable: false }),
35
+ (0, typeorm_1.JoinColumn)({ name: 'user_id' })
36
+ ], OTP.prototype, "user", void 0);
37
+ OTP = __decorate([
38
+ (0, typeorm_1.Entity)()
39
+ ], OTP);
40
+ exports.OTP = OTP;
41
+ /*
42
+ CREATE OR REPLACE FUNCTION getNumericId(integer, integer) RETURNS integer
43
+ AS 'SELECT floor(random() * ($2-$1+1) + $1)::int'
44
+ LANGUAGE SQL
45
+ IMMUTABLE
46
+ RETURNS NULL ON NULL INPUT;
47
+ */
@@ -5,4 +5,5 @@ export declare class User extends BaseEntity {
5
5
  last_name: string;
6
6
  email: string;
7
7
  password: string;
8
+ email_verified: boolean;
8
9
  }
@@ -25,6 +25,9 @@ __decorate([
25
25
  __decorate([
26
26
  (0, typeorm_1.Column)('text')
27
27
  ], User.prototype, "password", void 0);
28
+ __decorate([
29
+ (0, typeorm_1.Column)({ name: 'email_verified', type: 'boolean', default: false })
30
+ ], User.prototype, "email_verified", void 0);
28
31
  User = __decorate([
29
32
  (0, typeorm_1.Entity)()
30
33
  ], User);
@@ -25,6 +25,8 @@ exports.default = (req, res) => __awaiter(void 0, void 0, void 0, function* () {
25
25
  if (!user) {
26
26
  return res.status(404).send('User not found');
27
27
  }
28
+ if (!user.email_verified)
29
+ return res.status(403).send('Email verification required');
28
30
  if (user && (yield bcryptjs_1.default.compare(password, user.password))) {
29
31
  // Create token
30
32
  const token = yield (0, auth_1.performLogin)(req, res, user);
@@ -28,7 +28,7 @@ exports.default = (req, res) => __awaiter(void 0, void 0, void 0, function* () {
28
28
  payload = jsonwebtoken_1.default.verify(token, process.env.REFRESH_TOKEN_KEY);
29
29
  decodedSession = (0, session_1.verifySessionToken)(JSON.parse(session).token);
30
30
  const parsedSession = JSON.parse(session);
31
- (0, session_1.compareSessionTokenVersion)(parsedSession.session_id, parsedSession.token);
31
+ yield (0, session_1.compareSessionTokenVersion)(parsedSession.session_id, parsedSession.token);
32
32
  const newSession = yield (0, session_1.bumpSessionToken)(parsedSession.session_id);
33
33
  (0, session_1.setSessionCookie)(res, JSON.stringify(newSession));
34
34
  }
@@ -13,9 +13,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const bcryptjs_1 = __importDefault(require("bcryptjs"));
16
- const auth_1 = require("../services/auth");
17
16
  const User_1 = require("../Entities/User");
18
17
  const mail_1 = require("../services/mail");
18
+ const OTP_1 = require("../Entities/OTP");
19
19
  exports.default = (req, res) => __awaiter(void 0, void 0, void 0, function* () {
20
20
  try {
21
21
  const { first_name, last_name, email, password } = req.body;
@@ -37,10 +37,20 @@ exports.default = (req, res) => __awaiter(void 0, void 0, void 0, function* () {
37
37
  if (!user) {
38
38
  return;
39
39
  }
40
- const token = (0, auth_1.createAccessToken)({ user_id: user.user_id });
41
- const mail = new mail_1.WelcomeMail(user);
40
+ const queryRes = yield OTP_1.OTP.insert({
41
+ scope: 'registration',
42
+ user_id: user.user_id,
43
+ });
44
+ const otp = queryRes.identifiers[0];
45
+ if (!otp || !otp.code) {
46
+ return res.sendStatus(500);
47
+ }
48
+ const mail = new mail_1.WelcomeMail(user, otp.code);
42
49
  mail.send();
43
- res.status(201).json(Object.assign(Object.assign({}, user), { token }));
50
+ const response = Object.assign({}, user);
51
+ // remove unnecessary properties
52
+ delete response.password;
53
+ res.status(201).json(response);
44
54
  }
45
55
  catch (err) {
46
56
  console.log(err);
@@ -0,0 +1,3 @@
1
+ import { Request, Response } from 'express';
2
+ declare const _default: (req: Request, res: Response) => Promise<Response<any, Record<string, any>> | undefined>;
3
+ export default _default;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const OTP_1 = require("../Entities/OTP");
13
+ const User_1 = require("../Entities/User");
14
+ exports.default = (req, res) => __awaiter(void 0, void 0, void 0, function* () {
15
+ try {
16
+ const { email, code } = req.query;
17
+ const user = yield User_1.User.findOne({ email });
18
+ if (!user)
19
+ return res.sendStatus(403);
20
+ const otp = yield OTP_1.OTP.findOne({ code, user_id: user.user_id });
21
+ if (!otp)
22
+ return res.sendStatus(404);
23
+ yield OTP_1.OTP.delete({ code: otp.code });
24
+ yield User_1.User.update({ user_id: user.user_id }, { email_verified: true });
25
+ res.sendStatus(200);
26
+ }
27
+ catch (err) {
28
+ console.log(err);
29
+ }
30
+ });
package/lib/index.d.ts CHANGED
@@ -7,6 +7,8 @@ import { Session } from './Entities/Session';
7
7
  import { TempToken } from './Entities/TempToken';
8
8
  import { TempTokenService } from './services/TempToken';
9
9
  import SessionCleaner from './services/SessionCleaner';
10
+ import { OTP } from './Entities/OTP';
11
+ import OTPVacuum from './services/OTPVacuum';
10
12
  declare const _default: {
11
13
  router: (typeormConfig: ConnectionOptions) => import("express-serve-static-core").Router;
12
14
  authMiddleware: (req: express.Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: express.Response<any, Record<string, any>>, next: express.NextFunction) => Promise<void | express.Response<any, Record<string, any>>>;
@@ -14,10 +16,12 @@ declare const _default: {
14
16
  User: typeof User;
15
17
  Session: typeof Session;
16
18
  TempToken: typeof TempToken;
19
+ OTP: typeof OTP;
17
20
  };
18
21
  services: {
19
22
  TempTokenService: typeof TempTokenService;
20
23
  SessionCleaner: typeof SessionCleaner;
24
+ OTPVacuum: typeof OTPVacuum;
21
25
  };
22
26
  };
23
27
  export default _default;
package/lib/index.js CHANGED
@@ -20,12 +20,21 @@ const Session_1 = require("./Entities/Session");
20
20
  const TempToken_1 = require("./Entities/TempToken");
21
21
  const TempToken_2 = require("./services/TempToken");
22
22
  const SessionCleaner_1 = __importDefault(require("./services/SessionCleaner"));
23
+ const OTP_1 = require("./Entities/OTP");
24
+ const OTPVacuum_1 = __importDefault(require("./services/OTPVacuum"));
25
+ const registerOtp_1 = __importDefault(require("./controllers/registerOtp"));
23
26
  exports.default = {
24
27
  router: (typeormConfig) => {
25
- (0, typeorm_1.createConnection)(Object.assign(Object.assign({}, typeormConfig), { entities: [User_1.User, Session_1.Session, TempToken_1.TempToken] }));
28
+ (0, typeorm_1.createConnection)(Object.assign(Object.assign({}, typeormConfig), { entities: [User_1.User, Session_1.Session, TempToken_1.TempToken, OTP_1.OTP] })).then((connection) => {
29
+ User_1.User.useConnection(connection);
30
+ Session_1.Session.useConnection(connection);
31
+ TempToken_1.TempToken.useConnection(connection);
32
+ OTP_1.OTP.useConnection(connection);
33
+ });
26
34
  const router = express_1.default.Router();
27
35
  router.use(body_parser_1.default.urlencoded({ extended: false }));
28
36
  router.post('/register', register_1.default);
37
+ router.post('/register/otp', registerOtp_1.default);
29
38
  router.post('/login', (0, cookie_parser_1.default)(), login_1.default);
30
39
  router.post('/refresh_token', (0, cookie_parser_1.default)(), refresh_token_1.default);
31
40
  router.post('/logout', (0, cookie_parser_1.default)(), logout_1.default);
@@ -36,6 +45,6 @@ exports.default = {
36
45
  return router;
37
46
  },
38
47
  authMiddleware: verifyToken_1.default,
39
- entities: { User: User_1.User, Session: Session_1.Session, TempToken: TempToken_1.TempToken },
40
- services: { TempTokenService: TempToken_2.TempTokenService, SessionCleaner: SessionCleaner_1.default },
48
+ entities: { User: User_1.User, Session: Session_1.Session, TempToken: TempToken_1.TempToken, OTP: OTP_1.OTP },
49
+ services: { TempTokenService: TempToken_2.TempTokenService, SessionCleaner: SessionCleaner_1.default, OTPVacuum: OTPVacuum_1.default },
41
50
  };
@@ -0,0 +1,7 @@
1
+ declare class OTPVacuum {
2
+ private handle?;
3
+ constructor();
4
+ register(interval?: number): void;
5
+ unregister(): void;
6
+ }
7
+ export default OTPVacuum;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ // removes expired OTP codes
3
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
+ return new (P || (P = Promise))(function (resolve, reject) {
6
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
10
+ });
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ const OTP_1 = require("../Entities/OTP");
14
+ const typeorm_1 = require("typeorm");
15
+ class OTPVacuum {
16
+ constructor() { }
17
+ register(interval) {
18
+ this.handle = setInterval(() => __awaiter(this, void 0, void 0, function* () {
19
+ const res = yield OTP_1.OTP.query('select extract(epoch from now())::int as timestamp');
20
+ if (!res[0] || !res[0].timestamp)
21
+ return;
22
+ OTP_1.OTP.delete({ expiration: (0, typeorm_1.LessThan)(res[0].timestamp) });
23
+ }), interval || 60000);
24
+ }
25
+ unregister() {
26
+ if (!this.handle)
27
+ return;
28
+ clearInterval(this.handle);
29
+ }
30
+ }
31
+ exports.default = OTPVacuum;
@@ -28,7 +28,7 @@ export declare class NewAccessMail extends BaseMail {
28
28
  constructor(user: User, location: string);
29
29
  }
30
30
  export declare class WelcomeMail extends BaseMail {
31
- constructor(user: User);
31
+ constructor(user: User, otp: string);
32
32
  }
33
33
  export declare class ChangePasswordMail extends BaseMail {
34
34
  constructor(user: User, link: string);
@@ -67,11 +67,12 @@ class NewAccessMail extends BaseMail {
67
67
  }
68
68
  exports.NewAccessMail = NewAccessMail;
69
69
  class WelcomeMail extends BaseMail {
70
- constructor(user) {
70
+ constructor(user, otp) {
71
71
  const _template = handlebars_1.default.compile(default_1.default);
72
72
  const mailBody = _template({
73
73
  email: user.email,
74
- body: 'Welcome to Protonfile, we hope you enjoy using our service',
74
+ body: `Welcome to Protonfile, we hope you enjoy using our service.
75
+ Use this OTP code to complete your registration: ${otp}.`,
75
76
  });
76
77
  const mailOptions = {
77
78
  subject: 'Welcome to Protonfile',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "protonfile-auth",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "protonfile-auth",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",