electron-webauthn 0.0.14 → 0.0.15

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.
Files changed (47) hide show
  1. package/README.md +470 -109
  2. package/dist/create/authorization-controller.d.ts +9 -0
  3. package/dist/create/authorization-controller.js +69 -0
  4. package/dist/create/handler.d.ts +30 -1
  5. package/dist/create/handler.js +149 -1
  6. package/dist/get/handler.d.ts +34 -22
  7. package/dist/get/handler.js +126 -166
  8. package/dist/get/internal-handler.d.ts +24 -0
  9. package/dist/get/internal-handler.js +157 -0
  10. package/dist/get/updated-handler.d.ts +1 -0
  11. package/dist/get/updated-handler.js +1 -0
  12. package/dist/helpers/client-data.d.ts +14 -0
  13. package/dist/helpers/client-data.js +36 -0
  14. package/dist/helpers/index.d.ts +1 -1
  15. package/dist/helpers/index.js +13 -10
  16. package/dist/helpers/presentation.d.ts +1 -0
  17. package/dist/helpers/presentation.js +12 -0
  18. package/dist/helpers/public-key.d.ts +1 -0
  19. package/dist/helpers/public-key.js +49 -0
  20. package/dist/helpers/rpid.d.ts +13 -0
  21. package/dist/helpers/rpid.js +59 -0
  22. package/dist/helpers/validation.d.ts +3 -0
  23. package/dist/helpers/validation.js +9 -0
  24. package/dist/objc/authentication-services/as-authorization-c-public-key-credential-descriptor.d.ts +9 -0
  25. package/dist/objc/authentication-services/as-authorization-c-public-key-credential-descriptor.js +6 -0
  26. package/dist/objc/authentication-services/as-authorization-platform-public-key-credential-provider.d.ts +3 -1
  27. package/dist/objc/authentication-services/as-authorization-platform-public-key-credential-registration.d.ts +15 -0
  28. package/dist/objc/authentication-services/as-authorization-platform-public-key-credential-registration.js +2 -0
  29. package/dist/objc/authentication-services/as-authorization-public-key-credential-large-blob-registration-input.d.ts +7 -0
  30. package/dist/objc/authentication-services/as-authorization-public-key-credential-large-blob-registration-input.js +6 -0
  31. package/dist/objc/authentication-services/as-authorization-public-key-credential-large-blob-registration-output.d.ts +5 -0
  32. package/dist/objc/authentication-services/as-authorization-public-key-credential-large-blob-registration-output.js +2 -0
  33. package/dist/objc/authentication-services/as-authorization-public-key-credential-prf-registration-input.d.ts +9 -0
  34. package/dist/objc/authentication-services/as-authorization-public-key-credential-prf-registration-input.js +6 -0
  35. package/dist/objc/authentication-services/as-authorization-public-key-credential-prf-registration-output.d.ts +9 -0
  36. package/dist/objc/authentication-services/as-authorization-public-key-credential-prf-registration-output.js +2 -0
  37. package/dist/objc/authentication-services/as-authorization-public-key-credential-registration.d.ts +14 -0
  38. package/dist/objc/authentication-services/as-authorization-public-key-credential-registration.js +2 -0
  39. package/dist/objc/authentication-services/enums/as-authorization-public-key-credential-attestation-kind.d.ts +6 -0
  40. package/dist/objc/authentication-services/enums/as-authorization-public-key-credential-attestation-kind.js +7 -0
  41. package/dist/objc/authentication-services/enums/as-authorization-public-key-credential-large-blob-support-requirement.d.ts +4 -0
  42. package/dist/objc/authentication-services/enums/as-authorization-public-key-credential-large-blob-support-requirement.js +5 -0
  43. package/dist/objc/authentication-services/enums/as-authorization-public-key-credential-user-verification-preference.d.ts +5 -0
  44. package/dist/objc/authentication-services/enums/as-authorization-public-key-credential-user-verification-preference.js +6 -0
  45. package/dist/test.d.ts +1 -0
  46. package/dist/test.js +5 -0
  47. package/package.json +2 -1
