@social-mail/social-mail-web-server 1.4.829 → 1.4.830

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.
Files changed (30) hide show
  1. package/dist/server/model/entities/LoginSession.d.ts +3 -2
  2. package/dist/server/model/entities/LoginSession.d.ts.map +1 -1
  3. package/dist/server/model/entities/LoginSession.js +8 -16
  4. package/dist/server/model/entities/LoginSession.js.map +1 -1
  5. package/dist/server/model/entities/UserAuthFactor.d.ts +3 -0
  6. package/dist/server/model/entities/UserAuthFactor.d.ts.map +1 -1
  7. package/dist/server/model/entities/UserAuthFactor.js +9 -0
  8. package/dist/server/model/entities/UserAuthFactor.js.map +1 -1
  9. package/dist/server/model/entities/VerificationToken.d.ts +6 -0
  10. package/dist/server/model/entities/VerificationToken.d.ts.map +1 -1
  11. package/dist/server/model/entities/VerificationToken.js +9 -0
  12. package/dist/server/model/entities/VerificationToken.js.map +1 -1
  13. package/dist/server/model/events/LoginSessionEvents.d.ts.map +1 -1
  14. package/dist/server/model/events/LoginSessionEvents.js +5 -6
  15. package/dist/server/model/events/LoginSessionEvents.js.map +1 -1
  16. package/dist/server/model/events/UserAuthFactorEvents.d.ts.map +1 -1
  17. package/dist/server/model/events/UserAuthFactorEvents.js +5 -6
  18. package/dist/server/model/events/UserAuthFactorEvents.js.map +1 -1
  19. package/dist/server/services/external-otp-in/ExternalOtpInService.d.ts +6 -2
  20. package/dist/server/services/external-otp-in/ExternalOtpInService.d.ts.map +1 -1
  21. package/dist/server/services/external-otp-in/ExternalOtpInService.js +33 -5
  22. package/dist/server/services/external-otp-in/ExternalOtpInService.js.map +1 -1
  23. package/dist/tsconfig.tsbuildinfo +1 -1
  24. package/package.json +1 -1
  25. package/src/server/model/entities/LoginSession.ts +9 -14
  26. package/src/server/model/entities/UserAuthFactor.ts +10 -0
  27. package/src/server/model/entities/VerificationToken.ts +10 -5
  28. package/src/server/model/events/LoginSessionEvents.ts +5 -6
  29. package/src/server/model/events/UserAuthFactorEvents.ts +5 -6
  30. package/src/server/services/external-otp-in/ExternalOtpInService.ts +34 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@social-mail/social-mail-web-server",
3
- "version": "1.4.829",
3
+ "version": "1.4.830",
4
4
  "description": "## Phase 1",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -7,6 +7,7 @@ import type { AppFileVersion } from "./AppFile.js";
7
7
  import Index from "@entity-access/entity-access/dist/decorators/Index.js";
8
8
  import GraphService from "@entity-access/server-pages/dist/services/GraphService.js";
9
9
  import { IgnoreJsonProperty, JsonProperty } from "@entity-access/server-pages/dist/services/ModelService.js";
10
+ import VerificationToken from "./VerificationToken.js";
10
11
 
11
12
  const loginSessionStatuses = ["created", "change-password", "totp"] as const;
12
13
  export type loginStatusType = typeof loginSessionStatuses[number];
@@ -20,15 +21,6 @@ export type loginStatusType = typeof loginSessionStatuses[number];
20
21
  ],
21
22
  filter: (x) => x.invalid === false && x.deviceTokenType !== null
22
23
  })
