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.
@@ -1,33 +1,41 @@
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;
1
+ export interface CreateCredentialSuccessData {
2
+ credentialId: string;
3
+ clientDataJSON: string;
4
+ attestationObject: string;
5
+ authData: string;
6
+ publicKey: string;
11
7
  publicKeyAlgorithm: number;
12
- publicKey: Buffer;
13
- isLargeBlobSupported: boolean | null;
14
- isPRFSupported: boolean | null;
15
- prfFirst: Buffer | null;
16
- prfSecond: Buffer | null;
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
- 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
- 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
- export interface ExcludeCredential {
29
- id: Buffer;
30
- transports?: string[];
35
+ interface CreateCredentialErrorResult {
36
+ success: false;
37
+ error: "TypeError" | "AbortError" | "NotAllowedError" | "SecurityError" | "InvalidStateError";
31
38
  }
32
- declare function createCredential(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>;
33
- export { createCredential };
39
+ export type CreateCredentialResult = CreateCredentialSuccessResult | CreateCredentialErrorResult;
40
+ export declare function createCredential(publicKeyOptions: PublicKeyCredentialCreationOptions | undefined, additionalOptions: WebauthnCreateRequestOptions): Promise<CreateCredentialResult>;
41
+ export {};
@@ -1,154 +1,206 @@
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 createCredential(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;
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
- else if (largeBlobSupport === "preferred") {
37
- supportMode =
38
- ASAuthorizationPublicKeyCredentialLargeBlobSupportRequirement.Preferred;
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
- console.warn("[electron-webauthn] largeBlobSupport is enabled but largeBlobSupport is not provided, skipping large blob support");
108
+ return { success: false, error: "TypeError" };
42
109
  }
43
- if (supportMode) {
44
- const largeBlobInput = createASAuthorizationPublicKeyCredentialLargeBlobRegistrationInput(supportMode);
45
- platformKeyRequest.setLargeBlob$(largeBlobInput);
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
- 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);
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
- platformKeyRequest.setPrf$(ASAuthorizationPublicKeyCredentialPRFRegistrationInput.checkForSupport());
146
+ userVerificationPreference = "preferred";
72
147
  }
73
148
  }
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
- },
149
+ const { currentOrigin, topFrameOrigin, isPublicSuffix, nativeWindowHandle } = additionalOptions;
150
+ const isRpIdAllowed = isRpIdAllowedForOrigin(currentOrigin, rpId, {
151
+ isPublicSuffix,
147
152
  });
148
- authController.setDelegate$(delegate);
149
- const presentationContextProvider = createPresentationContextProviderFromNativeWindowHandle(nativeWindowHandle);
150
- authController.setPresentationContextProvider$(presentationContextProvider);
151
- authController.performRequests();
152
- return promise;
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 };
@@ -32,6 +32,8 @@ export function base64UrlToBuffer(b64url) {
32
32
  return Buffer.from(b64, "base64");
33
33
  }
34
34
  export function bufferSourceToBuffer(src) {
35
+ if (!src)
36
+ return null;
35
37
  if (Buffer.isBuffer(src))
36
38
  return src;
37
39
  if (src instanceof ArrayBuffer ||
@@ -101,6 +101,6 @@ function originString(x) {
101
101
  }
102
102
  const o = computeOrigin(url);
103
103
  if (o.type === "opaque")
104
- return "null";
104
+ return null;
105
105
  return `${o.scheme}://${o.host}${o.port == null ? "" : `:${o.port}`}`;
106
106
  }
@@ -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): value is Record<string, unknown>;
3
+ export declare function isObject(value: unknown): boolean;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  export * from "./get/handler.js";
2
2
  export * from "./create/handler.js";
3
- export type { PRFInput } from "./helpers/prf.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "electron-webauthn",
3
- "version": "0.0.15",
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",