electron-webauthn 0.0.15 → 0.0.17
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 +357 -113
- package/dist/create/authorization-controller.d.ts +1 -1
- package/dist/create/authorization-controller.js +1 -0
- package/dist/create/handler.d.ts +37 -29
- package/dist/create/handler.js +194 -142
- package/dist/create/internal-handler.d.ts +34 -0
- package/dist/create/internal-handler.js +154 -0
- package/dist/helpers/index.js +2 -0
- package/dist/helpers/origin.js +1 -1
- package/dist/helpers/validation.d.ts +1 -1
- package/dist/index.d.ts +0 -1
- package/package.json +2 -2
- package/dist/get/updated-handler.d.ts +0 -1
- package/dist/get/updated-handler.js +0 -1
- package/dist/helpers.d.ts +0 -9
- package/dist/helpers.js +0 -63
- package/dist/objc/authentication-services/as-authorization-public-key-credential-registration.d.ts +0 -14
- package/dist/objc/authentication-services/as-authorization-public-key-credential-registration.js +0 -2
- package/dist/prf.d.ts +0 -5
- package/dist/prf.js +0 -5
- package/dist/test.d.ts +0 -1
- package/dist/test.js +0 -5
package/dist/create/handler.d.ts
CHANGED
|
@@ -1,33 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
authenticatorData: Buffer;
|
|
8
|
-
attachment: AuthenticatorAttachment;
|
|
9
|
-
transports: string[];
|
|
10
|
-
isResidentKey: boolean;
|
|
1
|
+
export interface CreateCredentialSuccessData {
|
|
2
|
+
credentialId: string;
|
|
3
|
+
clientDataJSON: string;
|
|
4
|
+
attestationObject: string;
|
|
5
|
+
authData: string;
|
|
6
|
+
publicKey: string;
|
|
11
7
|
publicKeyAlgorithm: number;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
transports: string[];
|
|
9
|
+
extensions: {
|
|
10
|
+
credProps?: {
|
|
11
|
+
rk: boolean;
|
|
12
|
+
};
|
|
13
|
+
prf?: {
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
results: {
|
|
16
|
+
first?: string;
|
|
17
|
+
second?: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
largeBlob?: {
|
|
21
|
+
supported?: boolean;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
interface WebauthnCreateRequestOptions {
|
|
26
|
+
currentOrigin: string;
|
|
27
|
+
topFrameOrigin: string | undefined;
|
|
28
|
+
isPublicSuffix?: (domain: string) => boolean;
|
|
29
|
+
nativeWindowHandle: Buffer;
|
|
17
30
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export type CredentialCreationExtensions = (typeof VALID_EXTENSIONS)[number];
|
|
22
|
-
interface CreateCredentialAdditionalOptions {
|
|
23
|
-
topFrameOrigin?: string;
|
|
24
|
-
userDisplayName?: string;
|
|
25
|
-
largeBlobSupport?: "required" | "preferred" | "unspecified";
|
|
26
|
-
prf?: PRFInput;
|
|
31
|
+
interface CreateCredentialSuccessResult {
|
|
32
|
+
success: true;
|
|
33
|
+
data: CreateCredentialSuccessData;
|
|
27
34
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
interface CreateCredentialErrorResult {
|
|
36
|
+
success: false;
|
|
37
|
+
error: "TypeError" | "AbortError" | "NotAllowedError" | "SecurityError" | "InvalidStateError";
|
|
31
38
|
}
|
|
32
|
-
|
|
33
|
-
export
|
|
39
|
+
export type CreateCredentialResult = CreateCredentialSuccessResult | CreateCredentialErrorResult;
|
|
40
|
+
export declare function createCredential(publicKeyOptions: PublicKeyCredentialCreationOptions | undefined, additionalOptions: WebauthnCreateRequestOptions): Promise<CreateCredentialResult>;
|
|
41
|
+
export {};
|
package/dist/create/handler.js
CHANGED
|
@@ -1,154 +1,206 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
import { bufferSourceToBuffer, bufferToBase64Url } from "../helpers/index.js";
|
|
2
|
+
import { isRpIdAllowedForOrigin } from "../helpers/rpid.js";
|
|
3
|
+
import { isNumber, isObject, isString } from "../helpers/validation.js";
|
|
4
|
+
import { createCredentialInternal, } from "./internal-handler.js";
|
|
5
|
+
function getExtensionsConfiguration(extensionsData) {
|
|
6
|
+
if (!(extensionsData && typeof extensionsData === "object")) {
|
|
7
|
+
return {
|
|
8
|
+
extensions: [],
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const extensions = [];
|
|
12
|
+
let largeBlobSupport;
|
|
13
|
+
if (isObject(extensionsData.largeBlob)) {
|
|
14
|
+
extensions.push("largeBlob");
|
|
15
|
+
const largeBlobConfig = extensionsData.largeBlob;
|
|
16
|
+
if (largeBlobConfig.support === "required") {
|
|
17
|
+
largeBlobSupport = "required";
|
|
18
|
+
}
|
|
19
|
+
else if (largeBlobConfig.support === "preferred") {
|
|
20
|
+
largeBlobSupport = "preferred";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
let prf;
|
|
24
|
+
if (isObject(extensionsData.prf)) {
|
|
25
|
+
const prfEval = extensionsData.prf.eval;
|
|
26
|
+
if (prfEval) {
|
|
27
|
+
const first = bufferSourceToBuffer(prfEval.first);
|
|
28
|
+
const second = bufferSourceToBuffer(prfEval.second);
|
|
29
|
+
if (first) {
|
|
30
|
+
prf = {
|
|
31
|
+
first: first ? first : null,
|
|
32
|
+
second: second ? second : undefined,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
console.warn("[electron-webauthn] prf is enabled but prf.first is not valid, skipping PRF evaluation");
|
|
37
|
+
}
|
|
35
38
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
extensions,
|
|
42
|
+
largeBlobSupport,
|
|
43
|
+
prf,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export async function createCredential(publicKeyOptions, additionalOptions) {
|
|
47
|
+
if (!publicKeyOptions) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const rpInfo = publicKeyOptions.rp;
|
|
51
|
+
if (!isObject(rpInfo)) {
|
|
52
|
+
return { success: false, error: "TypeError" };
|
|
53
|
+
}
|
|
54
|
+
let rpId = rpInfo.id;
|
|
55
|
+
if (!rpId) {
|
|
56
|
+
try {
|
|
57
|
+
const url = new URL(additionalOptions.currentOrigin);
|
|
58
|
+
rpId = url.hostname;
|
|
59
|
+
}
|
|
60
|
+
catch { }
|
|
61
|
+
}
|
|
62
|
+
if (!isString(rpId)) {
|
|
63
|
+
return { success: false, error: "TypeError" };
|
|
64
|
+
}
|
|
65
|
+
let timeout = publicKeyOptions.timeout;
|
|
66
|
+
if (!isNumber(timeout) || timeout <= 0) {
|
|
67
|
+
timeout = 10 * 60 * 1000;
|
|
68
|
+
}
|
|
69
|
+
else if (timeout > 60 * 60 * 1000) {
|
|
70
|
+
timeout = 60 * 60 * 1000;
|
|
71
|
+
}
|
|
72
|
+
const challenge = bufferSourceToBuffer(publicKeyOptions.challenge);
|
|
73
|
+
if (!challenge) {
|
|
74
|
+
return { success: false, error: "TypeError" };
|
|
75
|
+
}
|
|
76
|
+
if (!isObject(publicKeyOptions.user)) {
|
|
77
|
+
return { success: false, error: "TypeError" };
|
|
78
|
+
}
|
|
79
|
+
const userName = publicKeyOptions.user.name;
|
|
80
|
+
const userDisplayName = publicKeyOptions.user.displayName;
|
|
81
|
+
if (!isString(userName) || !isString(userDisplayName)) {
|
|
82
|
+
return { success: false, error: "TypeError" };
|
|
83
|
+
}
|
|
84
|
+
const userID = bufferSourceToBuffer(publicKeyOptions.user.id);
|
|
85
|
+
if (!userID) {
|
|
86
|
+
return { success: false, error: "TypeError" };
|
|
87
|
+
}
|
|
88
|
+
const attestationPreference = publicKeyOptions.attestation;
|
|
89
|
+
if (attestationPreference && !isString(attestationPreference)) {
|
|
90
|
+
return { success: false, error: "TypeError" };
|
|
91
|
+
}
|
|
92
|
+
const pubKeyCredParams = publicKeyOptions.pubKeyCredParams;
|
|
93
|
+
const supportedAlgorithmIdentifiers = [];
|
|
94
|
+
if (pubKeyCredParams) {
|
|
95
|
+
if (Array.isArray(pubKeyCredParams)) {
|
|
96
|
+
for (const param of pubKeyCredParams) {
|
|
97
|
+
if (!isObject(param))
|
|
98
|
+
continue;
|
|
99
|
+
if (!isNumber(param.alg))
|
|
100
|
+
continue;
|
|
101
|
+
supportedAlgorithmIdentifiers.push({
|
|
102
|
+
type: "public-key",
|
|
103
|
+
algorithm: param.alg,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
39
106
|
}
|
|
40
107
|
else {
|
|
41
|
-
|
|
108
|
+
return { success: false, error: "TypeError" };
|
|
42
109
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
110
|
+
}
|
|
111
|
+
const excludeCredentials = [];
|
|
112
|
+
if (publicKeyOptions.excludeCredentials &&
|
|
113
|
+
Array.isArray(publicKeyOptions.excludeCredentials)) {
|
|
114
|
+
for (const excludeCredential of publicKeyOptions.excludeCredentials) {
|
|
115
|
+
if (!isObject(excludeCredential))
|
|
116
|
+
continue;
|
|
117
|
+
if (excludeCredential.type !== "public-key")
|
|
118
|
+
continue;
|
|
119
|
+
const idBuffer = bufferSourceToBuffer(excludeCredential.id);
|
|
120
|
+
if (!idBuffer)
|
|
121
|
+
continue;
|
|
122
|
+
excludeCredentials.push({
|
|
123
|
+
id: idBuffer,
|
|
124
|
+
transports: excludeCredential.transports,
|
|
125
|
+
});
|
|
46
126
|
}
|
|
47
127
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
let userVerificationPreference =
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (enabledExtensions.includes("prf")) {
|
|
65
|
-
if (additionalOptions.prf) {
|
|
66
|
-
const inputValues = createPRFInput(additionalOptions.prf);
|
|
67
|
-
const prfInput = createASAuthorizationPublicKeyCredentialPRFRegistrationInput(inputValues);
|
|
68
|
-
platformKeyRequest.setPrf$(prfInput);
|
|
128
|
+
const { extensions, largeBlobSupport, prf } = getExtensionsConfiguration(publicKeyOptions.extensions);
|
|
129
|
+
let residentKeyRequired = false;
|
|
130
|
+
let userVerificationPreference = "preferred";
|
|
131
|
+
if (publicKeyOptions.authenticatorSelection) {
|
|
132
|
+
if (publicKeyOptions.authenticatorSelection.residentKey === "required") {
|
|
133
|
+
residentKeyRequired = true;
|
|
134
|
+
}
|
|
135
|
+
else if (publicKeyOptions.authenticatorSelection.requireResidentKey) {
|
|
136
|
+
residentKeyRequired = true;
|
|
137
|
+
}
|
|
138
|
+
const userVerifyParam = publicKeyOptions.authenticatorSelection.userVerification;
|
|
139
|
+
if (userVerifyParam === "required") {
|
|
140
|
+
userVerificationPreference = "required";
|
|
141
|
+
}
|
|
142
|
+
else if (userVerifyParam === "discouraged") {
|
|
143
|
+
userVerificationPreference = "discouraged";
|
|
69
144
|
}
|
|
70
145
|
else {
|
|
71
|
-
|
|
146
|
+
userVerificationPreference = "preferred";
|
|
72
147
|
}
|
|
73
148
|
}
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
const { clientDataHash, clientDataBuffer } = generateClientDataInfo(clientData);
|
|
78
|
-
setControllerState(authController, clientDataHash, supportedAlgorithmIdentifiers, residentKeyRequired, excludeCredentials);
|
|
79
|
-
const finished = (_success) => {
|
|
80
|
-
removeControllerState(authController);
|
|
81
|
-
};
|
|
82
|
-
const delegate = createAuthorizationControllerDelegate({
|
|
83
|
-
didCompleteWithAuthorization: (_, authorization) => {
|
|
84
|
-
const credential = authorization.credential();
|
|
85
|
-
console.log("Authorization succeeded:", credential);
|
|
86
|
-
const credentialIdBuffer = bufferFromNSDataDirect(credential.credentialID());
|
|
87
|
-
const attestationObjectBuffer = bufferFromNSDataDirect(credential.rawAttestationObject());
|
|
88
|
-
const attestation = parseAttestationObject(attestationObjectBuffer);
|
|
89
|
-
const publicKey = attestation.authenticatorData.credential.publicKey;
|
|
90
|
-
const ec2Key = publicKey.ec2();
|
|
91
|
-
const publicKeySPKI = encodeEC2PublicKeyToSPKI(ec2Key.x, ec2Key.y);
|
|
92
|
-
const authenticatorData = Buffer.from(JSON.stringify(attestation.authenticatorData));
|
|
93
|
-
let authenticatorAttachment = "platform";
|
|
94
|
-
if (credential.attachment() ===
|
|
95
|
-
ASAuthorizationPublicKeyCredentialAttachment.ASAuthorizationPublicKeyCredentialAttachmentCrossPlatform) {
|
|
96
|
-
authenticatorAttachment = "cross-platform";
|
|
97
|
-
}
|
|
98
|
-
let isLargeBlobSupported = null;
|
|
99
|
-
if (enabledExtensions.includes("largeBlob")) {
|
|
100
|
-
const largeBlobOutput = credential.largeBlob();
|
|
101
|
-
if (largeBlobOutput) {
|
|
102
|
-
isLargeBlobSupported = largeBlobOutput.isSupported();
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
let prfFirst = null;
|
|
106
|
-
let prfSecond = null;
|
|
107
|
-
let isPRFSupported = null;
|
|
108
|
-
if (enabledExtensions.includes("prf")) {
|
|
109
|
-
const prfOutput = credential.prf();
|
|
110
|
-
if (prfOutput) {
|
|
111
|
-
const prfFirstData = prfOutput.first();
|
|
112
|
-
const prfSecondData = prfOutput.second();
|
|
113
|
-
if (prfFirstData) {
|
|
114
|
-
prfFirst = bufferFromNSDataDirect(prfFirstData);
|
|
115
|
-
}
|
|
116
|
-
if (prfSecondData) {
|
|
117
|
-
prfSecond = bufferFromNSDataDirect(prfSecondData);
|
|
118
|
-
}
|
|
119
|
-
isPRFSupported = prfOutput.isSupported();
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
const data = {
|
|
123
|
-
credentialId: credentialIdBuffer,
|
|
124
|
-
clientDataJSON: clientDataBuffer,
|
|
125
|
-
attestationObject: attestationObjectBuffer,
|
|
126
|
-
authenticatorData,
|
|
127
|
-
attachment: authenticatorAttachment,
|
|
128
|
-
transports: ["hybrid", "internal"],
|
|
129
|
-
isResidentKey: true,
|
|
130
|
-
publicKeyAlgorithm: publicKey.algorithm(),
|
|
131
|
-
publicKey: publicKeySPKI,
|
|
132
|
-
isLargeBlobSupported,
|
|
133
|
-
isPRFSupported,
|
|
134
|
-
prfFirst,
|
|
135
|
-
prfSecond,
|
|
136
|
-
};
|
|
137
|
-
resolve(data);
|
|
138
|
-
finished(true);
|
|
139
|
-
},
|
|
140
|
-
didCompleteWithError: (_, error) => {
|
|
141
|
-
const parsedError = error;
|
|
142
|
-
const errorMessage = parsedError.localizedDescription().UTF8String();
|
|
143
|
-
console.error("Authorization failed:", errorMessage);
|
|
144
|
-
reject(new Error(errorMessage));
|
|
145
|
-
finished(false);
|
|
146
|
-
},
|
|
149
|
+
const { currentOrigin, topFrameOrigin, isPublicSuffix, nativeWindowHandle } = additionalOptions;
|
|
150
|
+
const isRpIdAllowed = isRpIdAllowedForOrigin(currentOrigin, rpId, {
|
|
151
|
+
isPublicSuffix,
|
|
147
152
|
});
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
+
if (!isRpIdAllowed.ok) {
|
|
154
|
+
return { success: false, error: "NotAllowedError" };
|
|
155
|
+
}
|
|
156
|
+
const result = await createCredentialInternal(rpId, challenge, userName, userID, nativeWindowHandle, currentOrigin, extensions, attestationPreference, supportedAlgorithmIdentifiers, excludeCredentials, residentKeyRequired, userVerificationPreference, {
|
|
157
|
+
topFrameOrigin,
|
|
158
|
+
largeBlobSupport,
|
|
159
|
+
prf,
|
|
160
|
+
}).catch((error) => {
|
|
161
|
+
console.error("Error creating credential", error);
|
|
162
|
+
console.log("error.message", error.message);
|
|
163
|
+
if (error.message.includes("(com.apple.AuthenticationServices.AuthorizationError error 1006.)")) {
|
|
164
|
+
return "InvalidStateError";
|
|
165
|
+
}
|
|
166
|
+
if (error.message.startsWith("The operation couldn’t be completed.")) {
|
|
167
|
+
return "NotAllowedError";
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
});
|
|
171
|
+
if (typeof result === "string") {
|
|
172
|
+
return { success: false, error: result };
|
|
173
|
+
}
|
|
174
|
+
const data = {
|
|
175
|
+
credentialId: bufferToBase64Url(result.credentialId),
|
|
176
|
+
clientDataJSON: bufferToBase64Url(result.clientDataJSON),
|
|
177
|
+
attestationObject: bufferToBase64Url(result.attestationObject),
|
|
178
|
+
authData: bufferToBase64Url(result.authenticatorData),
|
|
179
|
+
publicKey: bufferToBase64Url(result.publicKey),
|
|
180
|
+
publicKeyAlgorithm: result.publicKeyAlgorithm,
|
|
181
|
+
transports: result.transports,
|
|
182
|
+
extensions: {},
|
|
183
|
+
};
|
|
184
|
+
if (publicKeyOptions.extensions?.credProps) {
|
|
185
|
+
data.extensions.credProps = {
|
|
186
|
+
rk: result.isResidentKey,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (result.isLargeBlobSupported !== null) {
|
|
190
|
+
data.extensions.largeBlob = {
|
|
191
|
+
supported: result.isLargeBlobSupported,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
if (result.isPRFSupported !== null) {
|
|
195
|
+
const prfFirst = result.prfFirst;
|
|
196
|
+
const prfSecond = result.prfSecond;
|
|
197
|
+
data.extensions.prf = {
|
|
198
|
+
enabled: result.isPRFSupported,
|
|
199
|
+
results: {
|
|
200
|
+
first: prfFirst ? bufferToBase64Url(prfFirst) : undefined,
|
|
201
|
+
second: prfSecond ? bufferToBase64Url(prfSecond) : undefined,
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
return { success: true, data };
|
|
153
206
|
}
|
|
154
|
-
export { createCredential };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type PRFInput } from "../helpers/prf.js";
|
|
2
|
+
import { type PublicKeyCredentialParams } from "./authorization-controller.js";
|
|
3
|
+
export interface CreateCredentialResult {
|
|
4
|
+
credentialId: Buffer;
|
|
5
|
+
clientDataJSON: Buffer;
|
|
6
|
+
attestationObject: Buffer;
|
|
7
|
+
authenticatorData: Buffer;
|
|
8
|
+
attachment: AuthenticatorAttachment;
|
|
9
|
+
transports: string[];
|
|
10
|
+
isResidentKey: boolean;
|
|
11
|
+
publicKeyAlgorithm: number;
|
|
12
|
+
publicKey: Buffer;
|
|
13
|
+
isLargeBlobSupported: boolean | null;
|
|
14
|
+
isPRFSupported: boolean | null;
|
|
15
|
+
prfFirst: Buffer | null;
|
|
16
|
+
prfSecond: Buffer | null;
|
|
17
|
+
}
|
|
18
|
+
type CredentialUserVerificationPreference = "required" | "preferred" | "discouraged";
|
|
19
|
+
type CredentialAttestationPreference = "direct" | "enterprise" | "indirect" | "none";
|
|
20
|
+
declare const VALID_EXTENSIONS: readonly ["largeBlob", "prf"];
|
|
21
|
+
export type CredentialCreationExtensions = (typeof VALID_EXTENSIONS)[number];
|
|
22
|
+
export type LargeBlobSupport = "required" | "preferred" | "unspecified";
|
|
23
|
+
interface CreateCredentialAdditionalOptions {
|
|
24
|
+
topFrameOrigin?: string;
|
|
25
|
+
userDisplayName?: string;
|
|
26
|
+
largeBlobSupport?: LargeBlobSupport;
|
|
27
|
+
prf?: PRFInput;
|
|
28
|
+
}
|
|
29
|
+
export interface ExcludeCredential {
|
|
30
|
+
id: Buffer;
|
|
31
|
+
transports?: string[];
|
|
32
|
+
}
|
|
33
|
+
declare function createCredentialInternal(rpid: string, challenge: Buffer, username: string, userID: Buffer, nativeWindowHandle: Buffer, origin: string, enabledExtensions: CredentialCreationExtensions[], attestation: CredentialAttestationPreference, supportedAlgorithmIdentifiers: PublicKeyCredentialParams[], excludeCredentials: ExcludeCredential[], residentKeyRequired?: boolean, userVerification?: CredentialUserVerificationPreference, additionalOptions?: CreateCredentialAdditionalOptions): Promise<CreateCredentialResult>;
|
|
34
|
+
export { createCredentialInternal };
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { generateClientDataInfo } from "../helpers/client-data.js";
|
|
2
|
+
import { generateWebauthnClientData } from "../helpers/client-data.js";
|
|
3
|
+
import { PromiseWithResolvers } from "../helpers/index.js";
|
|
4
|
+
import { encodeEC2PublicKeyToSPKI } from "../helpers/public-key.js";
|
|
5
|
+
import { createPresentationContextProviderFromNativeWindowHandle } from "../helpers/presentation.js";
|
|
6
|
+
import { createPRFInput } from "../helpers/prf.js";
|
|
7
|
+
import { createAuthorizationControllerDelegate } from "../objc/authentication-services/as-authorization-controller-delegate.js";
|
|
8
|
+
import { createPlatformPublicKeyCredentialProvider } from "../objc/authentication-services/as-authorization-platform-public-key-credential-provider.js";
|
|
9
|
+
import { createASAuthorizationPublicKeyCredentialLargeBlobRegistrationInput } from "../objc/authentication-services/as-authorization-public-key-credential-large-blob-registration-input.js";
|
|
10
|
+
import { ASAuthorizationPublicKeyCredentialPRFRegistrationInput, createASAuthorizationPublicKeyCredentialPRFRegistrationInput, } from "../objc/authentication-services/as-authorization-public-key-credential-prf-registration-input.js";
|
|
11
|
+
import { ASAuthorizationPublicKeyCredentialAttestationKind } from "../objc/authentication-services/enums/as-authorization-public-key-credential-attestation-kind.js";
|
|
12
|
+
import { ASAuthorizationPublicKeyCredentialLargeBlobSupportRequirement } from "../objc/authentication-services/enums/as-authorization-public-key-credential-large-blob-support-requirement.js";
|
|
13
|
+
import { ASAuthorizationPublicKeyCredentialUserVerificationPreference } from "../objc/authentication-services/enums/as-authorization-public-key-credential-user-verification-preference.js";
|
|
14
|
+
import { NSArrayFromObjects } from "../objc/foundation/nsarray.js";
|
|
15
|
+
import { bufferFromNSDataDirect, NSDataFromBuffer, } from "../objc/foundation/nsdata.js";
|
|
16
|
+
import { NSStringFromString } from "../objc/foundation/nsstring.js";
|
|
17
|
+
import { removeControllerState, setControllerState, WebauthnCreateController, } from "./authorization-controller.js";
|
|
18
|
+
import { parseAttestationObject } from "@oslojs/webauthn";
|
|
19
|
+
import { ASAuthorizationPublicKeyCredentialAttachment } from "../objc/authentication-services/enums/as-authorization-public-key-credential-attachment.js";
|
|
20
|
+
const VALID_EXTENSIONS = ["largeBlob", "prf"];
|
|
21
|
+
function createCredentialInternal(rpid, challenge, username, userID, nativeWindowHandle, origin, enabledExtensions, attestation = "none", supportedAlgorithmIdentifiers = [], excludeCredentials, residentKeyRequired = false, userVerification = "preferred", additionalOptions = {}) {
|
|
22
|
+
const { promise, resolve, reject } = PromiseWithResolvers();
|
|
23
|
+
const NS_rpID = NSStringFromString(rpid);
|
|
24
|
+
const NS_challenge = NSDataFromBuffer(challenge);
|
|
25
|
+
const NS_username = NSStringFromString(username);
|
|
26
|
+
const NS_userID = NSDataFromBuffer(userID);
|
|
27
|
+
const platformProvider = createPlatformPublicKeyCredentialProvider(NS_rpID);
|
|
28
|
+
const platformKeyRequest = platformProvider.createCredentialRegistrationRequestWithChallenge$name$userID$(NS_challenge, NS_username, NS_userID);
|
|
29
|
+
if (enabledExtensions.includes("largeBlob")) {
|
|
30
|
+
let supportMode;
|
|
31
|
+
const largeBlobSupport = additionalOptions.largeBlobSupport;
|
|
32
|
+
if (largeBlobSupport === "required") {
|
|
33
|
+
supportMode =
|
|
34
|
+
ASAuthorizationPublicKeyCredentialLargeBlobSupportRequirement.Required;
|
|
35
|
+
}
|
|
36
|
+
else if (largeBlobSupport === "preferred") {
|
|
37
|
+
supportMode =
|
|
38
|
+
ASAuthorizationPublicKeyCredentialLargeBlobSupportRequirement.Preferred;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
console.warn("[electron-webauthn] largeBlobSupport is enabled but largeBlobSupport is not provided, skipping large blob support");
|
|
42
|
+
}
|
|
43
|
+
if (supportMode) {
|
|
44
|
+
const largeBlobInput = createASAuthorizationPublicKeyCredentialLargeBlobRegistrationInput(supportMode);
|
|
45
|
+
platformKeyRequest.setLargeBlob$(largeBlobInput);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
let attestationPreference = ASAuthorizationPublicKeyCredentialAttestationKind.None;
|
|
49
|
+
platformKeyRequest.setAttestationPreference$(NSStringFromString(attestationPreference));
|
|
50
|
+
let userVerificationPreference = ASAuthorizationPublicKeyCredentialUserVerificationPreference.Preferred;
|
|
51
|
+
if (userVerification === "required") {
|
|
52
|
+
userVerificationPreference =
|
|
53
|
+
ASAuthorizationPublicKeyCredentialUserVerificationPreference.Required;
|
|
54
|
+
}
|
|
55
|
+
else if (userVerification === "discouraged") {
|
|
56
|
+
userVerificationPreference =
|
|
57
|
+
ASAuthorizationPublicKeyCredentialUserVerificationPreference.Discouraged;
|
|
58
|
+
}
|
|
59
|
+
platformKeyRequest.setUserVerificationPreference$(NSStringFromString(userVerificationPreference));
|
|
60
|
+
if (additionalOptions.userDisplayName) {
|
|
61
|
+
const userDisplayName = NSStringFromString(additionalOptions.userDisplayName);
|
|
62
|
+
platformKeyRequest.setDisplayName$(userDisplayName);
|
|
63
|
+
}
|
|
64
|
+
if (enabledExtensions.includes("prf")) {
|
|
65
|
+
if (additionalOptions.prf) {
|
|
66
|
+
const inputValues = createPRFInput(additionalOptions.prf);
|
|
67
|
+
const prfInput = createASAuthorizationPublicKeyCredentialPRFRegistrationInput(inputValues);
|
|
68
|
+
platformKeyRequest.setPrf$(prfInput);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
platformKeyRequest.setPrf$(ASAuthorizationPublicKeyCredentialPRFRegistrationInput.checkForSupport());
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const requestsArray = NSArrayFromObjects([platformKeyRequest]);
|
|
75
|
+
const authController = WebauthnCreateController.alloc().initWithAuthorizationRequests$(requestsArray);
|
|
76
|
+
const clientData = generateWebauthnClientData("webauthn.create", origin, challenge, additionalOptions.topFrameOrigin);
|
|
77
|
+
const { clientDataHash, clientDataBuffer } = generateClientDataInfo(clientData);
|
|
78
|
+
setControllerState(authController, clientDataHash, supportedAlgorithmIdentifiers, residentKeyRequired, excludeCredentials);
|
|
79
|
+
const finished = (_success) => {
|
|
80
|
+
removeControllerState(authController);
|
|
81
|
+
};
|
|
82
|
+
const delegate = createAuthorizationControllerDelegate({
|
|
83
|
+
didCompleteWithAuthorization: (_, authorization) => {
|
|
84
|
+
const credential = authorization.credential();
|
|
85
|
+
console.log("Authorization succeeded:", credential);
|
|
86
|
+
const credentialIdBuffer = bufferFromNSDataDirect(credential.credentialID());
|
|
87
|
+
const attestationObjectBuffer = bufferFromNSDataDirect(credential.rawAttestationObject());
|
|
88
|
+
const attestation = parseAttestationObject(attestationObjectBuffer);
|
|
89
|
+
const publicKey = attestation.authenticatorData.credential.publicKey;
|
|
90
|
+
const ec2Key = publicKey.ec2();
|
|
91
|
+
const publicKeySPKI = encodeEC2PublicKeyToSPKI(ec2Key.x, ec2Key.y);
|
|
92
|
+
const authenticatorData = Buffer.from(JSON.stringify(attestation.authenticatorData));
|
|
93
|
+
let authenticatorAttachment = "platform";
|
|
94
|
+
if (credential.attachment() ===
|
|
95
|
+
ASAuthorizationPublicKeyCredentialAttachment.ASAuthorizationPublicKeyCredentialAttachmentCrossPlatform) {
|
|
96
|
+
authenticatorAttachment = "cross-platform";
|
|
97
|
+
}
|
|
98
|
+
let isLargeBlobSupported = null;
|
|
99
|
+
if (enabledExtensions.includes("largeBlob")) {
|
|
100
|
+
const largeBlobOutput = credential.largeBlob();
|
|
101
|
+
if (largeBlobOutput) {
|
|
102
|
+
isLargeBlobSupported = largeBlobOutput.isSupported();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
let prfFirst = null;
|
|
106
|
+
let prfSecond = null;
|
|
107
|
+
let isPRFSupported = null;
|
|
108
|
+
if (enabledExtensions.includes("prf")) {
|
|
109
|
+
const prfOutput = credential.prf();
|
|
110
|
+
if (prfOutput) {
|
|
111
|
+
const prfFirstData = prfOutput.first();
|
|
112
|
+
const prfSecondData = prfOutput.second();
|
|
113
|
+
if (prfFirstData) {
|
|
114
|
+
prfFirst = bufferFromNSDataDirect(prfFirstData);
|
|
115
|
+
}
|
|
116
|
+
if (prfSecondData) {
|
|
117
|
+
prfSecond = bufferFromNSDataDirect(prfSecondData);
|
|
118
|
+
}
|
|
119
|
+
isPRFSupported = prfOutput.isSupported();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const data = {
|
|
123
|
+
credentialId: credentialIdBuffer,
|
|
124
|
+
clientDataJSON: clientDataBuffer,
|
|
125
|
+
attestationObject: attestationObjectBuffer,
|
|
126
|
+
authenticatorData,
|
|
127
|
+
attachment: authenticatorAttachment,
|
|
128
|
+
transports: ["hybrid", "internal"],
|
|
129
|
+
isResidentKey: true,
|
|
130
|
+
publicKeyAlgorithm: publicKey.algorithm(),
|
|
131
|
+
publicKey: publicKeySPKI,
|
|
132
|
+
isLargeBlobSupported,
|
|
133
|
+
isPRFSupported,
|
|
134
|
+
prfFirst,
|
|
135
|
+
prfSecond,
|
|
136
|
+
};
|
|
137
|
+
resolve(data);
|
|
138
|
+
finished(true);
|
|
139
|
+
},
|
|
140
|
+
didCompleteWithError: (_, error) => {
|
|
141
|
+
const parsedError = error;
|
|
142
|
+
const errorMessage = parsedError.localizedDescription().UTF8String();
|
|
143
|
+
console.error("Authorization failed:", errorMessage);
|
|
144
|
+
reject(new Error(errorMessage));
|
|
145
|
+
finished(false);
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
authController.setDelegate$(delegate);
|
|
149
|
+
const presentationContextProvider = createPresentationContextProviderFromNativeWindowHandle(nativeWindowHandle);
|
|
150
|
+
authController.setPresentationContextProvider$(presentationContextProvider);
|
|
151
|
+
authController.performRequests();
|
|
152
|
+
return promise;
|
|
153
|
+
}
|
|
154
|
+
export { createCredentialInternal };
|
package/dist/helpers/index.js
CHANGED
package/dist/helpers/origin.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare function isString(value: unknown): value is string;
|
|
2
2
|
export declare function isNumber(value: unknown): value is number;
|
|
3
|
-
export declare function isObject(value: unknown):
|
|
3
|
+
export declare function isObject(value: unknown): boolean;
|
package/dist/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electron-webauthn",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"repository": "https://github.com/iamEvanYT/electron-webauthn",
|
|
3
|
+
"version": "0.0.17",
|
|
4
|
+
"repository": "https://github.com/iamEvanYT/electron-webauthn.git",
|
|
5
5
|
"description": "Add support for WebAuthn for Electron.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.js",
|