23
- @Index({
24
- name: "IX_LoginSessions_Phone_Codes",
25
- columns: [
26
- { name: (x) => x.userID, descending: false },
27
- { name: (x) => x.smsToken, descending: false }
28
- ],
29
- unique: true,
30
- filter: (x) => x.smsToken !== null
31
- })
32
24
  export default class LoginSession {
33
25
 
34
26
  @Column({ key: true, generated: "identity"})
@@ -64,8 +56,12 @@ export default class LoginSession {
64
56
  @Column({ dataType: "AsciiChar", length: 50, nullable: true})
65
57
  public deviceTokenType: string;
66
58
 
67
- @Column({ dataType: "Char", length: 50, nullable: true })
68
- public smsToken: string;
59
+ @Column({ dataType: "BigInt", nullable: true })
60
+ @RelateTo(VerificationToken, {
61
+ property: (x) => x.verificationToken,
62
+ inverseProperty: (x) => x.loginSessions
63
+ })
64
+ public verificationID: number;
69
65
 
70
66
  /**
71
67
  * This will not be saved into database
@@ -98,13 +94,12 @@ export default class LoginSession {
98
94
  @JsonProperty({ help: "One Time Password"})
99
95
  public oneTimePassword: string;
100
96
 
101
- @JsonProperty({ help: "SMS URL to Display"})
102
- public smsConfig: any;
103
-
104
97
  public user: User;
105
98
 
106
99
  public versions: AppFileVersion[];
107
100
 
101
+ public verificationToken: VerificationToken;
102
+
108
103
  [GraphService.appendToGraph]() {
109
104
  delete this.deviceToken;
110
105
  delete this.deviceTokenType;
@@ -8,6 +8,7 @@ import IncomingSmsSource from "./IncomingSmsSource.js";
8
8
  import CheckConstraint from "@entity-access/entity-access/dist/decorators/CheckConstraint.js";
9
9
  import Sql from "@entity-access/entity-access/dist/sql/Sql.js";
10
10
  import Index from "@entity-access/entity-access/dist/decorators/Index.js";
11
+ import VerificationToken from "./VerificationToken.js";
11
12
 
12
13
  const authMethods = ["totp", "hotp", "fido2", "one-time", "otp", "otp-in"] as const;
13
14
  export type authMethodTypes = typeof authMethods[number];
@@ -50,6 +51,13 @@ export default class UserAuthFactor {
50
51
  @Column({ dataType: "Char", length: 20, nullable: true })
51
52
  isdCode: string;
52
53
 
54
+ @Column({ dataType: "BigInt", nullable: true })
55
+ @RelateTo(VerificationToken, {
56
+ property: (x) => x.verificationToken,
57
+ inverseProperty: (x) => x.authFactors
58
+ })
59
+ public verificationID: number;
60
+
53
61
  @Column({ dataType: "Boolean"})
54
62
  verified: boolean;
55
63
 
@@ -70,4 +78,6 @@ export default class UserAuthFactor {
70
78
  @JsonProperty()
71
79
  smsConfig: string;
72
80
 
81
+ public verificationToken: VerificationToken;
82
+
73
83
  }
@@ -1,13 +1,11 @@
1
1
  import Column from "@entity-access/entity-access/dist/decorators/Column.js";
2
- import Index from "@entity-access/entity-access/dist/decorators/Index.js";
3
2
  import Table from "@entity-access/entity-access/dist/decorators/Table.js";
4
- import { ServiceProvider } from "@entity-access/entity-access/dist/di/di.js";
5
3
  import Sql from "@entity-access/entity-access/dist/sql/Sql.js";
6
4
  import DateTime from "@entity-access/entity-access/dist/types/DateTime.js";
7
- import GraphService from "@entity-access/server-pages/dist/services/GraphService.js";
8
- import SignerService from "../../services/SignerService.js";
9
- import Page from "@entity-access/server-pages/dist/Page.js";
10
5
  import { RelateTo } from "@entity-access/entity-access/dist/decorators/Relate.js";
6
+ import type LoginSession from "./LoginSession.js";
7
+ import { JsonProperty } from "@entity-access/server-pages/dist/services/ModelService.js";
8
+ import type UserAuthFactor from "./UserAuthFactor.js";
11
9
 
12
10
  @Table("VerificationTokens")
13
11
  export default class VerificationToken {
@@ -15,6 +13,9 @@ export default class VerificationToken {
15
13
  @Column({ dataType: "BigInt", key: true, generated: "identity"})
16
14
  verificationID: number;
17
15
 
16
+ @Column({ dataType: "Char", length: 200, nullable: true })
17
+ externalID: string;
18
+
18
19
  @Column({ default: () => Sql.date.now()})
19
20
  dateCreated: DateTime;
20
21
 
@@ -33,7 +34,11 @@ export default class VerificationToken {
33
34
  @Column({ dataType: "Char", length: 50, nullable: true })
34
35
  ipAddress: string;
35
36
  addresses: VerificationTokenAddress[];
37
+ loginSessions: LoginSession[];
38
+ authFactors: UserAuthFactor[];
36
39
 
40
+ @JsonProperty({})
41
+ smsConfig: any;
37
42
  }
38
43
 
39
44
  @Table("VerificationTokenAddresses")
@@ -138,9 +138,9 @@ export default class LoginSessionEvents extends EntityEvents<LoginSession> {
138
138
 
139
139
  if (otpIn.length) {
140
140
  const idsCodes = otpIn.map((x) => x.phoneNumber );
141
- const smsConfig = await ServiceProvider.resolve(this, ExternalOtpInService).create(idsCodes);
142
- entity.smsConfig = smsConfig;
143
- entity.smsToken = smsConfig.id + ":" + smsConfig.accessCode;
141
+ const v = await ServiceProvider.resolve(this, ExternalOtpInService).create(idsCodes);
142
+ entity.verificationID = v.verificationID;
143
+ entity.verificationToken = v;
144
144
  }
145
145
 
146
146
  return;
@@ -189,10 +189,9 @@ export default class LoginSessionEvents extends EntityEvents<LoginSession> {
189
189
  async beforeUpdate(entity: LoginSession, entry: ChangeEntry<LoginSession>) {
190
190
 
191
191
  if (entry.isModified("dateUpdated")) {
192
- if(entity.smsToken) {
193
- const [id, accessCode] = entity.smsToken.split(":");
192
+ if(entity.verificationID) {
194
193
  const eis = ServiceProvider.resolve(this, ExternalOtpInService);
195
- const { success } = await eis.get(id, accessCode);
194
+ const { success } = await eis.get(entity.verificationID);
196
195
  if (success) {
197
196
  entity.status = "created";
198
197
  entity.invalid = false;
@@ -58,10 +58,9 @@ export default class UserAuthFactorEvents extends AuthenticatedEvents<UserAuthFa
58
58
 
59
59
  const phones = await db.userAuthFactors.where(entity, (p) => (x) => x.userID === p.userID && x.method === "otp-in").toArray();
60
60
  const phoneNumbers = phones.map((x) => x.phoneNumber);
61
- const smsConfig = await ServiceProvider.resolve(this, ExternalOtpInService).create(phoneNumbers);
62
- entity.smsConfig = JSON.stringify(smsConfig);
63
- entity.secret = smsConfig.accessCode;
64
- entity.hint = smsConfig.id;
61
+ const v = await ServiceProvider.resolve(this, ExternalOtpInService).create(phoneNumbers);
62
+ entity.verificationID = v.verificationID;
63
+ entity.verificationToken = v;
65
64
  return;
66
65
  }
67
66
 
@@ -102,10 +101,10 @@ export default class UserAuthFactorEvents extends AuthenticatedEvents<UserAuthFa
102
101
  }
103
102
  if (!entity.verified) {
104
103
 
105
- if (!entity.token) {
104
+ if (entity.verificationID) {
106
105
 
107
106
  const eois = ServiceProvider.resolve(this, ExternalOtpInService);
108
- const result = await eois.get(entity.hint, entity.secret);
107
+ const result = await eois.get(entity.verificationID);
109
108
  if (result.success) {
110
109
  entity.verified = true;
111
110
  }
@@ -1,5 +1,9 @@
1
- import { RegisterSingleton } from "@entity-access/entity-access/dist/di/di.js";
1
+ import Inject, { RegisterScoped, RegisterSingleton } from "@entity-access/entity-access/dist/di/di.js";
2
2
  import { globalEnv } from "../../../common/globalEnv.js";
3
+ import SocialMailContext from "../../model/SocialMailContext.js";
4
+ import CryptoService from "../CryptoService.js";
5
+ import { randomUUID } from "crypto";
6
+ import DateTime from "@entity-access/entity-access/dist/types/DateTime.js";
3
7
 
4
8
  const headers = {
5
9
  authorization: `basic ${Buffer.from(`${globalEnv.otpIn.key}:${globalEnv.otpIn.secret}`).toString("base64")}`
@@ -10,9 +14,15 @@ const postHeaders = {
10
14
  "content-type": "application/json",
11
15
  };
12
16
 
13
- @RegisterSingleton
17
+ @RegisterScoped
14
18
  export default class ExternalOtpInService {
15
19
 
20
+ @Inject
21
+ db: SocialMailContext;
22
+
23
+ @Inject
24
+ cryptoService: CryptoService;
25
+
16
26
  async create(phoneNumbers: string[]) {
17
27
  const rs = await fetch(`https://${globalEnv.otpIn.host}/api/otp-in/create`, {
18
28
  method: "POST",
@@ -21,11 +31,30 @@ export default class ExternalOtpInService {
21
31
  phoneNumbers
22
32
  })
23
33
  });
24
- return await rs.json();
34
+ const result = await rs.json();
35
+
36
+ const dateCreated = DateTime.now;
37
+ const expiry = dateCreated.addMinutes(15);
38
+
39
+ const { accessCode } = result;
40
+
41
+ // create hash
42
+ const v = await this.db.verificationTokens.statements.insert({
43
+ externalID: result.id,
44
+ accessCode,
45
+ dateCreated,
46
+ expiry,
47
+ });
48
+
49
+
50
+ result.id = v.verificationID.toString();
51
+ v.smsConfig = result;
52
+ return v;
25
53
  }
26
54
 
27
- async get(id, accessCode) {
28
- const rs = await fetch(`https://${globalEnv.otpIn.host}/api/otp-in/get/${id}/${accessCode}`, {
55
+ async get(verificationID) {
56
+ const v = await this.db.verificationTokens.statements.select({} , { verificationID });
57
+ const rs = await fetch(`https://${globalEnv.otpIn.host}/api/otp-in/get/${v.externalID}/${v.accessCode}`, {
29
58
  method: "GET",
30
59
  headers
31
60
  });