@@ -0,0 +1,9 @@
1
+ import { NobjcObject } from "objc-js";
2
+ import type { ExcludeCredential } from "./handler.js";
3
+ export interface PublicKeyCredentialParams {
4
+ type: "public-key";
5
+ algorithm: number;
6
+ }
7
+ export declare function setControllerState(self: NobjcObject, clientDataHash: Buffer, pubKeyCredParams: PublicKeyCredentialParams[], residentKeyRequired: boolean, excludeCredentialIds: ExcludeCredential[]): void;
8
+ export declare function removeControllerState(self: NobjcObject): void;
9
+ export declare const WebauthnCreateController: NobjcObject;
@@ -0,0 +1,69 @@
1
+ import { NobjcClass, NobjcObject, getPointer } from "objc-js";
2
+ import { NSDataFromBuffer } from "../objc/foundation/nsdata.js";
3
+ import { NSArrayFromObjects } from "../objc/foundation/nsarray.js";
4
+ import { NSStringFromString } from "../objc/foundation/nsstring.js";
5
+ import { createASCPublicKeyCredentialDescriptor } from "../objc/authentication-services/as-authorization-c-public-key-credential-descriptor.js";
6
+ import { NSNumberFromInteger } from "../objc/foundation/nsinteger.js";
7
+ const createControllerState = new Map();
8
+ function getObjectPointerString(self) {
9
+ return getPointer(self).toBase64();
10
+ }
11
+ export function setControllerState(self, clientDataHash, pubKeyCredParams, residentKeyRequired, excludeCredentialIds) {
12
+ const selfPointer = getObjectPointerString(self);
13
+ createControllerState.set(selfPointer, [
14
+ clientDataHash,
15
+ pubKeyCredParams,
16
+ residentKeyRequired,
17
+ excludeCredentialIds,
18
+ ]);
19
+ }
20
+ export function removeControllerState(self) {
21
+ const selfPointer = getObjectPointerString(self);
22
+ createControllerState.delete(selfPointer);
23
+ }
24
+ export const WebauthnCreateController = NobjcClass.define({
25
+ name: "WebauthnCreateController",
26
+ superclass: "ASAuthorizationController",
27
+ methods: {
28
+ _requestContextWithRequests$error$: {
29
+ types: "@@:@^@",
30
+ implementation: (self, requests, outError) => {
31
+ const context = NobjcClass.super(self, "_requestContextWithRequests$error$", requests, outError);
32
+ const selfPointer = getObjectPointerString(self);
33
+ if (context && createControllerState.has(selfPointer)) {
34
+ const registrationOptions = context.platformKeyCredentialCreationOptions();
35
+ const [clientDataHash, pubKeyCredParams, residentKeyRequired, excludeCredentials,] = createControllerState.get(selfPointer);
36
+ registrationOptions.setClientDataHash$(NSDataFromBuffer(clientDataHash));
37
+ registrationOptions.setChallenge$(null);
38
+ const supportedAlgos = [];
39
+ for (const param of pubKeyCredParams) {
40
+ if (param.type === "public-key") {
41
+ supportedAlgos.push(NSNumberFromInteger(param.algorithm));
42
+ }
43
+ }
44
+ if (supportedAlgos.length > 0) {
45
+ registrationOptions.setSupportedAlgorithmIdentifiers$(NSArrayFromObjects(supportedAlgos));
46
+ }
47
+ registrationOptions.setShouldRequireResidentKey$(residentKeyRequired);
48
+ const excludeList = [];
49
+ for (const cred of excludeCredentials) {
50
+ const transports = [];
51
+ if (cred.transports) {
52
+ for (const transport of cred.transports) {
53
+ transports.push(NSStringFromString(transport));
54
+ }
55
+ }
56
+ const credentialID = NSDataFromBuffer(cred.id);
57
+ const transportsArray = NSArrayFromObjects(transports);
58
+ const initializedDescriptor = createASCPublicKeyCredentialDescriptor(credentialID, transportsArray);
59
+ excludeList.push(initializedDescriptor);
60
+ }
61
+ if (excludeList.length > 0) {
62
+ registrationOptions.setExcludedCredentials$(NSArrayFromObjects(excludeList));
63
+ }
64
+ }
65
+ return context;
66
+ },
67
+ },
68
+ },
69
+ });
@@ -1,4 +1,33 @@
1
+ import { type PRFInput } from "../helpers/prf.js";
2
+ import { type PublicKeyCredentialParams } from "./authorization-controller.js";
1
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;
2
17
  }
