@prove-identity/prove-auth 2.13.1 → 2.15.1
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 +3 -2
- package/build/lib/index.js +1 -2
- package/build/lib/proveauth/authenticator-builder.d.ts +5 -0
- package/build/lib/proveauth/authenticator-builder.js +25 -2
- package/build/lib/proveauth/external/@authid/web-component/authid-web-component.d.ts +3 -0
- package/build/lib/proveauth/external/@authid/web-component/authid-web-component.js +55 -0
- package/build/lib/proveauth/instantlink.d.ts +2 -6
- package/build/lib/proveauth/instantlink.js +1 -11
- package/build/lib/proveauth/internal/auth-request.d.ts +5 -3
- package/build/lib/proveauth/internal/auth-response.d.ts +24 -28
- package/build/lib/proveauth/internal/auth-session.d.ts +6 -1
- package/build/lib/proveauth/internal/auth-session.js +34 -7
- package/build/lib/proveauth/internal/auth-token-claims.d.ts +22 -3
- package/build/lib/proveauth/internal/device-passive-register-step.js +8 -9
- package/build/lib/proveauth/internal/device-passive-silent-step.js +13 -6
- package/build/lib/proveauth/internal/device-passive-step.js +10 -5
- 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-instant-step.js +2 -2
- package/build/lib/proveauth/internal/mobile-instantlink-step.d.ts +0 -3
- package/build/lib/proveauth/internal/mobile-instantlink-step.js +22 -122
- package/build/lib/proveauth/internal/mobile-otp-step.js +3 -3
- package/build/lib/proveauth/internal/platform.d.ts +11 -0
- package/build/lib/proveauth/internal/report-error-step.js +5 -2
- package/build/lib/proveauth/internal/settings.d.ts +3 -0
- package/build/lib/proveauth/internal/settings.js +14 -0
- package/build/lib/proveauth/internal/user-mobileactive-step.js +1 -3
- package/build/lib/proveauth/internal/user-ppb-steps.d.ts +24 -0
- package/build/lib/proveauth/internal/user-ppb-steps.js +103 -0
- package/build/lib/proveauth/internal/user-present-step.js +1 -3
- package/build/lib/proveauth/internal/web-platform.d.ts +3 -1
- package/build/lib/proveauth/internal/web-platform.js +101 -0
- package/build/lib/proveauth/ppb.d.ts +9 -0
- package/build/lib/proveauth/ppb.js +2 -0
- package/build/lib/proveauth/version.d.ts +2 -2
- package/build/lib/proveauth/version.js +2 -2
- package/package.json +1 -1
|
@@ -21,6 +21,7 @@ const main_authenticator_1 = __importDefault(require("./internal/main-authentica
|
|
|
21
21
|
const logger_1 = require("./common/logger");
|
|
22
22
|
const settings_1 = __importDefault(require("./internal/settings"));
|
|
23
23
|
const device_context_options_1 = require("./device-context-options");
|
|
24
|
+
const user_ppb_steps_1 = require("./internal/user-ppb-steps");
|
|
24
25
|
var DeviceRole;
|
|
25
26
|
(function (DeviceRole) {
|
|
26
27
|
DeviceRole[DeviceRole["Primary"] = 0] = "Primary";
|
|
@@ -36,6 +37,7 @@ class AuthenticatorBuilder {
|
|
|
36
37
|
this.role = DeviceRole.Primary;
|
|
37
38
|
this.mobileAuthImplementation = MobileAuthImplementation.Fetch;
|
|
38
39
|
this.upkEnabled = false;
|
|
40
|
+
this.ppbEnabled = false;
|
|
39
41
|
this.log = logger_1.LoggerFactory.getLogger('authenticator-builder');
|
|
40
42
|
if (typeof window !== 'undefined') {
|
|
41
43
|
this.storage = window.localStorage;
|
|
@@ -131,6 +133,22 @@ class AuthenticatorBuilder {
|
|
|
131
133
|
this.upkEnabled = this.userConsentStep != null;
|
|
132
134
|
return this;
|
|
133
135
|
}
|
|
136
|
+
withPrivacyPreservingBiometrics(startStep, finishStep) {
|
|
137
|
+
this.ppbEnabled = true;
|
|
138
|
+
if (typeof startStep === 'function') {
|
|
139
|
+
this.ppbStartStep = { execute: startStep };
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
this.ppbStartStep = startStep;
|
|
143
|
+
}
|
|
144
|
+
if (typeof finishStep === 'function') {
|
|
145
|
+
this.ppbFinishStep = { execute: finishStep };
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this.ppbFinishStep = finishStep;
|
|
149
|
+
}
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
134
152
|
getUrlsByBuildConfig(buildConfig) {
|
|
135
153
|
switch (buildConfig) {
|
|
136
154
|
case device_context_options_1.BuildConfig.US_PROD:
|
|
@@ -144,7 +162,8 @@ class AuthenticatorBuilder {
|
|
|
144
162
|
device_context_options_1.ProveAuthProxyEndpoint.DEFAULT_US_UAT_ENDPOINT.toString(),
|
|
145
163
|
];
|
|
146
164
|
case device_context_options_1.BuildConfig.DEV:
|
|
147
|
-
this.log.debug("Recommended for Prove's internal testing only, BuildConfig.DEV
|
|
165
|
+
this.log.debug("Recommended for Prove's internal testing only, BuildConfig.DEV " +
|
|
166
|
+
'might need custom endpoint URL and custom script URL values to bypass ad blockers');
|
|
148
167
|
return [undefined, undefined];
|
|
149
168
|
default:
|
|
150
169
|
this.log.warn('Unknown BuildConfig value: ' + buildConfig);
|
|
@@ -237,8 +256,10 @@ class AuthenticatorBuilder {
|
|
|
237
256
|
new mobile_instant_step_1.default(this.mobileAuthImplementation, this.getDeviceIp),
|
|
238
257
|
new mobile_instantlink_step_1.default(this.instantLinkStartStep, this.instantLinkRetryStep, this.getDeviceIp),
|
|
239
258
|
new mobile_otp_step_1.default(this.otpStartStep, this.otpFinishStep),
|
|
240
|
-
new user_mobileactive_step_1.default(),
|
|
241
259
|
new scan_message_step_1.default(this.authMessageHandler),
|
|
260
|
+
new user_mobileactive_step_1.default(),
|
|
261
|
+
new user_ppb_steps_1.UserPpbEnrollStep(this.ppbEnabled, this.ppbStartStep, this.ppbFinishStep),
|
|
262
|
+
new user_ppb_steps_1.UserPpbVerifyStep(this.ppbEnabled, this.ppbStartStep, this.ppbFinishStep),
|
|
242
263
|
]);
|
|
243
264
|
}
|
|
244
265
|
else {
|
|
@@ -246,6 +267,8 @@ class AuthenticatorBuilder {
|
|
|
246
267
|
new device_passive_step_1.default(this.getDisplayName, undefined, this.role),
|
|
247
268
|
new mobile_instant_step_1.default(this.mobileAuthImplementation, this.getDeviceIp),
|
|
248
269
|
new mobile_instantlink_step_1.default(this.instantLinkStartStep, this.instantLinkRetryStep, this.getDeviceIp),
|
|
270
|
+
new user_ppb_steps_1.UserPpbEnrollStep(this.ppbEnabled, this.ppbStartStep, this.ppbFinishStep),
|
|
271
|
+
new user_ppb_steps_1.UserPpbVerifyStep(this.ppbEnabled, this.ppbStartStep, this.ppbFinishStep),
|
|
249
272
|
]);
|
|
250
273
|
}
|
|
251
274
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
class AuthIDComponent extends HTMLElement {
|
|
2
|
+
connectedCallback() {
|
|
3
|
+
const dataUrl = this.getAttribute('data-url');
|
|
4
|
+
if (!dataUrl) {
|
|
5
|
+
throw new Error('Missing data-url configuration!');
|
|
6
|
+
}
|
|
7
|
+
let dataTarget = this.getAttribute('data-target');
|
|
8
|
+
if (!dataTarget || dataTarget === 'auto') {
|
|
9
|
+
dataTarget = /Mobi|Android/i.test(navigator.userAgent) ? 'mobile' : 'desktop';
|
|
10
|
+
}
|
|
11
|
+
const iframe = document.createElement('iframe');
|
|
12
|
+
iframe.setAttribute('allow', 'fullscreen *;camera *;encrypted-media *;');
|
|
13
|
+
iframe.setAttribute('src', dataUrl);
|
|
14
|
+
const styleNode = document.createElement('style');
|
|
15
|
+
styleNode.textContent =
|
|
16
|
+
'div, iframe {' +
|
|
17
|
+
'position: fixed;' +
|
|
18
|
+
'top: 0;' +
|
|
19
|
+
'left: 0;' +
|
|
20
|
+
'height: 100vh;' +
|
|
21
|
+
'width: 100vw;' +
|
|
22
|
+
'border: 0;' +
|
|
23
|
+
'padding: 0;' +
|
|
24
|
+
'margin: 0;' +
|
|
25
|
+
'}';
|
|
26
|
+
const shadow = this.attachShadow({ mode: 'closed' });
|
|
27
|
+
shadow.appendChild(styleNode);
|
|
28
|
+
const container = document.createElement('div');
|
|
29
|
+
shadow.appendChild(container);
|
|
30
|
+
const dataWebauth = this.getAttribute('data-webauth');
|
|
31
|
+
if (dataWebauth && dataWebauth !== 'false' && dataWebauth !== 'no') {
|
|
32
|
+
const webauthHandler = document.createElement('script');
|
|
33
|
+
webauthHandler.setAttribute('src', dataUrl.replace('/?', '/webauthhandler.js?'));
|
|
34
|
+
shadow.appendChild(webauthHandler);
|
|
35
|
+
}
|
|
36
|
+
const dataControl = this.getAttribute('data-control');
|
|
37
|
+
if (dataControl && dataControl !== 'false' && dataControl !== 'no') {
|
|
38
|
+
const controlHandler = document.createElement('script');
|
|
39
|
+
controlHandler.setAttribute('src', dataUrl.replace('/?', '/controlhandler.js?'));
|
|
40
|
+
shadow.appendChild(controlHandler);
|
|
41
|
+
}
|
|
42
|
+
shadow.appendChild(iframe);
|
|
43
|
+
iframe.addEventListener('load', () => {
|
|
44
|
+
shadow.removeChild(container);
|
|
45
|
+
shadow.dispatchEvent(new CustomEvent('load', {
|
|
46
|
+
composed: true,
|
|
47
|
+
bubbles: true,
|
|
48
|
+
}));
|
|
49
|
+
}, { capture: true, once: true });
|
|
50
|
+
if (window.getComputedStyle(this)['z-index'] === 'auto') {
|
|
51
|
+
this.style['z-index'] = 1000;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
window.customElements.define('authid-component', AuthIDComponent);
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import AuthError from './internal/auth-error';
|
|
2
1
|
import PhoneNumberInput, { PhoneValidationError } from './internal/phone-number-input';
|
|
3
|
-
export declare class InstantLinkMaxRetryError extends AuthError {
|
|
4
|
-
constructor(message?: string, code?: number);
|
|
5
|
-
}
|
|
6
2
|
export interface InstantLinkStartStep {
|
|
7
3
|
execute: (phoneNumberNeeded: boolean, instantLinkError?: PhoneValidationError) => Promise<PhoneNumberInput | null>;
|
|
8
4
|
}
|
|
@@ -12,7 +8,7 @@ export declare enum InstantLinkResultType {
|
|
|
12
8
|
OnMobileNumberChange = 1
|
|
13
9
|
}
|
|
14
10
|
export interface InstantLinkRetryStep {
|
|
15
|
-
execute: (
|
|
11
|
+
execute: () => Promise<InstantLinkResultType>;
|
|
16
12
|
}
|
|
17
|
-
export type InstantLinkRetryStepFn = (
|
|
13
|
+
export type InstantLinkRetryStepFn = () => Promise<InstantLinkResultType>;
|
|
18
14
|
export type InstantLinkStartInput = PhoneNumberInput;
|
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.InstantLinkResultType =
|
|
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;
|
|
3
|
+
exports.InstantLinkResultType = void 0;
|
|
14
4
|
var InstantLinkResultType;
|
|
15
5
|
(function (InstantLinkResultType) {
|
|
16
6
|
InstantLinkResultType[InstantLinkResultType["OnResend"] = 0] = "OnResend";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MobileAuthImplementation } from '../authenticator-builder';
|
|
2
2
|
import { AuthResponseStatus } from './auth-response-status';
|
|
3
|
-
export type AuthRequest = V1ClientDeviceFido2RegisterStart | V1ClientDeviceFido2RegisterFinish | V1ClientDeviceFido2VerifyStart | V1ClientDeviceFido2VerifyFinish | V1ClientDevicePassiveRegister | V1ClientDevicePassiveVerify | V1ClientUserResponse | V1ClientAnyError | V1ClientChallenge | V1ClientMobileInstantLinkStart | V1ClientMobileInstantStart | V1ClientMobileInstantFinish | V1ClientOtpStart | V1ClientOtpFinish;
|
|
3
|
+
export type AuthRequest = V1ClientDeviceFido2RegisterStart | V1ClientDeviceFido2RegisterFinish | V1ClientDeviceFido2VerifyStart | V1ClientDeviceFido2VerifyFinish | V1ClientDevicePassiveRegister | V1ClientDevicePassiveVerify | V1ClientUserResponse | V1ClientAnyError | V1ClientChallenge | V1ClientMobileInstantLinkStart | V1ClientMobileInstantStart | V1ClientMobileInstantFinish | V1ClientOtpStart | V1ClientOtpFinish | V1ClientPpbFinish;
|
|
4
4
|
export type AuthRegistration = PassiveRegistration | Fido2Registration;
|
|
5
5
|
export interface PublicKey {
|
|
6
6
|
id: string;
|
|
@@ -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;
|
|
@@ -100,3 +98,7 @@ export interface Signal {
|
|
|
100
98
|
results?: string;
|
|
101
99
|
error?: string;
|
|
102
100
|
}
|
|
101
|
+
export interface V1ClientPpbFinish {
|
|
102
|
+
requestId: string;
|
|
103
|
+
operationId: string;
|
|
104
|
+
}
|
|
@@ -1,66 +1,62 @@
|
|
|
1
1
|
import AuthMessage from './auth-message';
|
|
2
2
|
export default interface AuthResponse {
|
|
3
3
|
next: string;
|
|
4
|
+
data?: ResponseData;
|
|
4
5
|
error?: AuthFailure;
|
|
5
6
|
refreshDeviceTrust?: boolean;
|
|
6
7
|
}
|
|
8
|
+
export interface ResponseData {
|
|
9
|
+
}
|
|
7
10
|
export interface AuthFailure {
|
|
8
11
|
message: string;
|
|
9
12
|
code: number;
|
|
10
13
|
}
|
|
14
|
+
export type RegisterStartAuthResponse = AuthResponse;
|
|
15
|
+
export type RegisterFinishAuthResponse = AuthResponse;
|
|
16
|
+
export type DeviceRegisterAuthResponse = AuthResponse;
|
|
17
|
+
export type VerifyStartAuthResponse = AuthResponse;
|
|
18
|
+
export type VerifyFinishAuthResponse = AuthResponse;
|
|
19
|
+
export type MobileStartAuthResponse = AuthResponse;
|
|
20
|
+
export type OtpStartResponse = AuthResponse;
|
|
21
|
+
export type OtpFinishResponse = AuthResponse;
|
|
22
|
+
export type InstantLinkStartResponse = AuthResponse;
|
|
23
|
+
export type PpbStartResponse = AuthResponse;
|
|
24
|
+
export type PpbFinishResponse = AuthResponse;
|
|
11
25
|
export interface RegisterStartAuthResponseData {
|
|
12
26
|
credCreateOptions?: PublicKeyCredentialCreationOptions;
|
|
13
27
|
credRequestOptions?: PublicKeyCredentialRequestOptions;
|
|
14
28
|
}
|
|
15
|
-
export interface
|
|
16
|
-
data: RegisterStartAuthResponseData;
|
|
17
|
-
}
|
|
18
|
-
export interface RegisterFinishAuthResponseData {
|
|
29
|
+
export interface RegisterFinishAuthResponseData extends ResponseData {
|
|
19
30
|
deviceId: string;
|
|
20
31
|
passkey: boolean;
|
|
21
32
|
scanMessage?: AuthMessage;
|
|
22
33
|
}
|
|
23
|
-
export interface RegisterFinishAuthResponse extends AuthResponse {
|
|
24
|
-
data: RegisterFinishAuthResponseData;
|
|
25
|
-
}
|
|
26
|
-
export type DeviceRegisterAuthResponse = RegisterFinishAuthResponse;
|
|
27
34
|
export interface VerifyStartAuthResponseData {
|
|
28
35
|
credRequestOptions: PublicKeyCredentialRequestOptions;
|
|
29
36
|
}
|
|
30
|
-
export interface
|
|
31
|
-
data: VerifyStartAuthResponseData;
|
|
32
|
-
}
|
|
33
|
-
export interface VerifyFinishAuthResponseData {
|
|
37
|
+
export interface VerifyFinishAuthResponseData extends ResponseData {
|
|
34
38
|
scanMessage?: AuthMessage;
|
|
35
39
|
deviceId?: string;
|
|
36
40
|
passkey?: boolean;
|
|
37
41
|
}
|
|
38
|
-
export interface VerifyFinishAuthResponse extends AuthResponse {
|
|
39
|
-
data?: VerifyFinishAuthResponseData;
|
|
40
|
-
}
|
|
41
42
|
export interface ChallengeResponse extends AuthResponse {
|
|
42
43
|
deviceId?: string;
|
|
43
44
|
challenge?: string;
|
|
44
45
|
ttl?: number;
|
|
45
46
|
receivedAt?: number;
|
|
46
47
|
}
|
|
47
|
-
export interface MobileStartAuthResponseData {
|
|
48
|
+
export interface MobileStartAuthResponseData extends ResponseData {
|
|
48
49
|
redirectUrl?: string;
|
|
49
50
|
}
|
|
50
|
-
export interface MobileStartAuthResponse extends AuthResponse {
|
|
51
|
-
data?: MobileStartAuthResponseData;
|
|
52
|
-
}
|
|
53
|
-
export interface OtpStartResponse extends AuthResponse {
|
|
54
|
-
data?: OtpResponseData;
|
|
55
|
-
}
|
|
56
|
-
export interface OtpFinishResponse extends AuthResponse {
|
|
57
|
-
data?: OtpResponseData;
|
|
58
|
-
}
|
|
59
51
|
export interface OtpResponseData {
|
|
60
52
|
code?: number;
|
|
61
53
|
message?: string;
|
|
62
54
|
}
|
|
63
|
-
export interface InstantLinkStartResponse extends AuthResponse {
|
|
64
|
-
data?: InstantLinkResponseData;
|
|
65
|
-
}
|
|
66
55
|
export type InstantLinkResponseData = OtpResponseData;
|
|
56
|
+
export interface PpbOperation {
|
|
57
|
+
operationId: string;
|
|
58
|
+
oneTimeSecret: string;
|
|
59
|
+
}
|
|
60
|
+
export interface PpbResponseData extends ResponseData {
|
|
61
|
+
ppb?: PpbOperation;
|
|
62
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="webappsec-credential-management" />
|
|
2
2
|
import AuthMessage from './auth-message';
|
|
3
3
|
import { AuthRequest, Signal } from './auth-request';
|
|
4
|
-
import AuthResponse from './auth-response';
|
|
4
|
+
import AuthResponse, { ResponseData } from './auth-response';
|
|
5
5
|
import AuthTokenClaims, { UserVerificationLevel } from './auth-token-claims';
|
|
6
6
|
import { DeviceRegistration } from './device-auth';
|
|
7
7
|
import Platform, { AuthSessionIntegration, MessageChannel, RequestSigner } from './platform';
|
|
@@ -16,6 +16,7 @@ export default class AuthSession implements AuthSessionIntegration {
|
|
|
16
16
|
readonly channels: Set<MessageChannel>;
|
|
17
17
|
private readonly log;
|
|
18
18
|
lastStep?: string;
|
|
19
|
+
lastData?: ResponseData;
|
|
19
20
|
credential?: CredentialType;
|
|
20
21
|
authMessage?: AuthMessage;
|
|
21
22
|
uvLevel?: UserVerificationLevel;
|
|
@@ -32,7 +33,11 @@ export default class AuthSession implements AuthSessionIntegration {
|
|
|
32
33
|
closeAllMessageChannels(): void;
|
|
33
34
|
getDeviceRegistration(): Promise<DeviceRegistration | null>;
|
|
34
35
|
embedFpResultToDeviceRegistration(registration: DeviceRegistration): Promise<DeviceRegistration>;
|
|
36
|
+
private getCurrentTimestampInSeconds;
|
|
37
|
+
markNewFptts(ts?: number): void;
|
|
38
|
+
resetFptts(): void;
|
|
35
39
|
getFingerprintData(): Promise<Signal | undefined>;
|
|
36
40
|
shouldCollectFP(): boolean;
|
|
41
|
+
shouldRefreshFpSignal(currentTimestamp: number): boolean;
|
|
37
42
|
private parseJwt;
|
|
38
43
|
}
|
|
@@ -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");
|
|
@@ -84,6 +85,7 @@ class AuthSession {
|
|
|
84
85
|
this.platform.deviceAuth.reset();
|
|
85
86
|
}
|
|
86
87
|
}
|
|
88
|
+
this.lastData = response.data;
|
|
87
89
|
resolve(response);
|
|
88
90
|
})
|
|
89
91
|
.catch(reject);
|
|
@@ -164,8 +166,7 @@ class AuthSession {
|
|
|
164
166
|
onClose(reason, code);
|
|
165
167
|
});
|
|
166
168
|
channel.addEventListener('error', (event) => {
|
|
167
|
-
this.log.debug('Message channel encountered an error');
|
|
168
|
-
this.log.debug(event);
|
|
169
|
+
this.log.debug('Message channel encountered an error', event);
|
|
169
170
|
});
|
|
170
171
|
channel.addEventListener('message', (event) => {
|
|
171
172
|
this.log.trace('Message channel received a message');
|
|
@@ -227,17 +228,28 @@ class AuthSession {
|
|
|
227
228
|
resolve(registration);
|
|
228
229
|
})
|
|
229
230
|
.catch((error) => {
|
|
230
|
-
const errorMsg = `Unexpected error happened during Fingerprint data collection: ${error
|
|
231
|
-
this.log.warn(
|
|
231
|
+
const errorMsg = `Unexpected error happened during Fingerprint data collection: ${auth_error_1.default.extractMessage(error)}`;
|
|
232
|
+
this.log.warn(error);
|
|
232
233
|
registration.setFpSignal({ error: errorMsg });
|
|
233
234
|
});
|
|
234
235
|
});
|
|
235
236
|
}
|
|
237
|
+
getCurrentTimestampInSeconds() {
|
|
238
|
+
const currentTimestampMs = Date.now();
|
|
239
|
+
const currentTimestampSeconds = Math.floor(currentTimestampMs / 1000);
|
|
240
|
+
return currentTimestampSeconds;
|
|
241
|
+
}
|
|
242
|
+
markNewFptts(ts) {
|
|
243
|
+
this.settings.fingerPrintTimestamp = ts ? ts : this.getCurrentTimestampInSeconds();
|
|
244
|
+
}
|
|
245
|
+
resetFptts() {
|
|
246
|
+
this.settings.fingerPrintTimestamp = null;
|
|
247
|
+
}
|
|
236
248
|
getFingerprintData() {
|
|
237
249
|
return new Promise((resolve) => {
|
|
238
250
|
var fpPromise = this.platform.getFpPromise();
|
|
239
251
|
if (!this.shouldCollectFP()) {
|
|
240
|
-
this.log.
|
|
252
|
+
this.log.trace('Fingerprint is not enabled from AuthToken');
|
|
241
253
|
resolve(undefined);
|
|
242
254
|
}
|
|
243
255
|
else if (!fpPromise) {
|
|
@@ -245,7 +257,12 @@ class AuthSession {
|
|
|
245
257
|
this.log.warn(msg);
|
|
246
258
|
resolve({ error: msg });
|
|
247
259
|
}
|
|
260
|
+
else if (!this.shouldRefreshFpSignal(this.getCurrentTimestampInSeconds())) {
|
|
261
|
+
this.log.trace('Existing FP signal is not yet expired, skip new collection');
|
|
262
|
+
resolve(undefined);
|
|
263
|
+
}
|
|
248
264
|
else {
|
|
265
|
+
this.log.trace('Collect new FP signal');
|
|
249
266
|
fpPromise
|
|
250
267
|
.then((fp) => fp.get())
|
|
251
268
|
.then((result) => {
|
|
@@ -260,8 +277,8 @@ class AuthSession {
|
|
|
260
277
|
}
|
|
261
278
|
})
|
|
262
279
|
.catch((error) => {
|
|
263
|
-
const msg = `Error in collecting Fingerprint data: ${error
|
|
264
|
-
this.log.warn(
|
|
280
|
+
const msg = `Error in collecting Fingerprint data: ${auth_error_1.default.extractMessage(error)}`;
|
|
281
|
+
this.log.warn(error);
|
|
265
282
|
resolve({ error: msg });
|
|
266
283
|
});
|
|
267
284
|
}
|
|
@@ -274,6 +291,16 @@ class AuthSession {
|
|
|
274
291
|
}
|
|
275
292
|
return false;
|
|
276
293
|
}
|
|
294
|
+
shouldRefreshFpSignal(currentTimestamp) {
|
|
295
|
+
var _a, _b, _c, _d;
|
|
296
|
+
if (!this.settings.deviceId || !this.settings.fingerPrintTimestamp) {
|
|
297
|
+
this.resetFptts();
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
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;
|
|
301
|
+
const interval = currentTimestamp - this.settings.fingerPrintTimestamp;
|
|
302
|
+
return interval >= refreshRate;
|
|
303
|
+
}
|
|
277
304
|
parseJwt(token) {
|
|
278
305
|
return JSON.parse(atob(token.split('.')[1]));
|
|
279
306
|
}
|
|
@@ -9,6 +9,8 @@ export interface PassiveAuthenticator {
|
|
|
9
9
|
}
|
|
10
10
|
export interface InstantAuthenticator {
|
|
11
11
|
}
|
|
12
|
+
export interface PresentAuthenticator {
|
|
13
|
+
}
|
|
12
14
|
export interface InstantLinkAuthenticator {
|
|
13
15
|
mnp: boolean;
|
|
14
16
|
amnrp?: boolean;
|
|
@@ -21,27 +23,44 @@ export interface UniversalAuthenticator {
|
|
|
21
23
|
endp: string;
|
|
22
24
|
ftu: string;
|
|
23
25
|
}
|
|
24
|
-
export interface
|
|
26
|
+
export interface PpbAuthenticator {
|
|
27
|
+
endp: string;
|
|
28
|
+
}
|
|
29
|
+
export interface DeviceAuthenticators {
|
|
30
|
+
pasv?: PassiveAuthenticator;
|
|
31
|
+
unvsl?: UniversalAuthenticator;
|
|
32
|
+
}
|
|
33
|
+
export interface MobileAuthenticators {
|
|
25
34
|
pasv?: PassiveAuthenticator;
|
|
26
35
|
inst?: InstantAuthenticator;
|
|
27
36
|
inln?: InstantLinkAuthenticator;
|
|
28
37
|
otp?: OtpAuthenticator;
|
|
29
38
|
unvsl?: UniversalAuthenticator;
|
|
30
39
|
}
|
|
40
|
+
export interface UserAuthenticators {
|
|
41
|
+
pasv?: PassiveAuthenticator;
|
|
42
|
+
prst?: PresentAuthenticator;
|
|
43
|
+
ppb?: PpbAuthenticator;
|
|
44
|
+
}
|
|
31
45
|
export interface Signals {
|
|
32
46
|
fpt?: boolean;
|
|
47
|
+
fptrr?: number;
|
|
33
48
|
dwn?: boolean;
|
|
34
49
|
}
|
|
35
50
|
export interface DeviceAuthSubjectClaim {
|
|
36
|
-
auths:
|
|
51
|
+
auths: DeviceAuthenticators;
|
|
37
52
|
sgnls?: Signals;
|
|
38
53
|
}
|
|
39
54
|
export interface MobileAuthSubjectClaim {
|
|
40
|
-
auths:
|
|
55
|
+
auths: MobileAuthenticators;
|
|
56
|
+
}
|
|
57
|
+
export interface UserAuthSubjectClaim {
|
|
58
|
+
auths: UserAuthenticators;
|
|
41
59
|
}
|
|
42
60
|
export interface AuthSubjectsClaim {
|
|
43
61
|
dev?: DeviceAuthSubjectClaim;
|
|
44
62
|
mob?: MobileAuthSubjectClaim;
|
|
63
|
+
usr?: UserAuthSubjectClaim;
|
|
45
64
|
}
|
|
46
65
|
export interface AuthClaim {
|
|
47
66
|
id: string;
|
|
@@ -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,23 +70,26 @@ 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 {
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
if ((_a = registration.getSignals().fingerprint) === null || _a === void 0 ? void 0 : _a.results) {
|
|
79
|
+
session.markNewFptts();
|
|
80
|
+
}
|
|
81
|
+
const data = response.data;
|
|
82
|
+
const deviceId = data === null || data === void 0 ? void 0 : data.deviceId;
|
|
79
83
|
if (!deviceId) {
|
|
80
84
|
reject(new auth_error_1.default('Failed to register device, returned deviceId is null or empty', 0, response.next));
|
|
81
85
|
}
|
|
82
86
|
session.settings.deviceId = deviceId;
|
|
83
|
-
if (
|
|
84
|
-
session.settings.fidoPasskeyRegistered =
|
|
87
|
+
if (data.passkey != null) {
|
|
88
|
+
session.settings.fidoPasskeyRegistered = data.passkey;
|
|
85
89
|
}
|
|
86
90
|
registration.deviceId = deviceId;
|
|
87
91
|
this.log.debug('Device ID: ' + deviceId);
|
|
88
|
-
|
|
89
|
-
if (data && data.scanMessage) {
|
|
92
|
+
if (data.scanMessage) {
|
|
90
93
|
session.authMessage = data.scanMessage;
|
|
91
94
|
}
|
|
92
95
|
session.platform.deviceAuth
|
|
@@ -113,10 +116,14 @@ class DevicePassiveSilentStep {
|
|
|
113
116
|
signals: registration.getSignals(),
|
|
114
117
|
})
|
|
115
118
|
.then((response) => {
|
|
119
|
+
var _a;
|
|
116
120
|
if (response.error) {
|
|
117
121
|
reject(new auth_error_1.default(response.error.message, response.error.code, response.next, false));
|
|
118
122
|
}
|
|
119
123
|
else {
|
|
124
|
+
if ((_a = registration.getSignals().fingerprint) === null || _a === void 0 ? void 0 : _a.results) {
|
|
125
|
+
session.markNewFptts();
|
|
126
|
+
}
|
|
120
127
|
resolve(response.next);
|
|
121
128
|
}
|
|
122
129
|
})
|
|
@@ -30,13 +30,17 @@ class DevicePassiveActions extends auth_status_actions_1.AuthStatusActions {
|
|
|
30
30
|
signals: signals,
|
|
31
31
|
})
|
|
32
32
|
.then((response) => {
|
|
33
|
-
var _a
|
|
33
|
+
var _a;
|
|
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 data = response.data;
|
|
42
|
+
const creationOptions = data === null || data === void 0 ? void 0 : data.credCreateOptions;
|
|
43
|
+
const requestOptions = data === null || data === void 0 ? void 0 : data.credRequestOptions;
|
|
40
44
|
if (creationOptions) {
|
|
41
45
|
this.createCredentials(session, displayName, creationOptions, response)
|
|
42
46
|
.then(resolve)
|
|
@@ -67,7 +71,8 @@ class DevicePassiveActions extends auth_status_actions_1.AuthStatusActions {
|
|
|
67
71
|
reject(new auth_error_1.default(response.error.message, response.error.code, response.next, false));
|
|
68
72
|
}
|
|
69
73
|
else {
|
|
70
|
-
const
|
|
74
|
+
const data = response.data;
|
|
75
|
+
const options = data === null || data === void 0 ? void 0 : data.credRequestOptions;
|
|
71
76
|
this.getCredentials(session, options, response).then(resolve).catch(reject);
|
|
72
77
|
}
|
|
73
78
|
})
|
|
@@ -141,7 +146,7 @@ class DevicePassiveActions extends auth_status_actions_1.AuthStatusActions {
|
|
|
141
146
|
parseCredRequestOptions(response, session) {
|
|
142
147
|
return new Promise((resolve, reject) => {
|
|
143
148
|
const data = response.data;
|
|
144
|
-
if (data.credRequestOptions) {
|
|
149
|
+
if (data === null || data === void 0 ? void 0 : data.credRequestOptions) {
|
|
145
150
|
const requestOptions = data.credRequestOptions;
|
|
146
151
|
this.getCredentials(session, requestOptions, response).then(resolve).catch(reject);
|
|
147
152
|
}
|
|
@@ -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
|
}
|