@prove-identity/prove-auth 2.10.1 → 2.14.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.
Files changed (28) hide show
  1. package/build/bundle/release/prove-auth.js +1 -1
  2. package/build/lib/index.d.ts +2 -2
  3. package/build/lib/index.js +4 -1
  4. package/build/lib/proveauth/authenticator-builder.d.ts +3 -2
  5. package/build/lib/proveauth/authenticator-builder.js +9 -3
  6. package/build/lib/proveauth/instantlink.d.ts +17 -3
  7. package/build/lib/proveauth/instantlink.js +16 -0
  8. package/build/lib/proveauth/internal/auth-request.d.ts +0 -2
  9. package/build/lib/proveauth/internal/auth-session.d.ts +4 -0
  10. package/build/lib/proveauth/internal/auth-session.js +33 -7
  11. package/build/lib/proveauth/internal/auth-token-claims.d.ts +2 -0
  12. package/build/lib/proveauth/internal/device-passive-register-step.js +8 -9
  13. package/build/lib/proveauth/internal/device-passive-silent-step.js +8 -0
  14. package/build/lib/proveauth/internal/device-passive-step.js +6 -3
  15. package/build/lib/proveauth/internal/device-passive-stepup-step.js +1 -19
  16. package/build/lib/proveauth/internal/device-passive-verify-step.js +2 -18
  17. package/build/lib/proveauth/internal/main-authenticator.js +3 -0
  18. package/build/lib/proveauth/internal/mobile-instantlink-step.d.ts +7 -3
  19. package/build/lib/proveauth/internal/mobile-instantlink-step.js +140 -29
  20. package/build/lib/proveauth/internal/mobile-otp-step.d.ts +1 -0
  21. package/build/lib/proveauth/internal/mobile-otp-step.js +24 -1
  22. package/build/lib/proveauth/internal/settings.d.ts +3 -0
  23. package/build/lib/proveauth/internal/settings.js +14 -0
  24. package/build/lib/proveauth/otp.d.ts +2 -1
  25. package/build/lib/proveauth/otp.js +1 -0
  26. package/build/lib/proveauth/version.d.ts +2 -2
  27. package/build/lib/proveauth/version.js +2 -2
  28. package/package.json +1 -1
@@ -1,4 +1,18 @@
1
- import PhoneNumberInput, { PhoneNumberStep, PhoneNumberStepFn } from './internal/phone-number-input';
1
+ import AuthError from './internal/auth-error';
2
+ import PhoneNumberInput, { PhoneValidationError } from './internal/phone-number-input';
3
+ export declare class InstantLinkMaxRetryError extends AuthError {
4
+ constructor(message?: string, code?: number);
5
+ }
6
+ export interface InstantLinkStartStep {
7
+ execute: (phoneNumberNeeded: boolean, instantLinkError?: PhoneValidationError) => Promise<PhoneNumberInput | null>;
8
+ }
9
+ export type InstantLinkStartStepFn = (phoneNumberNeeded: boolean, instantLinkError?: PhoneValidationError) => Promise<PhoneNumberInput | null>;
10
+ export declare enum InstantLinkResultType {
11
+ OnResend = 0,
12
+ OnMobileNumberChange = 1
13
+ }
14
+ export interface InstantLinkRetryStep {
15
+ execute: (instantLinkError?: InstantLinkMaxRetryError) => Promise<InstantLinkResultType>;
16
+ }
17
+ export type InstantLinkRetryStepFn = (instantLinkError?: InstantLinkMaxRetryError) => Promise<InstantLinkResultType>;
2
18
  export type InstantLinkStartInput = PhoneNumberInput;