3
- declare function createCredential(rpid: string): Promise<CreateCredentialResult>;
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;
27
+ }
28
+ export interface ExcludeCredential {
29
+ id: Buffer;
30
+ transports?: string[];
31
+ }
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>;
4
33
  export { createCredential };
@@ -1,6 +1,154 @@
1
+ import { generateClientDataInfo } from "../helpers/client-data.js";
2
+ import { generateWebauthnClientData } from "../helpers/client-data.js";
1
3
  import { PromiseWithResolvers } from "../helpers/index.js";
2
- function createCredential(rpid) {
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 = {}) {
3
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();
4
152
  return promise;
5
153
  }
6
154
  export { createCredential };
@@ -1,24 +1,36 @@
1
- import { type PRFInput } from "../helpers/prf.js";
2
- type AuthenticatorAttachment = "platform" | "cross-platform";
3
- export type UserVerificationPreference = "preferred" | "required" | "discouraged";
4
- declare const VALID_EXTENSIONS: readonly ["largeBlobRead", "largeBlobWrite", "prf"];
5
- export type CredentialAssertionExtensions = (typeof VALID_EXTENSIONS)[number];
6
- export interface GetCredentialResult {
7
- id: Buffer;
8
- authenticatorAttachment: AuthenticatorAttachment;
9
- clientDataJSON: Buffer;
10
- authenticatorData: Buffer;
11
- signature: Buffer;
12
- userHandle: Buffer;
13
- prf: [Buffer | null, Buffer | null];
14
- largeBlob: Buffer | null;
15
- largeBlobWritten: boolean | null;
1
+ export interface GetCredentialSuccessData {
2
+ credentialId: string;
3
+ clientDataJSON: string;
4
+ authenticatorData: string;
5
+ signature: string;
6
+ userHandle: string;
7
+ extensions?: {
8
+ prf?: {
9
+ results?: {
10
+ first: string;
11
+ second?: string;
12
+ };
13
+ };
14
+ largeBlob?: {
15
+ blob?: string;
16
+ written?: boolean;
17
+ };
18
+ };
16
19
  }
17
- export interface GetCredentialAdditionalOptions {
18
- largeBlobDataToWrite?: Buffer;
19
- prf?: PRFInput;
20
- prfByCredential?: Record<string, PRFInput>;
21
- topFrameOrigin?: string;
20
+ interface WebauthnGetRequestOptions {
21
+ currentOrigin: string;
22
+ topFrameOrigin: string | undefined;
23
+ isPublicSuffix?: (domain: string) => boolean;
24
+ nativeWindowHandle: Buffer;
22
25
  }
23
- declare function getCredential(rpid: string, challenge: Buffer, nativeWindowHandle: Buffer, origin: string, enabledExtensions: CredentialAssertionExtensions[], allowedCredentialIds: Buffer[], userVerificationPreference?: UserVerificationPreference, additionalOptions?: GetCredentialAdditionalOptions): Promise<GetCredentialResult>;
24
- export { getCredential };
26
+ interface GetCredentialSuccessResult {
27
+ success: true;
28
+ data: GetCredentialSuccessData;
29
+ }
30
+ interface GetCredentialErrorResult {
31
+ success: false;
32
+ error: "TypeError" | "AbortError" | "NotAllowedError" | "SecurityError";
33
+ }
34
+ export type GetCredentialResult = GetCredentialSuccessResult | GetCredentialErrorResult;
35
+ export declare function getCredential(publicKeyOptions: PublicKeyCredentialRequestOptions | undefined, additionalOptions: WebauthnGetRequestOptions): Promise<GetCredentialResult>;
36
+ export {};
@@ -1,179 +1,139 @@
1
- import { fromPointer } from "objc-js";
2
- import { createAuthorizationControllerDelegate } from "../objc/authentication-services/as-authorization-controller-delegate.js";
3
- import { ASAuthorizationController } from "../objc/authentication-services/as-authorization-controller.js";
4
- import { createPresentationContextProvider } from "../objc/authentication-services/as-authorization-controller-presentation-context-providing.js";
5
- import { createPlatformPublicKeyCredentialProvider } from "../objc/authentication-services/as-authorization-platform-public-key-credential-provider.js";
6
- import { createPlatformPublicKeyCredentialDescriptor } from "../objc/authentication-services/as-authorization-platform-public-key-credential-descriptor.js";
7
- import { NSArrayFromObjects } from "../objc/foundation/nsarray.js";
8
- import { bufferFromNSDataDirect, NSDataFromBuffer, } from "../objc/foundation/nsdata.js";
9
- import { NSStringFromString } from "../objc/foundation/nsstring.js";
10
- import { base64UrlToBuffer, bufferToBase64Url, clientDataJsonBufferToHash, PromiseWithResolvers, } from "../helpers/index.js";
11
- import { isSameOrigin, serializeOrigin } from "../helpers/origin.js";
12
- import { ASAuthorizationPublicKeyCredentialAttachment } from "../objc/authentication-services/enums/as-authorization-public-key-credential-attachment.js";
13
- import { removeClientDataHash, setClientDataHash, WebauthnGetController, } from "../get/authorization-controller.js";
14
- import { createSecurityKeyPublicKeyCredentialProvider } from "../objc/authentication-services/as-authorization-security-key-public-key-credential-provider.js";
15
- import { createASAuthorizationPublicKeyCredentialLargeBlobAssertionInput } from "../objc/authentication-services/as-authorization-public-key-credential-large-blob-assertion-input.js";
16
- import { ASAuthorizationPublicKeyCredentialLargeBlobAssertionOperation } from "../objc/authentication-services/enums/as-authorization-public-key-credential-large-blob-assertion-operation.js";
17
- import { createASAuthorizationPublicKeyCredentialPRFAssertionInput } from "../objc/authentication-services/as-authorization-public-key-credential-prf-assertion-input.js";
18
- import {} from "../objc/authentication-services/as-authorization-public-key-credential-prf-assertion-input-valuesas-authorization-public-key-credential-prf-assertion-input-values.js";
19
- import { createPRFInput } from "../helpers/prf.js";
20
- import { NSDictionaryFromKeysAndValues, } from "../objc/foundation/nsdictionary.js";
21
- const VALID_EXTENSIONS = ["largeBlobRead", "largeBlobWrite", "prf"];
22
- function setupPublicKeyCredentialRequest(type, keyRequest, userVerificationPreference, enabledExtensions, allowedCredentialIds, additionalOptions) {
23
- if (userVerificationPreference === "preferred") {
24
- keyRequest.setUserVerificationPreference$(NSStringFromString("preferred"));
1
+ import { bufferSourceToBuffer, bufferToBase64Url } from "../helpers/index.js";
2
+ import { isRpIdAllowedForOrigin } from "../helpers/rpid.js";
3
+ import { isNumber, isString } from "../helpers/validation.js";
4
+ import { getCredentialInternal, } from "./internal-handler.js";
5
+ function getExtensionsConfiguration(extensionsData) {
6
+ if (!(extensionsData && typeof extensionsData === "object")) {
7
+ return {
8
+ extensions: [],
9
+ };
25
10
  }
26
- else if (userVerificationPreference === "required") {
27
- keyRequest.setUserVerificationPreference$(NSStringFromString("required"));
28
- }
29
- else if (userVerificationPreference === "discouraged") {
30
- keyRequest.setUserVerificationPreference$(NSStringFromString("discouraged"));
31
- }
32
- if (type === "platform") {
33
- const largeBlobRead = enabledExtensions.includes("largeBlobRead");
34
- const largeBlobWrite = enabledExtensions.includes("largeBlobWrite");
35
- if (largeBlobRead) {
36
- const operation = ASAuthorizationPublicKeyCredentialLargeBlobAssertionOperation.Read;
37
- const largeBlobInput = createASAuthorizationPublicKeyCredentialLargeBlobAssertionInput(operation);
38
- keyRequest.setLargeBlob$(largeBlobInput);
11
+ const extensions = [];
12
+ let largeBlobWriteBuffer;
13
+ if (extensionsData.largeBlob) {
14
+ const largeBlobConfig = extensionsData.largeBlob;
15
+ if (largeBlobConfig.read) {
16
+ extensions.push("largeBlobRead");
39
17
  }
40
- else if (largeBlobWrite) {
41
- if (additionalOptions.largeBlobDataToWrite) {
42
- const operation = ASAuthorizationPublicKeyCredentialLargeBlobAssertionOperation.Write;
43
- const largeBlobInput = createASAuthorizationPublicKeyCredentialLargeBlobAssertionInput(operation);
44
- largeBlobInput.setDataToWrite$(NSDataFromBuffer(additionalOptions.largeBlobDataToWrite));
45
- keyRequest.setLargeBlob$(largeBlobInput);
46
- }
47
- else {
48
- console.warn("[electron-webauthn] largeBlobWrite is enabled but largeBlobDataToWrite is not provided, skipping large blob write");
49
- }
18
+ if (largeBlobConfig.write) {
19
+ extensions.push("largeBlobWrite");
20
+ largeBlobWriteBuffer = bufferSourceToBuffer(largeBlobConfig.write);
50
21
  }
51
22
  }
52
- if (type === "platform" && enabledExtensions.includes("prf")) {
53
- if (additionalOptions.prf || additionalOptions.prfByCredential) {
54
- let inputValues = null;
55
- if (additionalOptions.prf) {
56
- inputValues = createPRFInput(additionalOptions.prf);
57
- }
58
- let perCredentialInputValues = null;
59
- if (additionalOptions.prfByCredential &&
60
- allowedCredentialIds.length > 0) {
61
- const keys = [];
62
- const values = [];
63
- for (const [credentialId, prfInput] of Object.entries(additionalOptions.prfByCredential)) {
64
- const credentialIdBuffer = base64UrlToBuffer(credentialId);
65
- const credentialIdData = NSDataFromBuffer(credentialIdBuffer);
66
- keys.push(credentialIdData);
67
- values.push(createPRFInput(prfInput));
68
- }
69
- perCredentialInputValues = NSDictionaryFromKeysAndValues(keys, values);
70
- }
71
- const prfInput = createASAuthorizationPublicKeyCredentialPRFAssertionInput(inputValues, perCredentialInputValues);
72
- keyRequest.setPrf$(prfInput);
23
+ let prf;
24
+ let prfByCredential;
25
+ const prfExtension = extensionsData.prf;
26
+ if (prfExtension && (prfExtension.eval || prfExtension.evalByCredential)) {
27
+ extensions.push("prf");
28
+ if (prfExtension.eval) {
29
+ prf = {
30
+ first: bufferSourceToBuffer(prfExtension.eval.first),
31
+ second: prfExtension.eval.second
32
+ ? bufferSourceToBuffer(prfExtension.eval.second)
33
+ : undefined,
34
+ };
73
35
  }
74
- else {
75
- console.warn("[electron-webauthn] prf is enabled but prf or prfByCredential is not provided, skipping PRF");
36
+ if (prfExtension.evalByCredential) {
37
+ prfByCredential = {};
38
+ for (const [credId, value] of Object.entries(prfExtension.evalByCredential)) {
39
+ prfByCredential[credId] = {
40
+ first: bufferSourceToBuffer(value.first),
41
+ second: value.second ? bufferSourceToBuffer(value.second) : undefined,
42
+ };
43
+ }
76
44
  }
77
45
  }
78
- }
79
- function getCredential(rpid, challenge, nativeWindowHandle, origin, enabledExtensions = [], allowedCredentialIds, userVerificationPreference, additionalOptions = {}) {
80
- const { promise, resolve, reject } = PromiseWithResolvers();
81
- const NS_rpID = NSStringFromString(rpid);
82
- const NS_challenge = NSDataFromBuffer(challenge);
83
- const platformProvider = createPlatformPublicKeyCredentialProvider(NS_rpID);
84
- const platformKeyRequest = platformProvider.createCredentialAssertionRequestWithChallenge$(NS_challenge);
85
- setupPublicKeyCredentialRequest("platform", platformKeyRequest, userVerificationPreference, enabledExtensions, allowedCredentialIds, additionalOptions);
86
- const securityKeyProvider = createSecurityKeyPublicKeyCredentialProvider(NS_rpID);
87
- const securityKeyRequest = securityKeyProvider.createCredentialAssertionRequestWithChallenge$(NS_challenge);
88
- setupPublicKeyCredentialRequest("security-key", securityKeyRequest, userVerificationPreference, enabledExtensions, allowedCredentialIds, additionalOptions);
89
- const requestsArray = NSArrayFromObjects([
90
- platformKeyRequest,
91
- securityKeyRequest,
92
- ]);
93
- const authController = WebauthnGetController.alloc().initWithAuthorizationRequests$(requestsArray);
94
- const serializedOrigin = serializeOrigin(origin);
95
- const clientData = {
96
- type: "webauthn.get",
97
- challenge: bufferToBase64Url(challenge),
98
- origin: serializedOrigin,
99
- crossOrigin: false,
46
+ return {
47
+ extensions,
48
+ largeBlobWriteBuffer,
49
+ prf,
50
+ prfByCredential,
100
51
  };
101
- if (additionalOptions.topFrameOrigin) {
102
- const sameOrigin = isSameOrigin(origin, additionalOptions.topFrameOrigin);
103
- if (!sameOrigin) {
104
- const serializedTopFrameOrigin = serializeOrigin(additionalOptions.topFrameOrigin);
105
- clientData.topOrigin = serializedTopFrameOrigin;
106
- clientData.crossOrigin = true;
107
- }
52
+ }
53
+ export async function getCredential(publicKeyOptions, additionalOptions) {
54
+ if (!publicKeyOptions) {
55
+ return null;
108
56
  }
109
- const clientDataJSON = JSON.stringify(clientData);
110
- const clientDataBuffer = Buffer.from(clientDataJSON, "utf-8");
111
- const clientDataHash = clientDataJsonBufferToHash(clientDataBuffer);
112
- setClientDataHash(authController, clientDataHash);
113
- const finished = (_success) => {
114
- removeClientDataHash(authController);
115
- };
116
- if (allowedCredentialIds.length > 0) {
117
- const allowedCredentials = NSArrayFromObjects(allowedCredentialIds.map((id) => createPlatformPublicKeyCredentialDescriptor(NSDataFromBuffer(id))));
118
- platformKeyRequest.setAllowedCredentials$(allowedCredentials);
57
+ const rpId = publicKeyOptions.rpId;
58
+ if (!isString(rpId)) {
59
+ return { success: false, error: "TypeError" };
119
60
  }
120
- const delegate = createAuthorizationControllerDelegate({
121
- didCompleteWithAuthorization: (_, authorization) => {
122
- const credential = authorization.credential();
123
- const id_data = credential.credentialID();
124
- const id = bufferFromNSDataDirect(id_data);
125
- let authenticatorAttachment = "platform";
126
- if (credential.attachment() ===
127
- ASAuthorizationPublicKeyCredentialAttachment.ASAuthorizationPublicKeyCredentialAttachmentCrossPlatform) {
128
- authenticatorAttachment = "cross-platform";
129
- }
130
- const prf = credential.prf();
131
- const prfFirst = prf?.first ? prf.first() : null;
132
- const prfSecond = prf?.second ? prf.second() : null;
133
- let largeBlobBuffer = null;
134
- let largeBlobWritten = null;
135
- if (credential.largeBlob()) {
136
- const largeBlobData = credential.largeBlob().readData();
137
- if (largeBlobData) {
138
- largeBlobBuffer = bufferFromNSDataDirect(largeBlobData);
139
- }
140
- else {
141
- largeBlobWritten = credential.largeBlob().didWrite();
142
- }
143
- }
144
- resolve({
145
- id,
146
- authenticatorAttachment,
147
- clientDataJSON: clientDataBuffer,
148
- authenticatorData: bufferFromNSDataDirect(credential.rawAuthenticatorData()),
149
- signature: bufferFromNSDataDirect(credential.signature()),
150
- userHandle: bufferFromNSDataDirect(credential.userID()),
151
- prf: [
152
- prfFirst ? bufferFromNSDataDirect(prfFirst) : null,
153
- prfSecond ? bufferFromNSDataDirect(prfSecond) : null,
154
- ],
155
- largeBlob: largeBlobBuffer,
156
- largeBlobWritten,
157
- });
158
- finished(true);
159
- },
160
- didCompleteWithError: (_, error) => {
161
- const parsedError = error;
162
- const errorMessage = parsedError.localizedDescription().UTF8String();
163
- reject(new Error(errorMessage));
164
- finished(false);
165
- },
61
+ let timeout = publicKeyOptions.timeout;
62
+ if (!isNumber(timeout) || timeout <= 0) {
63
+ timeout = 10 * 60 * 1000;
64
+ }
65
+ else if (timeout > 60 * 60 * 1000) {
66
+ timeout = 60 * 60 * 1000;
67
+ }
68
+ const challenge = bufferSourceToBuffer(publicKeyOptions.challenge);
69
+ if (!challenge) {
70
+ return { success: false, error: "TypeError" };
71
+ }
72
+ const userVerification = publicKeyOptions.userVerification;
73
+ if (userVerification && !isString(userVerification)) {
74
+ return { success: false, error: "TypeError" };
75
+ }
76
+ const allowedCredentialsArray = [];
77
+ const allowedCredentials = publicKeyOptions.allowCredentials;
78
+ if (allowedCredentials && Array.isArray(allowedCredentials)) {
79
+ for (const allowedCredential of allowedCredentials) {
80
+ if (!(allowedCredential && typeof allowedCredential === "object"))
81
+ continue;
82
+ if (allowedCredential.type !== "public-key")
83
+ continue;
84
+ const id = bufferSourceToBuffer(allowedCredential.id);
85
+ if (!id)
86
+ continue;
87
+ allowedCredentialsArray.push(id);
88
+ }
89
+ }
90
+ const { extensions: enabledExtensions, largeBlobWriteBuffer, prf, prfByCredential, } = getExtensionsConfiguration(publicKeyOptions.extensions);
91
+ const { currentOrigin, topFrameOrigin, isPublicSuffix, nativeWindowHandle } = additionalOptions;
92
+ const isRpIdAllowed = isRpIdAllowedForOrigin(currentOrigin, rpId, {
93
+ isPublicSuffix,
166
94
  });
167
- authController.setDelegate$(delegate);
168
- const presentationContextProvider = createPresentationContextProvider({
169
- presentationAnchorForAuthorizationController: () => {
170
- const nsView = fromPointer(nativeWindowHandle);
171
- const nsWindow = nsView.window();
172
- return nsWindow;
173
- },
95
+ if (!isRpIdAllowed.ok) {
96
+ return { success: false, error: "NotAllowedError" };
97
+ }
98
+ const result = await getCredentialInternal(rpId, challenge, nativeWindowHandle, currentOrigin, enabledExtensions, allowedCredentialsArray, userVerification, {
99
+ topFrameOrigin,
100
+ largeBlobDataToWrite: largeBlobWriteBuffer,
101
+ prf,
102
+ prfByCredential,
103
+ }).catch((error) => {
104
+ console.error("Error getting credential", error);
105
+ if (error.message.startsWith("The operation couldn’t be completed.")) {
106
+ return "NotAllowedError";
107
+ }
108
+ return "NotAllowedError";
174
109
  });
175
- authController.setPresentationContextProvider$(presentationContextProvider);
176
- authController.performRequests();
177
- return promise;
110
+ if (typeof result === "string") {
111
+ if (result === "NotAllowedError") {
112
+ return { success: false, error: "NotAllowedError" };
113
+ }
114
+ return { success: false, error: "NotAllowedError" };
115
+ }
116
+ const data = {
117
+ credentialId: bufferToBase64Url(result.id),
118
+ clientDataJSON: bufferToBase64Url(result.clientDataJSON),
119
+ authenticatorData: bufferToBase64Url(result.authenticatorData),
120
+ signature: bufferToBase64Url(result.signature),
121
+ userHandle: bufferToBase64Url(result.userHandle),
122
+ extensions: {},
123
+ };
124
+ if (result.prf && (result.prf[0] || result.prf[1])) {
125
+ data.extensions.prf = {
126
+ results: {
127
+ first: bufferToBase64Url(result.prf[0]),
128
+ second: result.prf[1] ? bufferToBase64Url(result.prf[1]) : undefined,
129
+ },
130
+ };
131
+ }
132
+ if (result.largeBlob || result.largeBlobWritten) {
133
+ data.extensions.largeBlob = {
134
+ blob: result.largeBlob ? bufferToBase64Url(result.largeBlob) : undefined,
135
+ written: result.largeBlobWritten !== null ? result.largeBlobWritten : undefined,
136
+ };
137
+ }
138
+ return { success: true, data };
178
139
  }
179
- export { getCredential };