@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.
- package/build/bundle/release/prove-auth.js +1 -1
- package/build/lib/index.d.ts +2 -2
- package/build/lib/index.js +4 -1
- package/build/lib/proveauth/authenticator-builder.d.ts +3 -2
- package/build/lib/proveauth/authenticator-builder.js +9 -3
- package/build/lib/proveauth/instantlink.d.ts +17 -3
- package/build/lib/proveauth/instantlink.js +16 -0
- package/build/lib/proveauth/internal/auth-request.d.ts +0 -2
- package/build/lib/proveauth/internal/auth-session.d.ts +4 -0
- package/build/lib/proveauth/internal/auth-session.js +33 -7
- package/build/lib/proveauth/internal/auth-token-claims.d.ts +2 -0
- package/build/lib/proveauth/internal/device-passive-register-step.js +8 -9
- package/build/lib/proveauth/internal/device-passive-silent-step.js +8 -0
- package/build/lib/proveauth/internal/device-passive-step.js +6 -3
- package/build/lib/proveauth/internal/device-passive-stepup-step.js +1 -19
- package/build/lib/proveauth/internal/device-passive-verify-step.js +2 -18
- package/build/lib/proveauth/internal/main-authenticator.js +3 -0
- package/build/lib/proveauth/internal/mobile-instantlink-step.d.ts +7 -3
- package/build/lib/proveauth/internal/mobile-instantlink-step.js +140 -29
- package/build/lib/proveauth/internal/mobile-otp-step.d.ts +1 -0
- package/build/lib/proveauth/internal/mobile-otp-step.js +24 -1
- package/build/lib/proveauth/internal/settings.d.ts +3 -0
- package/build/lib/proveauth/internal/settings.js +14 -0
- package/build/lib/proveauth/otp.d.ts +2 -1
- package/build/lib/proveauth/otp.js +1 -0
- package/build/lib/proveauth/version.d.ts +2 -2
- package/build/lib/proveauth/version.js +2 -2
- package/package.json +1 -1
|
@@ -1,4 +1,18 @@
|
|
|
1
|
-
import
|
|
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
|
|
231
|
-
this.log.warn(
|
|
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.
|
|
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
|
|
264
|
-
this.log.warn(
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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, [
|
|
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
|
|
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
|
-
|
|
39
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|
|
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 (
|
|
95
|
+
if (errorCode) {
|
|
67
96
|
errorMessage += `Error Code: ${authResponse.data.code}, `;
|
|
68
97
|
}
|
|
69
|
-
if (
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
149
|
+
runRetryStep(session, instantLinkError) {
|
|
96
150
|
return new Promise((resolve, reject) => {
|
|
97
|
-
if (
|
|
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
|
-
|
|
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
|
}
|