3
- export type InstantLinkStartStep = PhoneNumberStep;
4
- export type InstantLinkStartStepFn = PhoneNumberStepFn;
@@ -1,2 +1,18 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.InstantLinkResultType = exports.InstantLinkMaxRetryError = void 0;
7
+ const auth_error_1 = __importDefault(require("./internal/auth-error"));
8
+ class InstantLinkMaxRetryError extends auth_error_1.default {
9
+ constructor(message, code) {
10
+ super(message, code);
11
+ }
12
+ }
13
+ exports.InstantLinkMaxRetryError = InstantLinkMaxRetryError;
14
+ var InstantLinkResultType;
15
+ (function (InstantLinkResultType) {
16
+ InstantLinkResultType[InstantLinkResultType["OnResend"] = 0] = "OnResend";
17
+ InstantLinkResultType[InstantLinkResultType["OnMobileNumberChange"] = 1] = "OnMobileNumberChange";
18
+ })(InstantLinkResultType = exports.InstantLinkResultType || (exports.InstantLinkResultType = {}));
@@ -33,7 +33,6 @@ export interface V1ClientDeviceFido2RegisterFinish {
33
33
  deviceName: string;
34
34
  deviceCapabilities: string[];
35
35
  registrations: AuthRegistration[];
36
- signals?: Signals;
37
36
  }
38
37
  export interface V1ClientDeviceFido2VerifyStart {
39
38
  deviceId: string;
@@ -52,7 +51,6 @@ export interface WebAuthnAssertion {
52
51
  }
53
52
  export interface V1ClientDeviceFido2VerifyFinish {
54
53
  webAuthnAssertion: WebAuthnAssertion;
55
- signals?: Signals;
56
54
  }
57
55
  export interface V1ClientDevicePassiveRegister {
58
56
  deviceName: string;
@@ -32,7 +32,11 @@ export default class AuthSession implements AuthSessionIntegration {
32
32
  closeAllMessageChannels(): void;
33
33
  getDeviceRegistration(): Promise<DeviceRegistration | null>;
34
34
  embedFpResultToDeviceRegistration(registration: DeviceRegistration): Promise<DeviceRegistration>;
35
+ private getCurrentTimestampInSeconds;
36
+ markNewFptts(ts?: number): void;
37
+ resetFptts(): void;
35
38
  getFingerprintData(): Promise<Signal | undefined>;
36
39
  shouldCollectFP(): boolean;
40
+ shouldRefreshFpSignal(currentTimestamp: number): boolean;
37
41
  private parseJwt;
38
42
  }
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const logger_1 = require("../common/logger");
7
7
  const version_1 = require("../version");
8
+ const auth_error_1 = __importDefault(require("./auth-error"));
8
9
  const auth_token_claims_1 = require("./auth-token-claims");
9
10
  const error_code_1 = __importDefault(require("./error-code"));
10
11
  const web_socket_close_reasons_1 = require("./web-socket-close-reasons");
@@ -164,8 +165,7 @@ class AuthSession {
164
165
  onClose(reason, code);
165
166
  });
166
167
  channel.addEventListener('error', (event) => {
167
- this.log.debug('Message channel encountered an error');
168
- this.log.debug(event);
168
+ this.log.debug('Message channel encountered an error', event);
169
169
  });
170
170
  channel.addEventListener('message', (event) => {
171
171
  this.log.trace('Message channel received a message');
@@ -227,17 +227,28 @@ class AuthSession {
227
227
  resolve(registration);
228
228
  })
229
229
  .catch((error) => {
230
- const errorMsg = `Unexpected error happened during Fingerprint data collection: ${error.toString}`;
231
- this.log.warn(errorMsg);
230
+ const errorMsg = `Unexpected error happened during Fingerprint data collection: ${auth_error_1.default.extractMessage(error)}`;
231
+ this.log.warn(error);
232
232
  registration.setFpSignal({ error: errorMsg });
233
233
  });
234
234
  });
235
235
  }
