@prove-identity/prove-auth 2.8.2 → 2.9.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/README.md +8 -9
- package/build/bundle/release/prove-auth.js +1 -1
- package/build/lib/index.d.ts +2 -2
- package/build/lib/proveauth/authenticator-builder.d.ts +3 -0
- package/build/lib/proveauth/authenticator-builder.js +7 -3
- package/build/lib/proveauth/internal/auth-request.d.ts +1 -0
- package/build/lib/proveauth/internal/auth-response.d.ts +5 -1
- package/build/lib/proveauth/internal/auth-session.d.ts +3 -1
- package/build/lib/proveauth/internal/auth-session.js +50 -9
- package/build/lib/proveauth/internal/auth-status-actions.js +3 -3
- package/build/lib/proveauth/internal/device-passive-register-step.d.ts +1 -0
- package/build/lib/proveauth/internal/device-passive-register-step.js +55 -39
- package/build/lib/proveauth/internal/device-passive-silent-step.js +4 -0
- package/build/lib/proveauth/internal/device-passive-step.d.ts +12 -4
- package/build/lib/proveauth/internal/device-passive-step.js +172 -52
- package/build/lib/proveauth/internal/device-passive-stepup-step.d.ts +2 -1
- package/build/lib/proveauth/internal/device-passive-stepup-step.js +25 -3
- package/build/lib/proveauth/internal/device-passive-verify-step.d.ts +3 -2
- package/build/lib/proveauth/internal/device-passive-verify-step.js +29 -11
- package/build/lib/proveauth/internal/fido-options-error.d.ts +30 -0
- package/build/lib/proveauth/internal/fido-options-error.js +161 -0
- package/build/lib/proveauth/internal/main-authenticator.js +1 -1
- package/build/lib/proveauth/internal/mobile-instantlink-step.js +36 -29
- package/build/lib/proveauth/internal/mobile-otp-step.d.ts +3 -0
- package/build/lib/proveauth/internal/mobile-otp-step.js +115 -67
- package/build/lib/proveauth/internal/scan-message-step.js +1 -1
- package/build/lib/proveauth/internal/settings.js +1 -0
- package/build/lib/proveauth/internal/web-socket-close-reasons.d.ts +15 -0
- package/build/lib/proveauth/internal/web-socket-close-reasons.js +19 -0
- package/build/lib/proveauth/version.d.ts +1 -1
- package/build/lib/proveauth/version.js +1 -1
- package/package.json +1 -1
|
@@ -9,7 +9,8 @@ export interface AuthFailure {
|
|
|
9
9
|
code: number;
|
|
10
10
|
}
|
|
11
11
|
export interface RegisterStartAuthResponseData {
|
|
12
|
-
credCreateOptions
|
|
12
|
+
credCreateOptions?: PublicKeyCredentialCreationOptions;
|
|
13
|
+
credRequestOptions?: PublicKeyCredentialRequestOptions;
|
|
13
14
|
}
|
|
14
15
|
export interface RegisterStartAuthResponse extends AuthResponse {
|
|
15
16
|
data: RegisterStartAuthResponseData;
|
|
@@ -17,6 +18,7 @@ export interface RegisterStartAuthResponse extends AuthResponse {
|
|
|
17
18
|
export interface RegisterFinishAuthResponseData {
|
|
18
19
|
deviceId: string;
|
|
19
20
|
passkey: boolean;
|
|
21
|
+
scanMessage?: AuthMessage;
|
|
20
22
|
}
|
|
21
23
|
export interface RegisterFinishAuthResponse extends AuthResponse {
|
|
22
24
|
data: RegisterFinishAuthResponseData;
|
|
@@ -30,6 +32,8 @@ export interface VerifyStartAuthResponse extends AuthResponse {
|
|
|
30
32
|
}
|
|
31
33
|
export interface VerifyFinishAuthResponseData {
|
|
32
34
|
scanMessage?: AuthMessage;
|
|
35
|
+
deviceId?: string;
|
|
36
|
+
passkey?: boolean;
|
|
33
37
|
}
|
|
34
38
|
export interface VerifyFinishAuthResponse extends AuthResponse {
|
|
35
39
|
data?: VerifyFinishAuthResponseData;
|
|
@@ -6,6 +6,7 @@ import AuthTokenClaims, { UserVerificationLevel } from './auth-token-claims';
|
|
|
6
6
|
import { DeviceRegistration } from './device-auth';
|
|
7
7
|
import Platform, { AuthSessionIntegration, MessageChannel, RequestSigner } from './platform';
|
|
8
8
|
import Settings from './settings';
|
|
9
|
+
import { WebSocketCloseReasons } from './web-socket-close-reasons';
|
|
9
10
|
export default class AuthSession implements AuthSessionIntegration {
|
|
10
11
|
readonly platform: Platform;
|
|
11
12
|
readonly authToken?: string;
|
|
@@ -26,7 +27,8 @@ export default class AuthSession implements AuthSessionIntegration {
|
|
|
26
27
|
get next(): string;
|
|
27
28
|
constructor(settings: Settings, platform: Platform, authToken?: string);
|
|
28
29
|
fetchFromBackend(query: string, body: AuthRequest): Promise<AuthResponse>;
|
|
29
|
-
|
|
30
|
+
static parseCloseEvent(event: CloseEvent): [WebSocketCloseReasons, number];
|
|
31
|
+
createMessageChannel(query: string, onClose: (reason: WebSocketCloseReasons, code: number) => void, onError: (message: string) => void, onMessage: (data: string) => void): MessageChannel;
|
|
30
32
|
closeAllMessageChannels(): void;
|
|
31
33
|
getDeviceRegistration(): Promise<DeviceRegistration | null>;
|
|
32
34
|
embedFpResultToDeviceRegistration(registration: DeviceRegistration): Promise<DeviceRegistration>;
|
|
@@ -5,9 +5,9 @@ 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"));
|
|
9
8
|
const auth_token_claims_1 = require("./auth-token-claims");
|
|
10
9
|
const error_code_1 = __importDefault(require("./error-code"));
|
|
10
|
+
const web_socket_close_reasons_1 = require("./web-socket-close-reasons");
|
|
11
11
|
class AuthSession {
|
|
12
12
|
get namespace() {
|
|
13
13
|
var _a;
|
|
@@ -91,6 +91,48 @@ class AuthSession {
|
|
|
91
91
|
.catch(reject);
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
|
+
static parseCloseEvent(event) {
|
|
95
|
+
var reason = web_socket_close_reasons_1.WebSocketCloseReasons.UNKNOWN_REASON;
|
|
96
|
+
switch (event.code) {
|
|
97
|
+
case 1000:
|
|
98
|
+
reason = web_socket_close_reasons_1.WebSocketCloseReasons.NORMAL_CLOSURE;
|
|
99
|
+
break;
|
|
100
|
+
case 1001:
|
|
101
|
+
reason = web_socket_close_reasons_1.WebSocketCloseReasons.GOING_AWAY;
|
|
102
|
+
break;
|
|
103
|
+
case 1002:
|
|
104
|
+
reason = web_socket_close_reasons_1.WebSocketCloseReasons.PROTOCOL_ERROR;
|
|
105
|
+
break;
|
|
106
|
+
case 1003:
|
|
107
|
+
reason = web_socket_close_reasons_1.WebSocketCloseReasons.UNSUPPORTED_DATA;
|
|
108
|
+
break;
|
|
109
|
+
case 1005:
|
|
110
|
+
reason = web_socket_close_reasons_1.WebSocketCloseReasons.NO_STATUS_RECEIVED;
|
|
111
|
+
break;
|
|
112
|
+
case 1006:
|
|
113
|
+
reason = web_socket_close_reasons_1.WebSocketCloseReasons.ABNORMAL_CLOSURE;
|
|
114
|
+
break;
|
|
115
|
+
case 1007:
|
|
116
|
+
reason = web_socket_close_reasons_1.WebSocketCloseReasons.INVALID_FRAME_PAYLOAD_DATA;
|
|
117
|
+
break;
|
|
118
|
+
case 1008:
|
|
119
|
+
reason = web_socket_close_reasons_1.WebSocketCloseReasons.POLICY_VIOLATION;
|
|
120
|
+
break;
|
|
121
|
+
case 1009:
|
|
122
|
+
reason = web_socket_close_reasons_1.WebSocketCloseReasons.MESSAGE_TOO_BIG;
|
|
123
|
+
break;
|
|
124
|
+
case 1010:
|
|
125
|
+
reason = web_socket_close_reasons_1.WebSocketCloseReasons.MANDATORY_EXTENSION;
|
|
126
|
+
break;
|
|
127
|
+
case 1011:
|
|
128
|
+
reason = web_socket_close_reasons_1.WebSocketCloseReasons.INTERNAL_ERROR;
|
|
129
|
+
break;
|
|
130
|
+
case 1015:
|
|
131
|
+
reason = web_socket_close_reasons_1.WebSocketCloseReasons.TLS_HANDSHAKE;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
return [reason, event.code];
|
|
135
|
+
}
|
|
94
136
|
createMessageChannel(query, onClose, onError, onMessage) {
|
|
95
137
|
if (!this.authToken) {
|
|
96
138
|
throw new Error('Authentication token is not initialized, cannot create MessageChannel');
|
|
@@ -113,18 +155,17 @@ class AuthSession {
|
|
|
113
155
|
this.channels.delete(channel);
|
|
114
156
|
}
|
|
115
157
|
}, KEEP_ALIVE_INTERVAL);
|
|
116
|
-
channel.addEventListener('close', (
|
|
117
|
-
|
|
158
|
+
channel.addEventListener('close', (event) => {
|
|
159
|
+
var statusCode = event.code ? `with status code: ${event.code}` : 'without status code';
|
|
160
|
+
this.log.debug(`Message channel is closed ${statusCode}`);
|
|
118
161
|
clearInterval(keepAlive);
|
|
119
|
-
onClose();
|
|
120
162
|
this.channels.delete(channel);
|
|
163
|
+
const [reason, code] = AuthSession.parseCloseEvent(event);
|
|
164
|
+
onClose(reason, code);
|
|
121
165
|
});
|
|
122
166
|
channel.addEventListener('error', (event) => {
|
|
123
|
-
this.log.
|
|
124
|
-
this.log.
|
|
125
|
-
clearInterval(keepAlive);
|
|
126
|
-
onError(auth_error_1.default.extractMessage(event));
|
|
127
|
-
this.channels.delete(channel);
|
|
167
|
+
this.log.debug('Message channel encountered an error');
|
|
168
|
+
this.log.debug(event);
|
|
128
169
|
});
|
|
129
170
|
channel.addEventListener('message', (event) => {
|
|
130
171
|
this.log.trace('Message channel received a message');
|
|
@@ -14,9 +14,9 @@ class AuthStatusActions {
|
|
|
14
14
|
var gotResponse = false;
|
|
15
15
|
this.log.trace('Waiting for auth status');
|
|
16
16
|
return new Promise((resolve, reject) => {
|
|
17
|
-
const channel = session.createMessageChannel('/v1/client/status?token=' + encodeURIComponent(session.authToken), () => {
|
|
17
|
+
const channel = session.createMessageChannel('/v1/client/status?token=' + encodeURIComponent(session.authToken), (reason, code) => {
|
|
18
18
|
if (!gotResponse) {
|
|
19
|
-
reject(new auth_error_1.default(
|
|
19
|
+
reject(new auth_error_1.default(`Failed to receive secondary authentication with status code ${code}. ${reason}`));
|
|
20
20
|
}
|
|
21
21
|
}, (errorMessage) => {
|
|
22
22
|
gotResponse = true;
|
|
@@ -27,7 +27,7 @@ class AuthStatusActions {
|
|
|
27
27
|
this.log.debug(('Secondary authentication status: ' + data));
|
|
28
28
|
const response = JSON.parse(data);
|
|
29
29
|
if (response.error) {
|
|
30
|
-
reject(new auth_error_1.default(response.error.message, response.error.code, response.next));
|
|
30
|
+
reject(new auth_error_1.default(response.error.message, response.error.code, response.next, false));
|
|
31
31
|
}
|
|
32
32
|
else {
|
|
33
33
|
session.lastStep = response.next;
|
|
@@ -5,6 +5,7 @@ export default class DevicePassiveRegisterStep implements AuthStep {
|
|
|
5
5
|
private readonly log;
|
|
6
6
|
readonly name = "device/passive/register";
|
|
7
7
|
execute(session: AuthSession): Promise<string>;
|
|
8
|
+
static hasAssertionResponse(session: AuthSession): boolean;
|
|
8
9
|
private finishRegistration;
|
|
9
10
|
private getFido2Registration;
|
|
10
11
|
}
|
|
@@ -7,6 +7,7 @@ const base64_1 = __importDefault(require("../common/base64"));
|
|
|
7
7
|
const logger_1 = require("../common/logger");
|
|
8
8
|
const auth_error_1 = __importDefault(require("./auth-error"));
|
|
9
9
|
const error_code_1 = __importDefault(require("./error-code"));
|
|
10
|
+
const device_passive_verify_step_1 = __importDefault(require("./device-passive-verify-step"));
|
|
10
11
|
class DevicePassiveRegisterStep {
|
|
11
12
|
constructor() {
|
|
12
13
|
this.log = logger_1.LoggerFactory.getLogger('device-passive-register-step');
|
|
@@ -14,46 +15,56 @@ class DevicePassiveRegisterStep {
|
|
|
14
15
|
}
|
|
15
16
|
execute(session) {
|
|
16
17
|
return new Promise((resolve, reject) => {
|
|
17
|
-
session
|
|
18
|
-
.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
.
|
|
27
|
-
.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
.
|
|
41
|
-
.then((
|
|
42
|
-
.then((
|
|
43
|
-
devRegistration
|
|
44
|
-
|
|
45
|
-
.
|
|
46
|
-
.then(() =>
|
|
18
|
+
if (DevicePassiveRegisterStep.hasAssertionResponse(session)) {
|
|
19
|
+
device_passive_verify_step_1.default.runFidoVerify(this.log, session).then(resolve).catch(reject);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
session
|
|
23
|
+
.getDeviceRegistration()
|
|
24
|
+
.then((devRegistration) => {
|
|
25
|
+
if (devRegistration) {
|
|
26
|
+
session
|
|
27
|
+
.embedFpResultToDeviceRegistration(devRegistration)
|
|
28
|
+
.then((devRegistration) => session.embedDarwiniumResultToDeviceRegistration(devRegistration))
|
|
29
|
+
.then((devRegistration) => {
|
|
30
|
+
this.finishRegistration(session, [this.getFido2Registration(session)], devRegistration.getSignals())
|
|
31
|
+
.then(resolve)
|
|
32
|
+
.catch(reject);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
session.platform.deviceAuth
|
|
37
|
+
.createRegistration({
|
|
38
|
+
namespace: session.namespace,
|
|
39
|
+
endpoint: session.backendOrigin,
|
|
40
|
+
})
|
|
41
|
+
.then((devRegistration) => session.embedFpResultToDeviceRegistration(devRegistration))
|
|
42
|
+
.then((devRegistration) => session.embedDarwiniumResultToDeviceRegistration(devRegistration))
|
|
43
|
+
.then((devRegistration) => {
|
|
44
|
+
devRegistration
|
|
45
|
+
.getAuthRegistration(session.challenge)
|
|
46
|
+
.then((authRegistration) => this.finishRegistration(session, [this.getFido2Registration(session), authRegistration], devRegistration.getSignals()))
|
|
47
|
+
.then((next) => {
|
|
48
|
+
devRegistration.deviceId = session.settings.deviceId;
|
|
49
|
+
session.platform.deviceAuth
|
|
50
|
+
.storeRegistration(devRegistration)
|
|
51
|
+
.then(() => resolve(next))
|
|
52
|
+
.catch(reject);
|
|
53
|
+
})
|
|
47
54
|
.catch(reject);
|
|
48
55
|
})
|
|
49
56
|
.catch(reject);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
.catch(reject);
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
.catch(reject);
|
|
60
|
+
}
|
|
55
61
|
});
|
|
56
62
|
}
|
|
63
|
+
static hasAssertionResponse(session) {
|
|
64
|
+
const credential = session.credential;
|
|
65
|
+
const assertion = credential.response;
|
|
66
|
+
return assertion && 'authenticatorData' in assertion && 'signature' in assertion;
|
|
67
|
+
}
|
|
57
68
|
finishRegistration(session, registrations, signals) {
|
|
58
69
|
return new Promise((resolve, reject) => {
|
|
59
70
|
session
|
|
@@ -65,13 +76,18 @@ class DevicePassiveRegisterStep {
|
|
|
65
76
|
})
|
|
66
77
|
.then((response) => {
|
|
67
78
|
if (response.error) {
|
|
68
|
-
reject(new auth_error_1.default(response.error.message, response.error.code, response.next));
|
|
79
|
+
reject(new auth_error_1.default(response.error.message, response.error.code, response.next, false));
|
|
69
80
|
}
|
|
70
81
|
else {
|
|
71
82
|
const data = response.data;
|
|
72
|
-
if (data
|
|
73
|
-
|
|
74
|
-
|
|
83
|
+
if (data) {
|
|
84
|
+
if (data.deviceId) {
|
|
85
|
+
session.settings.deviceId = data.deviceId;
|
|
86
|
+
session.settings.fidoPasskeyRegistered = true;
|
|
87
|
+
}
|
|
88
|
+
if (data.scanMessage) {
|
|
89
|
+
session.authMessage = data.scanMessage;
|
|
90
|
+
}
|
|
75
91
|
resolve(response.next);
|
|
76
92
|
}
|
|
77
93
|
else {
|
|
@@ -87,6 +87,10 @@ class DevicePassiveSilentStep {
|
|
|
87
87
|
session.settings.fidoPasskeyRegistered = deviceRegisterAuthResp.data.passkey;
|
|
88
88
|
registration.deviceId = deviceId;
|
|
89
89
|
this.log.debug('Device ID: ' + deviceId);
|
|
90
|
+
const data = response.data;
|
|
91
|
+
if (data && data.scanMessage) {
|
|
92
|
+
session.authMessage = data.scanMessage;
|
|
93
|
+
}
|
|
90
94
|
session.platform.deviceAuth
|
|
91
95
|
.storeRegistration(registration)
|
|
92
96
|
.then(() => resolve(response.next))
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import AuthSession from './auth-session';
|
|
2
2
|
import AuthStep from './auth-step';
|
|
3
|
-
import { DeviceRole } from '../authenticator-builder';
|
|
3
|
+
import { DeviceRole, PasskeyAlreadyExistCallback } from '../authenticator-builder';
|
|
4
4
|
import { AuthStatusActions } from './auth-status-actions';
|
|
5
|
+
import { Signals } from './auth-request';
|
|
5
6
|
export declare class DevicePassiveActions extends AuthStatusActions {
|
|
6
7
|
protected log: import("../common/logger").Logger;
|
|
7
8
|
private readonly getDisplayName;
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
private handler?;
|
|
10
|
+
static readonly NO_REQUEST_CREDS_FOUND = "Passkey has already been registered but found no CredentialRequestOptions in the fido/register/start response payload";
|
|
11
|
+
static readonly NO_CREDS_FOUND = "Neither credCreateOptions nor credRequestOptions are found in the fido/register/start response payload";
|
|
12
|
+
static readonly USER_NOT_ACCEPTING_RESPONSE = "User not accepting to continue by reusing the existing passkey with user response";
|
|
13
|
+
protected constructor(getDisplayName?: () => string | null, handler?: PasskeyAlreadyExistCallback);
|
|
14
|
+
protected register(session: AuthSession, signals?: Signals): Promise<string>;
|
|
10
15
|
protected verify(session: AuthSession): Promise<string>;
|
|
16
|
+
private createCredentials;
|
|
17
|
+
private parseCredRequestOptions;
|
|
18
|
+
private getCredentials;
|
|
11
19
|
}
|
|
12
20
|
export default class DevicePassiveStep extends DevicePassiveActions implements AuthStep {
|
|
13
21
|
static readonly NAME = "device/passive";
|
|
14
22
|
readonly name = "device/passive";
|
|
15
23
|
private readonly role;
|
|
16
|
-
constructor(getDisplayName?: () => string | null, role?: DeviceRole);
|
|
24
|
+
constructor(getDisplayName?: () => string | null, handler?: PasskeyAlreadyExistCallback, role?: DeviceRole);
|
|
17
25
|
execute(session: AuthSession): Promise<string>;
|
|
18
26
|
}
|
|
@@ -11,57 +11,51 @@ const auth_token_claims_1 = require("./auth-token-claims");
|
|
|
11
11
|
const auth_error_1 = __importDefault(require("./auth-error"));
|
|
12
12
|
const authenticator_builder_1 = require("../authenticator-builder");
|
|
13
13
|
const auth_status_actions_1 = require("./auth-status-actions");
|
|
14
|
+
const fido_options_error_1 = require("./fido-options-error");
|
|
15
|
+
const auth_response_status_1 = require("./auth-response-status");
|
|
14
16
|
class DevicePassiveActions extends auth_status_actions_1.AuthStatusActions {
|
|
15
|
-
constructor(getDisplayName) {
|
|
17
|
+
constructor(getDisplayName, handler) {
|
|
16
18
|
super();
|
|
17
19
|
this.log = logger_1.LoggerFactory.getLogger('device-passive-actions');
|
|
18
20
|
this.getDisplayName = getDisplayName ? getDisplayName : () => null;
|
|
21
|
+
this.handler = handler;
|
|
19
22
|
}
|
|
20
|
-
register(session) {
|
|
21
|
-
this.log.trace('Registering');
|
|
23
|
+
register(session, signals) {
|
|
24
|
+
this.log.trace('Registering Passkey');
|
|
22
25
|
return new Promise((resolve, reject) => {
|
|
23
26
|
const displayName = this.getDisplayName();
|
|
24
27
|
session
|
|
25
28
|
.fetchFromBackend('/v1/client/device/fido2/register/start', {
|
|
26
29
|
displayName: displayName ? displayName : undefined,
|
|
30
|
+
signals: signals,
|
|
27
31
|
})
|
|
28
32
|
.then((response) => {
|
|
33
|
+
var _a, _b;
|
|
29
34
|
if (response.error) {
|
|
30
|
-
reject(new auth_error_1.default(response.error.message, response.error.code, response.next));
|
|
35
|
+
reject(new auth_error_1.default(response.error.message, response.error.code, response.next, false));
|
|
31
36
|
}
|
|
32
37
|
else {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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;
|
|
40
|
+
if (creationOptions) {
|
|
41
|
+
this.createCredentials(session, displayName, creationOptions, response)
|
|
42
|
+
.then(resolve)
|
|
43
|
+
.catch(reject);
|
|
44
|
+
}
|
|
45
|
+
else if (requestOptions) {
|
|
46
|
+
this.getCredentials(session, requestOptions, response).then(resolve).catch(reject);
|
|
38
47
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
});
|
|
48
|
+
else {
|
|
49
|
+
const error = new auth_error_1.default(DevicePassiveActions.NO_CREDS_FOUND);
|
|
50
|
+
reject(error);
|
|
43
51
|
}
|
|
44
|
-
session.platform.webauthn
|
|
45
|
-
.createCredentials({
|
|
46
|
-
publicKey: options,
|
|
47
|
-
})
|
|
48
|
-
.then((credential) => {
|
|
49
|
-
if (!credential) {
|
|
50
|
-
reject(new auth_error_1.default('Failed to create FIDO2 credentials'));
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
session.credential = credential;
|
|
54
|
-
resolve(response.next);
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
.catch(reject);
|
|
58
52
|
}
|
|
59
53
|
})
|
|
60
54
|
.catch(reject);
|
|
61
55
|
});
|
|
62
56
|
}
|
|
63
57
|
verify(session) {
|
|
64
|
-
this.log.trace('Verifying');
|
|
58
|
+
this.log.trace('Verifying Passkey');
|
|
65
59
|
return new Promise((resolve, reject) => {
|
|
66
60
|
if (session.settings.deviceId) {
|
|
67
61
|
session
|
|
@@ -69,46 +63,150 @@ class DevicePassiveActions extends auth_status_actions_1.AuthStatusActions {
|
|
|
69
63
|
deviceId: session.settings.deviceId,
|
|
70
64
|
})
|
|
71
65
|
.then((response) => {
|
|
72
|
-
var _a;
|
|
73
66
|
if (response.error) {
|
|
74
|
-
reject(new auth_error_1.default(response.error.message, response.error.code, response.next));
|
|
67
|
+
reject(new auth_error_1.default(response.error.message, response.error.code, response.next, false));
|
|
75
68
|
}
|
|
76
69
|
else {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
70
|
+
const options = response.data.credRequestOptions;
|
|
71
|
+
this.getCredentials(session, options, response).then(resolve).catch(reject);
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
.catch(reject);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
reject(new auth_error_1.default('Failed to start verification, DeviceId is missing'));
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
createCredentials(session, displayName, options, response) {
|
|
82
|
+
this.log.trace('Trying create new FIDO credentials');
|
|
83
|
+
return new Promise((resolve, reject) => {
|
|
84
|
+
options.challenge = base64_1.default.bufferDecode(options.challenge);
|
|
85
|
+
options.user.id = base64_1.default.bufferDecode(options.user.id);
|
|
86
|
+
if (displayName) {
|
|
87
|
+
options.user.displayName = displayName;
|
|
88
|
+
}
|
|
89
|
+
if (options.excludeCredentials) {
|
|
90
|
+
options.excludeCredentials.forEach((i) => {
|
|
91
|
+
i.id = base64_1.default.bufferDecode(i.id);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (options.authenticatorSelection) {
|
|
95
|
+
options.authenticatorSelection.residentKey = 'preferred';
|
|
96
|
+
options.authenticatorSelection.requireResidentKey = false;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
options.authenticatorSelection = {
|
|
100
|
+
residentKey: 'preferred',
|
|
101
|
+
requireResidentKey: false,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const fidoCreationOptions = {
|
|
105
|
+
publicKey: options,
|
|
106
|
+
};
|
|
107
|
+
session.platform.webauthn
|
|
108
|
+
.createCredentials(fidoCreationOptions)
|
|
109
|
+
.then((credential) => {
|
|
110
|
+
if (!credential) {
|
|
111
|
+
reject(new auth_error_1.default('Failed to create FIDO2 credentials'));
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
session.credential = credential;
|
|
115
|
+
resolve(response.next);
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
.catch((error) => {
|
|
119
|
+
const fidoCreationError = fido_options_error_1.FidoOptionsError.identifyRegistrationError({
|
|
120
|
+
error: error,
|
|
121
|
+
options: fidoCreationOptions,
|
|
122
|
+
platform: session.platform,
|
|
123
|
+
});
|
|
124
|
+
if (fidoCreationError instanceof fido_options_error_1.FidoOptionsError &&
|
|
125
|
+
fidoCreationError.name === 'InvalidStateError') {
|
|
126
|
+
if (this.handler) {
|
|
127
|
+
this.log.trace('Passkey handler callback has been defined');
|
|
128
|
+
this.handler()
|
|
129
|
+
.then((userResponse) => {
|
|
130
|
+
if (userResponse === auth_response_status_1.AuthResponseStatus.Accept) {
|
|
131
|
+
this.parseCredRequestOptions(response, session).then(resolve).catch(reject);
|
|
91
132
|
}
|
|
92
133
|
else {
|
|
93
|
-
|
|
94
|
-
|
|
134
|
+
reject(new auth_error_1.default(`${DevicePassiveActions.USER_NOT_ACCEPTING_RESPONSE}: ${userResponse}`));
|
|
135
|
+
return;
|
|
95
136
|
}
|
|
96
137
|
})
|
|
97
138
|
.catch(reject);
|
|
98
139
|
}
|
|
99
|
-
|
|
100
|
-
|
|
140
|
+
else {
|
|
141
|
+
this.log.trace('Passkey handler callback is null, go ahead with passkey discoverable as default behavior');
|
|
142
|
+
this.parseCredRequestOptions(response, session).then(resolve).catch(reject);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
reject(fidoCreationError);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
parseCredRequestOptions(response, session) {
|
|
152
|
+
return new Promise((resolve, reject) => {
|
|
153
|
+
const data = response.data;
|
|
154
|
+
if (data.credRequestOptions) {
|
|
155
|
+
const requestOptions = data.credRequestOptions;
|
|
156
|
+
this.getCredentials(session, requestOptions, response).then(resolve).catch(reject);
|
|
101
157
|
}
|
|
102
158
|
else {
|
|
103
|
-
reject(new auth_error_1.default(
|
|
159
|
+
reject(new auth_error_1.default(DevicePassiveActions.NO_REQUEST_CREDS_FOUND));
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
getCredentials(session, options, response) {
|
|
164
|
+
this.log.trace('Trying get existing FIDO credentials');
|
|
165
|
+
return new Promise((resolve, reject) => {
|
|
166
|
+
var _a, _b;
|
|
167
|
+
if (((_a = options.allowCredentials) === null || _a === void 0 ? void 0 : _a.length) == 0) {
|
|
168
|
+
reject(new auth_error_1.default('AllowCredentials array should not be empty when call getCredentials()'));
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
options.challenge = base64_1.default.bufferDecode(options.challenge);
|
|
172
|
+
if (options.allowCredentials) {
|
|
173
|
+
(_b = options.allowCredentials) === null || _b === void 0 ? void 0 : _b.forEach((i) => {
|
|
174
|
+
i.id = base64_1.default.bufferDecode(i.id);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const fidoRequestOptions = {
|
|
178
|
+
publicKey: options,
|
|
179
|
+
};
|
|
180
|
+
session.platform.webauthn
|
|
181
|
+
.getCredentials(fidoRequestOptions)
|
|
182
|
+
.then((credential) => {
|
|
183
|
+
if (!credential) {
|
|
184
|
+
reject(new auth_error_1.default('Failed to load FIDO2 credentials'));
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
session.credential = credential;
|
|
188
|
+
resolve(response.next);
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
.catch((error) => {
|
|
192
|
+
const fidoAuthenticationError = fido_options_error_1.FidoOptionsError.identifyAuthenticationError({
|
|
193
|
+
error: error,
|
|
194
|
+
options: fidoRequestOptions,
|
|
195
|
+
platform: session.platform,
|
|
196
|
+
});
|
|
197
|
+
reject(fidoAuthenticationError);
|
|
198
|
+
});
|
|
104
199
|
}
|
|
105
200
|
});
|
|
106
201
|
}
|
|
107
202
|
}
|
|
203
|
+
DevicePassiveActions.NO_REQUEST_CREDS_FOUND = 'Passkey has already been registered but found no CredentialRequestOptions in the fido/register/start response payload';
|
|
204
|
+
DevicePassiveActions.NO_CREDS_FOUND = 'Neither credCreateOptions nor credRequestOptions are found in the fido/register/start response payload';
|
|
205
|
+
DevicePassiveActions.USER_NOT_ACCEPTING_RESPONSE = 'User not accepting to continue by reusing the existing passkey with user response';
|
|
108
206
|
exports.DevicePassiveActions = DevicePassiveActions;
|
|
109
207
|
class DevicePassiveStep extends DevicePassiveActions {
|
|
110
|
-
constructor(getDisplayName, role) {
|
|
111
|
-
super(getDisplayName);
|
|
208
|
+
constructor(getDisplayName, handler, role) {
|
|
209
|
+
super(getDisplayName, handler);
|
|
112
210
|
this.name = DevicePassiveStep.NAME;
|
|
113
211
|
this.role = role !== null && role !== void 0 ? role : authenticator_builder_1.DeviceRole.Primary;
|
|
114
212
|
this.log = logger_1.LoggerFactory.getLogger('device-passive-step');
|
|
@@ -127,7 +225,29 @@ class DevicePassiveStep extends DevicePassiveActions {
|
|
|
127
225
|
}
|
|
128
226
|
return Promise.resolve(device_passive_silent_step_1.default.NAME);
|
|
129
227
|
}
|
|
130
|
-
return
|
|
228
|
+
return new Promise((resolve, reject) => {
|
|
229
|
+
session
|
|
230
|
+
.getFingerprintData()
|
|
231
|
+
.then((signal) => {
|
|
232
|
+
const signals = {
|
|
233
|
+
fingerprint: signal,
|
|
234
|
+
};
|
|
235
|
+
session.embedDarwiniumSignal(signals);
|
|
236
|
+
this.register(session, signals).then(resolve).catch(reject);
|
|
237
|
+
})
|
|
238
|
+
.catch((error) => {
|
|
239
|
+
const errorMsg = `Unexpected error happened during Fingerprint data collection: ${error.message}`;
|
|
240
|
+
this.log.warn(errorMsg);
|
|
241
|
+
this.log.warn(error);
|
|
242
|
+
const signals = {
|
|
243
|
+
fingerprint: {
|
|
244
|
+
error: errorMsg,
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
session.embedDarwiniumSignal(signals);
|
|
248
|
+
this.register(session, signals).then(resolve).catch(reject);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
131
251
|
}
|
|
132
252
|
}
|
|
133
253
|
DevicePassiveStep.NAME = 'device/passive';
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { PasskeyAlreadyExistCallback } from '../authenticator-builder';
|
|
1
2
|
import AuthSession from './auth-session';
|
|
2
3
|
import AuthStep from './auth-step';
|
|
3
4
|
import { DevicePassiveActions } from './device-passive-step';
|
|
4
5
|
export default class DevicePassiveStepupStep extends DevicePassiveActions implements AuthStep {
|
|
5
6
|
static readonly NAME = "device/passive/stepup";
|
|
6
7
|
readonly name = "device/passive/stepup";
|
|
7
|
-
constructor(getDisplayName?: () => string | null);
|
|
8
|
+
constructor(getDisplayName?: () => string | null, handler?: PasskeyAlreadyExistCallback);
|
|
8
9
|
execute(session: AuthSession): Promise<string>;
|
|
9
10
|
}
|