236
+ getCurrentTimestampInSeconds() {
237
+ const currentTimestampMs = Date.now();
238
+ const currentTimestampSeconds = Math.floor(currentTimestampMs / 1000);
239
+ return currentTimestampSeconds;
240
+ }
241
+ markNewFptts(ts) {
242
+ this.settings.fingerPrintTimestamp = ts ? ts : this.getCurrentTimestampInSeconds();
243
+ }
244
+ resetFptts() {
245
+ this.settings.fingerPrintTimestamp = null;
246
+ }
236
247
  getFingerprintData() {
237
248
  return new Promise((resolve) => {
238
249
  var fpPromise = this.platform.getFpPromise();
239
250
  if (!this.shouldCollectFP()) {
240
- this.log.debug('Fingerprint is not enabled from AuthToken');
251
+ this.log.trace('Fingerprint is not enabled from AuthToken');
241
252
  resolve(undefined);
242
253
  }
243
254
  else if (!fpPromise) {
@@ -245,7 +256,12 @@ class AuthSession {
245
256
  this.log.warn(msg);
246
257
  resolve({ error: msg });
247
258
  }
259
+ else if (!this.shouldRefreshFpSignal(this.getCurrentTimestampInSeconds())) {
260
+ this.log.trace('Existing FP signal is not yet expired, skip new collection');
261
+ resolve(undefined);
262
+ }
248
263
  else {
264
+ this.log.trace('Collect new FP signal');
249
265
  fpPromise
250
266
  .then((fp) => fp.get())
251
267
  .then((result) => {
@@ -260,8 +276,8 @@ class AuthSession {
260
276
  }
261
277
  })
262
278
  .catch((error) => {
263
- const msg = `Error in collecting Fingerprint data: ${error.toString}`;
264
- this.log.warn(msg);
279
+ const msg = `Error in collecting Fingerprint data: ${auth_error_1.default.extractMessage(error)}`;
280
+ this.log.warn(error);
265
281
  resolve({ error: msg });
266
282
  });
267
283
  }
@@ -274,6 +290,16 @@ class AuthSession {
274
290
  }
275
291
  return false;
276
292
  }
293
+ shouldRefreshFpSignal(currentTimestamp) {
294
+ var _a, _b, _c, _d;
295
+ if (!this.settings.deviceId || !this.settings.fingerPrintTimestamp) {
296
+ this.resetFptts();
297
+ return true;
298
+ }
299
+ const refreshRate = (_d = (_c = (_b = (_a = this.claims) === null || _a === void 0 ? void 0 : _a.auth.subs.dev) === null || _b === void 0 ? void 0 : _b.sgnls) === null || _c === void 0 ? void 0 : _c.fptrr) !== null && _d !== void 0 ? _d : 0;
300
+ const interval = currentTimestamp - this.settings.fingerPrintTimestamp;
301
+ return interval >= refreshRate;
302
+ }
277
303
  parseJwt(token) {
278
304
  return JSON.parse(atob(token.split('.')[1]));
279
305
  }
@@ -11,6 +11,7 @@ export interface InstantAuthenticator {
11
11
  }
12
12
  export interface InstantLinkAuthenticator {
13
13
  mnp: boolean;
14
+ amnrp?: boolean;
14
15
  tme?: boolean;
15
16
  }
16
17
  export interface OtpAuthenticator {
@@ -29,6 +30,7 @@ export interface Authenticators {
29
30
  }
30
31
  export interface Signals {
31
32
  fpt?: boolean;
33
+ fptrr?: number;
32
34
  dwn?: boolean;
33
35
  }
34
36
  export interface DeviceAuthSubjectClaim {
@@ -23,11 +23,9 @@ class DevicePassiveRegisterStep {
23
23
  .getDeviceRegistration()
24
24
  .then((devRegistration) => {
25
25
  if (devRegistration) {
26
- session.embedFpResultToDeviceRegistration(devRegistration).then((devRegistration) => {
27
- this.finishRegistration(session, [this.getFido2Registration(session)], devRegistration.getSignals())
28
- .then(resolve)
29
- .catch(reject);
30
- });
26
+ this.finishRegistration(session, [this.getFido2Registration(session)])
27
+ .then(resolve)
28
+ .catch(reject);
31
29
  }
32
30
  else {
33
31
  session.platform.deviceAuth
@@ -35,11 +33,13 @@ class DevicePassiveRegisterStep {
35
33
  namespace: session.namespace,
36
34
  endpoint: session.backendOrigin,
37
35
  })
38
- .then((devRegistration) => session.embedFpResultToDeviceRegistration(devRegistration))
39
36
  .then((devRegistration) => {
40
37
  devRegistration
41
38
  .getAuthRegistration(session.challenge)
42
- .then((authRegistration) => this.finishRegistration(session, [this.getFido2Registration(session), authRegistration], devRegistration.getSignals()))
39
+ .then((authRegistration) => this.finishRegistration(session, [
40
+ this.getFido2Registration(session),
41
+ authRegistration,
42
+ ]))
43
43
  .then((next) => {
44
44
  devRegistration.deviceId = session.settings.deviceId;
45
45
  session.platform.deviceAuth
@@ -61,14 +61,13 @@ class DevicePassiveRegisterStep {
61
61
  const assertion = credential.response;
62
62
  return assertion && 'authenticatorData' in assertion && 'signature' in assertion;
63
63
  }
64
- finishRegistration(session, registrations, signals) {
64
+ finishRegistration(session, registrations) {
65
65
  return new Promise((resolve, reject) => {
66
66
  session
67
67
  .fetchFromBackend('/v1/client/device/fido2/register/finish', {
68
68
  deviceName: session.platform.getPlatformName(),
69
69
  deviceCapabilities: session.platform.getDeviceCapabilities(),
70
70
  registrations: registrations,
71
- signals: signals,
72
71
  })
73
72
  .then((response) => {
74
73
  if (response.error) {
@@ -70,10 +70,14 @@ class DevicePassiveSilentStep {
70
70
  signals: registration.getSignals(),
71
71
  })
72
72
  .then((response) => {
73
+ var _a;
73
74
  if (response.error) {
74
75
  reject(new auth_error_1.default(response.error.message, response.error.code, response.next, false));
75
76
  }
76
77
  else {
78
+ if ((_a = registration.getSignals().fingerprint) === null || _a === void 0 ? void 0 : _a.results) {
79
+ session.markNewFptts();
80
+ }
77
81
  const deviceRegisterAuthResp = response;
78
82
  const deviceId = deviceRegisterAuthResp.data.deviceId;
79
83
  if (!deviceId) {
@@ -113,10 +117,14 @@ class DevicePassiveSilentStep {
113
117
  signals: registration.getSignals(),
114
118
  })
115
119
  .then((response) => {
120
+ var _a;
116
121
  if (response.error) {
117
122
  reject(new auth_error_1.default(response.error.message, response.error.code, response.next, false));
118
123
  }
119
124
  else {
125
+ if ((_a = registration.getSignals().fingerprint) === null || _a === void 0 ? void 0 : _a.results) {
126
+ session.markNewFptts();
127
+ }
120
128
  resolve(response.next);
121
129
  }
122
130
  })
@@ -30,13 +30,16 @@ class DevicePassiveActions extends auth_status_actions_1.AuthStatusActions {
30
30
  signals: signals,
31
31
  })
32
32
  .then((response) => {
33
- var _a, _b;
33
+ var _a, _b, _c;
34
34
  if (response.error) {
35
35
  reject(new auth_error_1.default(response.error.message, response.error.code, response.next, false));
36
36
  }
37
37
  else {
38
- const creationOptions = (_a = response.data) === null || _a === void 0 ? void 0 : _a.credCreateOptions;
39
- const requestOptions = (_b = response.data) === null || _b === void 0 ? void 0 : _b.credRequestOptions;
38
+ if ((_a = signals === null || signals === void 0 ? void 0 : signals.fingerprint) === null || _a === void 0 ? void 0 : _a.results) {
39
+ session.markNewFptts();
40
+ }
41
+ const creationOptions = (_b = response.data) === null || _b === void 0 ? void 0 : _b.credCreateOptions;
42
+ const requestOptions = (_c = response.data) === null || _c === void 0 ? void 0 : _c.credRequestOptions;
40
43
  if (creationOptions) {
41
44
  this.createCredentials(session, displayName, creationOptions, response)
42
45
  .then(resolve)
@@ -19,25 +19,7 @@ class DevicePassiveStepupStep extends device_passive_step_1.DevicePassiveActions
19
19
  return Promise.reject(new Error('FIDO2 Passkey is already registered'));
20
20
  }
21
21
  return new Promise((resolve, reject) => {
22
- session
23
- .getFingerprintData()
24
- .then((signal) => {
25
- const signals = {
26
- fingerprint: signal,
27
- };
28
- this.register(session, signals).then(resolve).catch(reject);
29
- })
30
- .catch((error) => {
31
- const errorMsg = `Unexpected error happened during Fingerprint data collection: ${error.message}`;
32
- this.log.warn(errorMsg);
33
- this.log.warn(error);
34
- const signals = {
35
- fingerprint: {
36
- error: errorMsg,
37
- },
38
- };
39
- this.register(session, signals).then(resolve).catch(reject);
40
- });
22
+ this.register(session).then(resolve).catch(reject);
41
23
  });
42
24
  }
43
25
  }
@@ -18,25 +18,10 @@ class DevicePassiveVerifyStep {
18
18
  }
19
19
  static runFidoVerify(log, session) {
20
20
  return new Promise((resolve, reject) => {
21
- session
22
- .getFingerprintData()
23
- .then((signal) => {
24
- const signals = { fingerprint: signal };
25
- DevicePassiveVerifyStep.makeFidoVerifyFinishRequest(log, session, signals)
26
- .then(resolve)
27
- .catch(reject);
28
- })
29
- .catch((error) => {
30
- const errorMsg = `Unexpected error happened during Fingerprint data collection ${error.toString}`;
31
- log.warn(errorMsg);
32
- const signals = { fingerprint: { error: errorMsg } };
33
- DevicePassiveVerifyStep.makeFidoVerifyFinishRequest(log, session, signals)
34
- .then(resolve)
35
- .catch(reject);
36
- });
21
+ DevicePassiveVerifyStep.makeFidoVerifyFinishRequest(log, session).then(resolve).catch(reject);
37
22
  });
38
23
  }
39
- static makeFidoVerifyFinishRequest(log, session, signals) {
24
+ static makeFidoVerifyFinishRequest(log, session) {
40
25
  return new Promise((resolve, reject) => {
41
26
  const credential = session.credential;
42
27
  const assertion = credential.response;
@@ -55,7 +40,6 @@ class DevicePassiveVerifyStep {
55
40
  : undefined,
56
41
  },
57
42
  },
58
- signals: signals,
59
43
  })
60
44
  .then((response) => {
61
45
  if (response.error) {
@@ -138,6 +138,9 @@ class MainAuthenticator {
138
138
  session.lastStep = step;
139
139
  return new Promise((resolve, reject) => {
140
140
  if ([MainAuthenticator.AUTH_DONE, MainAuthenticator.AUTH_EMPTY].includes(step)) {
141
+ if (!session.settings.deviceId && session.settings.fingerPrintTimestamp) {
142
+ session.resetFptts();
143
+ }
141
144
  resolve();
142
145
  }
143
146
  else if (attempt > MainAuthenticator.MAX_ATTEMPTS) {
@@ -1,15 +1,19 @@
1
1
  import AuthSession from './auth-session';
2
2
  import AuthStep from './auth-step';
3
- import { InstantLinkStartStep } from '../instantlink';
3
+ import { InstantLinkRetryStep, InstantLinkStartStep } from '../instantlink';
4
4
  import { AuthStatusActions } from './auth-status-actions';
5
5
  export default class MobileInstantLinkStep extends AuthStatusActions implements AuthStep {
6
6
  static readonly NAME = "mobile/instantlink";
7
7
  readonly name = "mobile/instantlink";
8
8
  protected log: import("../common/logger").Logger;
9
9
  private readonly startStep?;
10
+ private readonly retryStep?;
10
11
  private readonly getDeviceIp;
11
- constructor(startStep?: InstantLinkStartStep, getDeviceIp?: () => string | null);
12
+ constructor(startStep?: InstantLinkStartStep, retryStep?: InstantLinkRetryStep, getDeviceIp?: () => string | null);
13
+ private isTestMode;
12
14
  execute(session: AuthSession): Promise<string>;
15
+ private getInstantLinkError;
13
16
  private runStartStep;
14
- private runFinishStep;
17
+ private runRetryStep;
18
+ private handleTestMode;
15
19
  }
@@ -4,46 +4,75 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const logger_1 = require("../common/logger");
7
+ const instantlink_1 = require("../instantlink");
7
8
  const phone_number_input_1 = require("./phone-number-input");
8
9
  const auth_error_1 = __importDefault(require("./auth-error"));
9
10
  const auth_status_actions_1 = require("./auth-status-actions");
10
11
  const mobile_otp_step_1 = __importDefault(require("./mobile-otp-step"));
11
12
  const SIMULATED_LINK_CLICK_DELAY = 100;
12
13
  class MobileInstantLinkStep extends auth_status_actions_1.AuthStatusActions {
13
- constructor(startStep, getDeviceIp) {
14
+ constructor(startStep, retryStep, getDeviceIp) {
14
15
  super();
15
16
  this.name = MobileInstantLinkStep.NAME;
16
17
  this.log = logger_1.LoggerFactory.getLogger('mobile-instantlink-step');
17
18
  this.startStep = startStep;
19
+ this.retryStep = retryStep;
18
20
  this.getDeviceIp = getDeviceIp !== null && getDeviceIp !== void 0 ? getDeviceIp : (() => null);
19
21
  }
22
+ isTestMode(session) {
23
+ var _a, _b, _c;
24
+ var testMode = false;
25
+ if ((_c = (_b = (_a = session.claims) === null || _a === void 0 ? void 0 : _a.auth.subs.mob) === null || _b === void 0 ? void 0 : _b.auths.inln) === null || _c === void 0 ? void 0 : _c.tme) {
26
+ testMode = true;
27
+ }
28
+ return testMode;
29
+ }
20
30
  execute(session) {
21
31
  this.log.trace('Executing');
22
32
  return new Promise((resolve, reject) => {
23
- var _a, _b, _c, _d, _e, _f;
33
+ var _a, _b, _c;
24
34
  var phoneNumberNeeded = true;
25
35
  if ((_c = (_b = (_a = session.claims) === null || _a === void 0 ? void 0 : _a.auth.subs.mob) === null || _b === void 0 ? void 0 : _b.auths.inln) === null || _c === void 0 ? void 0 : _c.mnp) {
26
36
  phoneNumberNeeded = false;
27
37
  }
28
- var testMode = false;
29
- if ((_f = (_e = (_d = session.claims) === null || _d === void 0 ? void 0 : _d.auth.subs.mob) === null || _e === void 0 ? void 0 : _e.auths.inln) === null || _f === void 0 ? void 0 : _f.tme) {
30
- testMode = true;
31
- }
32
38
  this.runStartStep(session, phoneNumberNeeded)
33
- .then((_) => {
34
- this.runFinishStep(session, testMode)
35
- .then(() => this.waitForStatus(session))
36
- .then((next) => resolve(next))
37
- .catch(reject);
38
- })
39
39
  .catch(reject);
40
+ this.waitForStatus(session).then(resolve).catch(reject);
40
41
  });
41
42
  }
42
- runStartStep(session, phoneNumberNeeded, phoneValidationError) {
43
+ getInstantLinkError(responseData) {
44
+ const errorCode = responseData.code;
45
+ var errorMessage = '';
46
+ if (errorCode) {
47
+ errorMessage += `Error Code: ${responseData.code}, `;
48
+ }
49
+ let instantLinkError;
50
+ if (errorCode === 10005) {
51
+ if (responseData.message) {
52
+ errorMessage += `${responseData.message}`;
53
+ }
54
+ else {
55
+ errorMessage += `Max number of retires reached.`;
56
+ }
57
+ instantLinkError = new instantlink_1.InstantLinkMaxRetryError(errorMessage, responseData.code);
58
+ }
59
+ else {
60
+ if (responseData.message) {
61
+ errorMessage += `${responseData.message}`;
62
+ }
63
+ else {
64
+ errorMessage += `Error validating phone number`;
65
+ }
66
+ instantLinkError = new phone_number_input_1.PhoneValidationError(errorMessage, responseData.code);
67
+ }
68
+ this.log.error(`Server reports instant link error: ${errorMessage}`);
69
+ return instantLinkError;
70
+ }
71
+ runStartStep(session, phoneNumberNeeded, instantLinkError) {
43
72
  return new Promise((resolve, reject) => {
44
73
  if (this.startStep) {
45
74
  this.startStep
46
- .execute(phoneNumberNeeded, phoneValidationError)
75
+ .execute(phoneNumberNeeded, instantLinkError)
47
76
  .then((input) => {
48
77
  const inputError = mobile_otp_step_1.default.validatePhoneNumberInput(input);
49
78
  if (inputError) {
@@ -56,30 +85,55 @@ class MobileInstantLinkStep extends auth_status_actions_1.AuthStatusActions {
56
85
  mobileNumber: input === null || input === void 0 ? void 0 : input.phoneNumber,
57
86
  })
58
87
  .then((response) => {
59
- var _a, _b, _c;
60
88
  const authResponse = response;
61
89
  if (authResponse.error) {
62
90
  reject(new auth_error_1.default(authResponse.error.message, authResponse.error.code, response.next, false));
63
91
  }
64
92
  else if (authResponse.data) {
93
+ const errorCode = authResponse.data.code;
65
94
  var errorMessage = '';
66
- if ((_a = authResponse.data) === null || _a === void 0 ? void 0 : _a.code) {
95
+ if (errorCode) {
67
96
  errorMessage += `Error Code: ${authResponse.data.code}, `;
68
97
  }
69
- if ((_b = authResponse.data) === null || _b === void 0 ? void 0 : _b.message) {
70
- errorMessage += `${authResponse.data.message}`;
98
+ if (errorCode === 10005) {
99
+ if (authResponse.data.message) {
100
+ errorMessage += `${authResponse.data.message}`;
101
+ }
102
+ else {
103
+ errorMessage += `Max number of retires reached.`;
104
+ }
105
+ let maxRetryError = new instantlink_1.InstantLinkMaxRetryError(errorMessage, authResponse.data.code);
106
+ if (this.retryStep) {
107
+ this.runRetryStep(session, maxRetryError).then(resolve).catch(reject);
108
+ }
109
+ else {
110
+ this.log.error('Max number of retires reached but retry step is undefined.');
111
+ }
71
112
  }
72
113
  else {
73
- errorMessage += `Error validating phone number`;
114
+ if (authResponse.data.message) {
115
+ errorMessage += `${authResponse.data.message}`;
116
+ }
117
+ else {
118
+ errorMessage += `Error validating phone number.`;
119
+ }
120
+ let invalidPhoneError = new phone_number_input_1.PhoneValidationError(errorMessage, authResponse.data.code);
121
+ this.runStartStep(session, phoneNumberNeeded, invalidPhoneError)
122
+ .then(resolve)
123
+ .catch(reject);
74
124
  }
75
- let phoneNumberValidationError = new phone_number_input_1.PhoneValidationError(errorMessage, (_c = authResponse.data) === null || _c === void 0 ? void 0 : _c.code);
76
- this.log.error(`Server reports invalid phone number: ${errorMessage}`);
77
- this.runStartStep(session, phoneNumberNeeded, phoneNumberValidationError)
78
- .then(resolve)
79
- .catch(reject);
80
125
  }
81
126
  else {
82
- resolve(authResponse.next);
127
+ this.handleTestMode(session)
128
+ .then(() => {
129
+ if (this.retryStep) {
130
+ this.runRetryStep(session).then(resolve).catch(reject);
131
+ }
132
+ else {
133
+ resolve(authResponse.next);
134
+ }
135
+ })
136
+ .catch(reject);
83
137
  }
84
138
  })
85
139
  .catch(reject);
@@ -92,9 +146,65 @@ class MobileInstantLinkStep extends auth_status_actions_1.AuthStatusActions {
92
146
  }
93
147
  });
94
148
  }
95
- runFinishStep(session, testMode) {
149
+ runRetryStep(session, instantLinkError) {
96
150
  return new Promise((resolve, reject) => {
97
- if (testMode) {
151
+ if (this.retryStep) {
152
+ this.retryStep
153
+ .execute(instantLinkError)
154
+ .then((resultType) => {
155
+ if (resultType === instantlink_1.InstantLinkResultType.OnResend) {
156
+ session
157
+ .fetchFromBackend('/v1/client/mobile/instantlink/start', {
158
+ sourceIp: this.getDeviceIp(),
159
+ mobileNumber: '',
160
+ })
161
+ .then((response) => {
162
+ const authResponse = response;
163
+ if (authResponse.error) {
164
+ reject(new auth_error_1.default(authResponse.error.message, authResponse.error.code, response.next, false));
165
+ }
166
+ else if (authResponse.data) {
167
+ const errorCode = authResponse.data.code;
168
+ var errorMessage = '';
169
+ if (errorCode) {
170
+ errorMessage += `Error Code: ${authResponse.data.code}, `;
171
+ }
172
+ if (authResponse.data.message) {
173
+ errorMessage += `${authResponse.data.message}`;
174
+ }
175
+ else {
176
+ errorMessage += `Max number of retires reached.`;
177
+ }
178
+ let maxRetryError = new instantlink_1.InstantLinkMaxRetryError(errorMessage, authResponse.data.code);
179
+ this.runRetryStep(session, maxRetryError).then(resolve).catch(reject);
180
+ }
181
+ else {
182
+ this.handleTestMode(session)
183
+ .then(() => this.runRetryStep(session))
184
+ .then(resolve)
185
+ .catch(reject);
186
+ }
187
+ })
188
+ .catch(reject);
189
+ }
190
+ else if (resultType === instantlink_1.InstantLinkResultType.OnMobileNumberChange) {
191
+ this.runStartStep(session, true).then(resolve).catch(reject);
192
+ }
193
+ else {
194
+ this.log.warn('Unkown enum of ', resultType);
195
+ }
196
+ })
197
+ .catch(reject);
198
+ }
199
+ else {
200
+ this.log.debug("Retry step doesn't exsist, skip.");
201
+ resolve('');
202
+ }
203
+ });
204
+ }
205
+ handleTestMode(session) {
206
+ return new Promise((resolve, reject) => {
207
+ if (this.isTestMode(session)) {
98
208
  this.log.info('Simulating user clicking the instant link');
99
209
  setTimeout(() => {
100
210
  session.platform
@@ -105,10 +215,11 @@ class MobileInstantLinkStep extends auth_status_actions_1.AuthStatusActions {
105
215
  mode: 'no-cors',
106
216
  method: 'GET',
107
217
  })
218
+ .then(() => resolve)
108
219
  .catch((e) => {
109
220
  this.log.error('Calling AuthFinish in test mode has failed: ', auth_error_1.default.extractMessage(e));
110
- })
111
- .finally(resolve);
221
+ reject(e);
222
+ });
112
223
  }, SIMULATED_LINK_CLICK_DELAY);
113
224
  }
114
225
  else {
@@ -13,5 +13,6 @@ export default class MobileOtpStep implements AuthStep {
13
13
  static validatePhoneNumberInput(input: any): AuthError | undefined;
14
14
  private runOtpStartStep;
15
15
  static validateFinishResult(result: any): AuthError | undefined;
16
+ private resendOtp;
16
17
  private runOtpFinishStep;
17
18
  }