electron-webauthn 0.0.12 → 0.0.14

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 (130) hide show
  1. package/README.md +66 -79
  2. package/dist/create/handler.d.ts +4 -0
  3. package/dist/create/handler.js +6 -0
  4. package/dist/{get-authorization-controller.d.ts → get/authorization-controller.d.ts} +0 -1
  5. package/dist/{get-authorization-controller.js → get/authorization-controller.js} +1 -4
  6. package/dist/get/handler.d.ts +24 -0
  7. package/dist/get/handler.js +179 -0
  8. package/dist/helpers/index.d.ts +8 -0
  9. package/dist/helpers/index.js +43 -0
  10. package/dist/helpers/origin.d.ts +19 -0
  11. package/dist/helpers/origin.js +106 -0
  12. package/dist/helpers/prf.d.ts +5 -0
  13. package/dist/helpers/prf.js +5 -0
  14. package/dist/helpers.d.ts +1 -38
  15. package/dist/helpers.js +14 -46
  16. package/dist/index.d.ts +3 -15
  17. package/dist/index.js +2 -118
  18. package/dist/objc/authentication-services/as-authorization-controller-delegate.d.ts +0 -47
  19. package/dist/objc/authentication-services/as-authorization-controller-delegate.js +0 -29
  20. package/dist/objc/authentication-services/as-authorization-controller-presentation-context-providing.d.ts +0 -37
  21. package/dist/objc/authentication-services/as-authorization-controller-presentation-context-providing.js +0 -25
  22. package/dist/objc/authentication-services/as-authorization-controller.d.ts +0 -13
  23. package/dist/objc/authentication-services/as-authorization-controller.js +0 -7
  24. package/dist/objc/authentication-services/as-authorization-platform-public-key-credential-assertion.d.ts +0 -1
  25. package/dist/objc/authentication-services/as-authorization-platform-public-key-credential-assertion.js +0 -1
  26. package/dist/objc/authentication-services/as-authorization-platform-public-key-credential-descriptor.d.ts +0 -31
  27. package/dist/objc/authentication-services/as-authorization-platform-public-key-credential-descriptor.js +0 -7
  28. package/dist/objc/authentication-services/as-authorization-platform-public-key-credential-provider.d.ts +0 -13
  29. package/dist/objc/authentication-services/as-authorization-platform-public-key-credential-provider.js +0 -7
  30. package/dist/objc/authentication-services/as-authorization-platform-security-key-credential-assertion.d.ts +17 -0
  31. package/dist/objc/authentication-services/as-authorization-platform-security-key-credential-assertion.js +2 -0
  32. package/dist/objc/authentication-services/as-authorization-public-key-credential-large-blob-assertion-input.d.ts +9 -0
  33. package/dist/objc/authentication-services/as-authorization-public-key-credential-large-blob-assertion-input.js +6 -0
  34. package/dist/objc/authentication-services/as-authorization-public-key-credential-large-blob-assertion.d.ts +0 -1
  35. package/dist/objc/authentication-services/as-authorization-public-key-credential-large-blob-assertion.js +0 -1
  36. package/dist/objc/authentication-services/as-authorization-public-key-credential-prf-assertion-input-valuesas-authorization-public-key-credential-prf-assertion-input-values.d.ts +7 -0
  37. package/dist/objc/authentication-services/as-authorization-public-key-credential-prf-assertion-input-valuesas-authorization-public-key-credential-prf-assertion-input-values.js +6 -0
  38. package/dist/objc/authentication-services/as-authorization-public-key-credential-prf-assertion-input.d.ts +8 -0
  39. package/dist/objc/authentication-services/as-authorization-public-key-credential-prf-assertion-input.js +6 -0
  40. package/dist/objc/authentication-services/as-authorization-public-key-credential-prf-assertion.d.ts +0 -1
  41. package/dist/objc/authentication-services/as-authorization-public-key-credential-prf-assertion.js +0 -1
  42. package/dist/objc/authentication-services/as-authorization-security-key-public-key-credential-provider.d.ts +9 -0
  43. package/dist/objc/authentication-services/as-authorization-security-key-public-key-credential-provider.js +6 -0
  44. package/dist/objc/authentication-services/as-authorization.d.ts +0 -35
  45. package/dist/objc/authentication-services/as-authorization.js +0 -1
  46. package/dist/objc/authentication-services/enums/as-authorization-public-key-credential-attachment.d.ts +0 -4
  47. package/dist/objc/authentication-services/enums/as-authorization-public-key-credential-attachment.js +0 -4
  48. package/dist/objc/authentication-services/enums/as-authorization-public-key-credential-large-blob-assertion-operation.d.ts +4 -0
  49. package/dist/objc/authentication-services/enums/as-authorization-public-key-credential-large-blob-assertion-operation.js +5 -0
  50. package/dist/objc/authentication-services/index.d.ts +0 -1
  51. package/dist/objc/authentication-services/index.js +0 -2
  52. package/dist/objc/foundation/index.d.ts +0 -1
  53. package/dist/objc/foundation/index.js +0 -2
  54. package/dist/objc/foundation/nsarray.d.ts +1 -8
  55. package/dist/objc/foundation/nsarray.js +6 -10
  56. package/dist/objc/foundation/nsdata.d.ts +1 -75
  57. package/dist/objc/foundation/nsdata.js +0 -64
  58. package/dist/objc/foundation/nsdictionary.d.ts +37 -0
  59. package/dist/objc/foundation/nsdictionary.js +64 -0
  60. package/dist/objc/foundation/nserror.d.ts +0 -11
  61. package/dist/objc/foundation/nserror.js +0 -18
  62. package/dist/objc/foundation/nsinteger.d.ts +50 -0
  63. package/dist/objc/foundation/nsinteger.js +124 -0
  64. package/dist/objc/foundation/nsstring.d.ts +0 -6
  65. package/dist/objc/foundation/nsstring.js +0 -7
  66. package/dist/objc/foundation/nsview.d.ts +0 -1
  67. package/dist/objc/foundation/nsview.js +0 -1
  68. package/dist/objc/foundation/nswindow.d.ts +0 -1
  69. package/dist/objc/foundation/nswindow.js +0 -1
  70. package/dist/objc/helpers.d.ts +0 -6
  71. package/dist/objc/helpers.js +0 -6
  72. package/dist/prf.d.ts +5 -0
  73. package/dist/prf.js +5 -0
  74. package/package.json +3 -3
  75. package/dist/get-authorization-controller.d.ts.map +0 -1
  76. package/dist/get-authorization-controller.js.map +0 -1
  77. package/dist/helpers.d.ts.map +0 -1
  78. package/dist/helpers.js.map +0 -1
  79. package/dist/index.d.ts.map +0 -1
  80. package/dist/index.js.map +0 -1
  81. package/dist/objc/authentication-services/as-authorization-controller-delegate.d.ts.map +0 -1
  82. package/dist/objc/authentication-services/as-authorization-controller-delegate.js.map +0 -1
  83. package/dist/objc/authentication-services/as-authorization-controller-presentation-context-providing.d.ts.map +0 -1
  84. package/dist/objc/authentication-services/as-authorization-controller-presentation-context-providing.js.map +0 -1
  85. package/dist/objc/authentication-services/as-authorization-controller.d.ts.map +0 -1
  86. package/dist/objc/authentication-services/as-authorization-controller.js.map +0 -1
  87. package/dist/objc/authentication-services/as-authorization-platform-public-key-credential-assertion.d.ts.map +0 -1
  88. package/dist/objc/authentication-services/as-authorization-platform-public-key-credential-assertion.js.map +0 -1
  89. package/dist/objc/authentication-services/as-authorization-platform-public-key-credential-descriptor.d.ts.map +0 -1
  90. package/dist/objc/authentication-services/as-authorization-platform-public-key-credential-descriptor.js.map +0 -1
  91. package/dist/objc/authentication-services/as-authorization-platform-public-key-credential-provider.d.ts.map +0 -1
  92. package/dist/objc/authentication-services/as-authorization-platform-public-key-credential-provider.js.map +0 -1
  93. package/dist/objc/authentication-services/as-authorization-public-key-credential-large-blob-assertion.d.ts.map +0 -1
  94. package/dist/objc/authentication-services/as-authorization-public-key-credential-large-blob-assertion.js.map +0 -1
  95. package/dist/objc/authentication-services/as-authorization-public-key-credential-prf-assertion.d.ts.map +0 -1
  96. package/dist/objc/authentication-services/as-authorization-public-key-credential-prf-assertion.js.map +0 -1
  97. package/dist/objc/authentication-services/as-authorization.d.ts.map +0 -1
  98. package/dist/objc/authentication-services/as-authorization.js.map +0 -1
  99. package/dist/objc/authentication-services/enums/as-authorization-public-key-credential-attachment.d.ts.map +0 -1
  100. package/dist/objc/authentication-services/enums/as-authorization-public-key-credential-attachment.js.map +0 -1
  101. package/dist/objc/authentication-services/index.d.ts.map +0 -1
  102. package/dist/objc/authentication-services/index.js.map +0 -1
  103. package/dist/objc/foundation/index.d.ts.map +0 -1
  104. package/dist/objc/foundation/index.js.map +0 -1
  105. package/dist/objc/foundation/nsarray.d.ts.map +0 -1
  106. package/dist/objc/foundation/nsarray.js.map +0 -1
  107. package/dist/objc/foundation/nsdata.d.ts.map +0 -1
  108. package/dist/objc/foundation/nsdata.js.map +0 -1
  109. package/dist/objc/foundation/nserror.d.ts.map +0 -1
  110. package/dist/objc/foundation/nserror.js.map +0 -1
  111. package/dist/objc/foundation/nsstring.d.ts.map +0 -1
  112. package/dist/objc/foundation/nsstring.js.map +0 -1
  113. package/dist/objc/foundation/nsview.d.ts.map +0 -1
  114. package/dist/objc/foundation/nsview.js.map +0 -1
  115. package/dist/objc/foundation/nswindow.d.ts.map +0 -1
  116. package/dist/objc/foundation/nswindow.js.map +0 -1
  117. package/dist/objc/helpers.d.ts.map +0 -1
  118. package/dist/objc/helpers.js.map +0 -1
  119. package/dist/test/example.d.ts +0 -2
  120. package/dist/test/example.d.ts.map +0 -1
  121. package/dist/test/example.js +0 -24
  122. package/dist/test/example.js.map +0 -1
  123. package/dist/test/index.d.ts +0 -2
  124. package/dist/test/index.d.ts.map +0 -1
  125. package/dist/test/index.js +0 -9
  126. package/dist/test/index.js.map +0 -1
  127. package/dist/test/window.d.ts +0 -5
  128. package/dist/test/window.d.ts.map +0 -1
  129. package/dist/test/window.js +0 -58
  130. package/dist/test/window.js.map +0 -1
package/README.md CHANGED
@@ -15,6 +15,9 @@ This package provides JavaScript bindings to Apple's AuthenticationServices fram
15
15
  - 🔑 Support for cross-platform authenticators (external security keys)
16
16
  - 📦 TypeScript first with complete type definitions
17
17
  - 🎨 Seamless integration with Electron's native window system
18
+ - 🔐 PRF (Pseudo-Random Function) support for credential assertions
19
+ - 💾 Large Blob support for storing credential-specific data
20
+ - ⚙️ User verification preference configuration (preferred, required, discouraged)
18
21
 
19
22
  ## Installation
20
23
 
@@ -49,8 +52,9 @@ async function authenticate() {
49
52
  const result = await getCredential(
50
53
  "example.com", // Relying Party ID
51
54
  Buffer.from("your-challenge"), // Challenge from server
52
- nativeWindowHandle, // Native window for UI presentation
53
- [] // Optional: allowed credential IDs
55
+ "https://example.com", // Origin
56
+ [], // Optional: allowed credential IDs
57
+ "preferred" // Optional: user verification preference
54
58
  );
55
59
 
56
60
  console.log(result);
@@ -61,12 +65,14 @@ async function authenticate() {
61
65
  // - authenticatorData: Authenticator data (Buffer)
62
66
  // - signature: Assertion signature (Buffer)
63
67
  // - userHandle: User handle (Buffer)
68
+ // - prf: [Buffer | null, Buffer | null] - PRF output (if supported)
69
+ // - largeBlob: Buffer | null - Large blob data (if supported)
64
70
  }
65
71
  ```
66
72
 
67
73
  ## API Reference
68
74
 
69
- ### `getCredential(rpid, challenge, nativeWindowHandle, allowedCredentialIds)`
75
+ ### `getCredential(rpid, challenge, origin, allowedCredentialIds, userVerificationPreference)`
70
76
 
71
77
  Performs a WebAuthn assertion (authentication) using available platform and cross-platform authenticators.
72
78
 
@@ -74,8 +80,12 @@ Performs a WebAuthn assertion (authentication) using available platform and cros
74
80
 
75
81
  - **`rpid: string`** - The Relying Party ID (typically your domain)
76
82
  - **`challenge: Buffer`** - The challenge from your server (32+ bytes recommended)
77
- - **`nativeWindowHandle: Buffer`** - The native window handle from Electron
83
+ - **`origin: string`** - The origin where the credential assertion will be used (e.g., "https://example.com")
78
84
  - **`allowedCredentialIds: Buffer[]`** - Optional array of credential IDs the user can use (empty = all registered credentials)
85
+ - **`userVerificationPreference?: UserVerificationPreference`** - Optional preference for user verification. Can be:
86
+ - `"preferred"` - User verification is preferred but not required (default)
87
+ - `"required"` - User verification is required
88
+ - `"discouraged"` - User verification should be discouraged
79
89
 
80
90
  #### Returns
81
91
 
@@ -91,49 +101,20 @@ interface GetCredentialResult {
91
101
  authenticatorData: Buffer; // Authenticator data from the device
92
102
  signature: Buffer; // Digital signature from the authenticator
93
103
  userHandle: Buffer; // User handle from the credential
104
+ prf: [Buffer | null, Buffer | null]; // PRF output (if supported by authenticator)
105
+ largeBlob: Buffer | null; // Large blob data (if supported by authenticator)
94
106
  }
95
107
  ```
96
108
 
97
- ## Architecture
98
-
99
- ### Package Structure
109
+ #### User Verification Preference Type
100
110
 
111
+ ```typescript
112
+ type UserVerificationPreference = "preferred" | "required" | "discouraged";
101
113
  ```
102
- src/
103
- ├── index.ts # Main export - getCredential function
104
- ├── helpers.ts # Utility functions
105
- ├── objc/
106
- │ ├── authentication-services/ # AuthenticationServices framework bindings
107
- │ │ ├── as-authorization-*.ts # Authorization request/response objects
108
- │ │ ├── enums/ # Enumeration values
109
- │ │ └── index.ts # Exports
110
- │ └── foundation/ # Foundation framework bindings
111
- │ ├── nsdata.ts # NSData (binary data) bindings
112
- │ ├── nsstring.ts # NSString bindings
113
- │ ├── nsarray.ts # NSArray bindings
114
- │ ├── nserror.ts # NSError bindings
115
- │ ├── nsview.ts # NSView bindings
116
- │ ├── nswindow.ts # NSWindow bindings
117
- │ └── index.ts # Exports
118
- └── test/ # Test utilities
119
- ├── index.ts # Test script
120
- ├── window.ts # Test window helpers
121
- └── example.ts # Example usage
122
- ```
123
-
124
- ### Dependencies
125
114
 
126
- - **objc-js** - Native Objective-C bindings for JavaScript
127
- - **TypeScript** - For type checking and compilation
128
-
129
- ## How It Works
115
+ ## Architecture
130
116
 
131
- 1. **Request Creation**: The library creates a `ASAuthorizationPlatformPublicKeyCredentialProvider` with your RP ID
132
- 2. **Request Configuration**: Sets up the challenge and optional allowed credentials list
133
- 3. **Controller Setup**: Creates an `ASAuthorizationController` to manage the authentication flow
134
- 4. **Delegation**: Registers delegates to handle success/error responses from the system
135
- 5. **Presentation**: Shows the native authentication UI in your window
136
- 6. **Result Processing**: Converts Objective-C objects to JavaScript Buffers and returns the assertion data
117
+ This library implements the WebAuthn standard using native APIs. Under the hood it handles all the complexity of macOS's authentication system, so you just call `getCredential()` with your challenge and get back the signed assertion.
137
118
 
138
119
  ## Usage Examples
139
120
 
@@ -155,7 +136,7 @@ app.on("ready", () => {
155
136
  // In your preload script or main process
156
137
  export async function authenticateUser(challenge: Buffer) {
157
138
  const nativeHandle = getPointer(mainWindow.getNativeWindowHandle());
158
- return getCredential("myapp.com", challenge, nativeHandle, []);
139
+ return getCredential("myapp.com", challenge, "https://myapp.com", []);
159
140
  }
160
141
  ```
161
142
 
@@ -166,11 +147,43 @@ export async function authenticateUser(challenge: Buffer) {
166
147
  const result = await getCredential(
167
148
  "myapp.com",
168
149
  challenge,
169
- nativeWindowHandle,
150
+ "https://myapp.com",
170
151
  [credentialId1, credentialId2] // User can only use these credentials
171
152
  );
172
153
  ```
173
154
 
155
+ ### With User Verification Requirement
156
+
157
+ ```typescript
158
+ // Require user verification (e.g., for sensitive operations)
159
+ const result = await getCredential(
160
+ "myapp.com",
161
+ challenge,
162
+ "https://myapp.com",
163
+ [], // No credential restrictions
164
+ "required" // Require user verification (biometric or PIN)
165
+ );
166
+ ```
167
+
168
+ ### Using PRF Output
169
+
170
+ ```typescript
171
+ // Get credential with PRF support
172
+ const result = await getCredential(
173
+ "myapp.com",
174
+ challenge,
175
+ "https://myapp.com",
176
+ []
177
+ );
178
+
179
+ // PRF output is available in the result
180
+ const [prfFirst, prfSecond] = result.prf;
181
+ if (prfFirst) {
182
+ // Use PRF output for additional cryptographic operations
183
+ console.log("PRF first output:", prfFirst);
184
+ }
185
+ ```
186
+
174
187
  ### Server-Side Verification
175
188
 
176
189
  After getting the assertion result, verify it on your server:
@@ -197,7 +210,7 @@ try {
197
210
  const result = await getCredential(
198
211
  "example.com",
199
212
  challenge,
200
- nativeWindowHandle,
213
+ "https://example.com",
201
214
  []
202
215
  );
203
216
  } catch (error) {
@@ -206,51 +219,25 @@ try {
206
219
  }
207
220
  ```
208
221
 
209
- ## Platform Support
210
-
211
- | Platform | Support |
212
- | --------- | ---------------- |
213
- | macOS 12+ | ✅ Full support |
214
- | Windows | ❌ Not supported |
215
- | Linux | ❌ Not supported |
216
- | iOS | ❌ Not supported |
217
-
218
- ## WebAuthn Spec Compliance
219
-
220
- This library implements the WebAuthn Level 2 specification for the assertion (authentication) operation:
221
-
222
- - ✅ Public Key Credential (assertions)
223
- - ✅ Platform authenticators (built-in)
224
- - ✅ Cross-platform authenticators (external)
225
- - ❌ Credential registration (create)
226
- - ℹ️ See [WebAuthn Specification](https://www.w3.org/TR/webauthn-2/)
227
-
228
- ## Development
229
-
230
- ### Building
231
-
232
- ```bash
233
- bun run build
234
- ```
235
-
236
- This compiles TypeScript to JavaScript in the `dist/` directory.
222
+ **Supported:**
237
223
 
238
- ## Contributing
224
+ - ✅ WebAuthn assertions (authentication with existing credentials)
225
+ - ✅ Cross-platform authenticators (external security keys)
226
+ - ✅ Platform authenticators (Touch ID, Face ID)
227
+ - ✅ PRF (Pseudo-Random Function) output
228
+ - ✅ Large Blob support
239
229
 
240
- Contributions are welcome! Please ensure:
230
+ **Not Supported:**
241
231
 
242
- 1. Code is properly typed with TypeScript
243
- 2. New features include appropriate documentation
244
- 3. Tests pass with `bun run test`
245
- 4. Code builds successfully with `bun run build`
232
+ - Credential registration (attestation)
233
+ - Discoverable credentials
246
234
 
247
235
  ## License
248
236
 
249
237
  See [LICENSE](./LICENSE) file for details.
250
238
 
251
- ## Related Resources
239
+ ## Resources
252
240
 
253
241
  - [WebAuthn Specification](https://www.w3.org/TR/webauthn-2/)
254
- - [Apple AuthenticationServices Documentation](https://developer.apple.com/documentation/authenticationservices)
255
242
  - [Electron Documentation](https://www.electronjs.org/docs)
256
243
  - [objc-js Library](https://github.com/iamEvanYT/objc-js)
@@ -0,0 +1,4 @@
1
+ export interface CreateCredentialResult {
2
+ }
3
+ declare function createCredential(rpid: string): Promise<CreateCredentialResult>;
4
+ export { createCredential };
@@ -0,0 +1,6 @@
1
+ import { PromiseWithResolvers } from "../helpers/index.js";
2
+ function createCredential(rpid) {
3
+ const { promise, resolve, reject } = PromiseWithResolvers();
4
+ return promise;
5
+ }
6
+ export { createCredential };
@@ -2,4 +2,3 @@ import { NobjcObject } from "objc-js";
2
2
  export declare function setClientDataHash(self: NobjcObject, clientDataHash: Buffer): void;
3
3
  export declare function removeClientDataHash(self: NobjcObject): void;
4
4
  export declare const WebauthnGetController: NobjcObject;
5
- //# sourceMappingURL=get-authorization-controller.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { NobjcClass, NobjcObject, getPointer } from "objc-js";
2
- import { NSDataFromBuffer } from "./objc/foundation/nsdata.js";
2
+ import { NSDataFromBuffer } from "../objc/foundation/nsdata.js";
3
3
  const getControllerState = new Map();
4
4
  function getObjectPointerString(self) {
5
5
  return getPointer(self).toBase64();
@@ -16,12 +16,10 @@ export const WebauthnGetController = NobjcClass.define({
16
16
  name: "WebauthnGetController",
17
17
  superclass: "ASAuthorizationController",
18
18
  methods: {
19
- // This overrides the default implementation of _requestContextWithRequests$error$ to allow us to set the clientDataHash on the assertion options
20
19
  _requestContextWithRequests$error$: {
21
20
  types: "@@:@^@",
22
21
  implementation: (self, requests, outError) => {
23
22
  const context = NobjcClass.super(self, "_requestContextWithRequests$error$", requests, outError);
24
- // Grab the assertion options, set the client data hash, and set a copy of the assertion options back on the context
25
23
  const selfPointer = getObjectPointerString(self);
26
24
  if (getControllerState.has(selfPointer)) {
27
25
  const assertionOptions = context.platformKeyCredentialAssertionOptions();
@@ -34,4 +32,3 @@ export const WebauthnGetController = NobjcClass.define({
34
32
  },
35
33
  },
36
34
  });
37
- //# sourceMappingURL=get-authorization-controller.js.map
@@ -0,0 +1,24 @@
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;
16
+ }
17
+ export interface GetCredentialAdditionalOptions {
18
+ largeBlobDataToWrite?: Buffer;
19
+ prf?: PRFInput;
20
+ prfByCredential?: Record<string, PRFInput>;
21
+ topFrameOrigin?: string;
22
+ }
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 };
@@ -0,0 +1,179 @@
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"));
25
+ }
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);
39
+ }
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
+ }
50
+ }
51
+ }
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);
73
+ }
74
+ else {
75
+ console.warn("[electron-webauthn] prf is enabled but prf or prfByCredential is not provided, skipping PRF");
76
+ }
77
+ }
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,
100
+ };
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
+ }
108
+ }
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);
119
+ }
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
+ },
166
+ });
167
+ authController.setDelegate$(delegate);
168
+ const presentationContextProvider = createPresentationContextProvider({
169
+ presentationAnchorForAuthorizationController: () => {
170
+ const nsView = fromPointer(nativeWindowHandle);
171
+ const nsWindow = nsView.window();
172
+ return nsWindow;
173
+ },
174
+ });
175
+ authController.setPresentationContextProvider$(presentationContextProvider);
176
+ authController.performRequests();
177
+ return promise;
178
+ }
179
+ export { getCredential };
@@ -0,0 +1,8 @@
1
+ export declare function PromiseWithResolvers<T = void>(): {
2
+ promise: Promise<T>;
3
+ resolve: (value: T | PromiseLike<T>) => void;
4
+ reject: (reason?: unknown) => void;
5
+ };
6
+ export declare function clientDataJsonBufferToHash(clientDataJSON: Buffer): Buffer;
7
+ export declare function bufferToBase64Url(buffer: Buffer): string;
8
+ export declare function base64UrlToBuffer(b64url: string): Buffer;
@@ -0,0 +1,43 @@
1
+ import { createHash } from "crypto";
2
+ export function PromiseWithResolvers() {
3
+ let resolve;
4
+ let reject;
5
+ const promise = new Promise((res, rej) => {
6
+ resolve = res;
7
+ reject = rej;
8
+ });
9
+ return { promise, resolve: resolve, reject: reject };
10
+ }
11
+ export function clientDataJsonBufferToHash(clientDataJSON) {
12
+ if (!Buffer.isBuffer(clientDataJSON)) {
13
+ throw new TypeError("clientDataJsonBufferToHash: clientDataJSON must be a Buffer");
14
+ }
15
+ if (clientDataJSON.length === 0) {
16
+ throw new RangeError("clientDataJsonBufferToHash: clientDataJSON is empty");
17
+ }
18
+ return createHash("sha256").update(clientDataJSON).digest();
19
+ }
20
+ export function bufferToBase64Url(buffer) {
21
+ const bytes = new Uint8Array(buffer);
22
+ let binary = "";
23
+ for (let i = 0; i < bytes.length; i++) {
24
+ binary += String.fromCharCode(bytes[i]);
25
+ }
26
+ return btoa(binary)
27
+ .replace(/\+/g, "-")
28
+ .replace(/\//g, "_")
29
+ .replace(/=+$/, "");
30
+ }
31
+ export function base64UrlToBuffer(b64url) {
32
+ if (typeof b64url !== "string")
33
+ throw new TypeError("base64Url must be a string");
34
+ let b64 = b64url.replace(/-/g, "+").replace(/_/g, "/");
35
+ const pad = b64.length % 4;
36
+ if (pad === 2)
37
+ b64 += "==";
38
+ else if (pad === 3)
39
+ b64 += "=";
40
+ else if (pad !== 0)
41
+ throw new Error("Invalid base64url length");
42
+ return Buffer.from(b64, "base64");
43
+ }
@@ -0,0 +1,19 @@
1
+ export declare function serializeOrigin(origin: string): string | null;
2
+ type TupleOrigin = {
3
+ type: "tuple";
4
+ scheme: string;
5
+ host: string;
6
+ port: number | null;
7
+ };
8
+ type OpaqueOrigin = {
9
+ type: "opaque";
10
+ reason?: string;
11
+ };
12
+ type Origin = TupleOrigin | OpaqueOrigin;
13
+ export type OriginCompareOptions = {
14
+ allowOpaqueStringEquality?: boolean;
15
+ };
16
+ export declare function computeOrigin(input: string | URL): Origin;
17
+ export declare function isSameOrigin(a: string | URL, b: string | URL, opts?: OriginCompareOptions): boolean;
18
+ export declare function isCrossOrigin(a: string | URL, b: string | URL, opts?: OriginCompareOptions): boolean;
19
+ export {};
@@ -0,0 +1,106 @@
1
+ export function serializeOrigin(origin) {
2
+ if (origin === "null" || !origin) {
3
+ return null;
4
+ }
5
+ try {
6
+ const url = new URL(origin);
7
+ let result = url.protocol;
8
+ if (!result.endsWith("://")) {
9
+ result = result.replace(/:$/, "") + "://";
10
+ }
11
+ result += url.hostname;
12
+ if (url.port) {
13
+ result += ":" + url.port;
14
+ }
15
+ return result;
16
+ }
17
+ catch (error) {
18
+ return null;
19
+ }
20
+ }
21
+ const DEFAULT_PORT = {
22
+ http: 80,
23
+ https: 443,
24
+ ws: 80,
25
+ wss: 443,
26
+ ftp: 21,
27
+ };
28
+ const TUPLE_SCHEMES = new Set(["http", "https", "ws", "wss", "ftp"]);
29
+ export function computeOrigin(input) {
30
+ if (typeof input === "string" && input.trim().toLowerCase() === "null") {
31
+ return { type: "opaque", reason: "explicit 'null' origin string" };
32
+ }
33
+ const url = toURL(input);
34
+ if (!url)
35
+ return { type: "opaque", reason: "unparseable URL/origin string" };
36
+ const scheme = url.protocol.replace(/:$/, "").toLowerCase();
37
+ if (scheme === "blob") {
38
+ const embedded = url.href.slice("blob:".length);
39
+ const embeddedUrl = toURL(embedded);
40
+ return embeddedUrl
41
+ ? computeOrigin(embeddedUrl)
42
+ : { type: "opaque", reason: "blob: with unparseable embedded URL" };
43
+ }
44
+ if (!TUPLE_SCHEMES.has(scheme)) {
45
+ return { type: "opaque", reason: `non-tuple scheme '${scheme}'` };
46
+ }
47
+ const host = url.hostname;
48
+ if (!host)
49
+ return { type: "opaque", reason: "missing hostname" };
50
+ const rawPort = url.port ? safeParsePort(url.port) : null;
51
+ const port = normalizePort(scheme, rawPort);
52
+ return { type: "tuple", scheme, host, port };
53
+ }
54
+ function normalizePort(scheme, port) {
55
+ if (port == null)
56
+ return null;
57
+ const def = DEFAULT_PORT[scheme];
58
+ if (def != null && port === def)
59
+ return null;
60
+ return port;
61
+ }
62
+ function safeParsePort(portStr) {
63
+ const n = Number(portStr);
64
+ if (!Number.isInteger(n) || n < 0 || n > 65535)
65
+ return null;
66
+ return n;
67
+ }
68
+ function toURL(input) {
69
+ if (input instanceof URL)
70
+ return input;
71
+ try {
72
+ return new URL(input);
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
78
+ export function isSameOrigin(a, b, opts = {}) {
79
+ const oa = computeOrigin(a);
80
+ const ob = computeOrigin(b);
81
+ if (oa.type === "tuple" && ob.type === "tuple") {
82
+ return (oa.scheme === ob.scheme && oa.host === ob.host && oa.port === ob.port);
83
+ }
84
+ if (opts.allowOpaqueStringEquality) {
85
+ const sa = originString(a);
86
+ const sb = originString(b);
87
+ return sa != null && sb != null && sa === sb;
88
+ }
89
+ return false;
90
+ }
91
+ export function isCrossOrigin(a, b, opts) {
92
+ return !isSameOrigin(a, b, opts);
93
+ }
94
+ function originString(x) {
95
+ if (typeof x === "string") {
96
+ return serializeOrigin(x);
97
+ }
98
+ const url = toURL(x);
99
+ if (!url) {
100
+ return null;
101
+ }
102
+ const o = computeOrigin(url);
103
+ if (o.type === "opaque")
104
+ return "null";
105
+ return `${o.scheme}://${o.host}${o.port == null ? "" : `:${o.port}`}`;
106
+ }
@@ -0,0 +1,5 @@
1
+ export interface PRFInput {
2
+ first: Buffer;
3
+ second?: Buffer;
4
+ }
5
+ export declare function createPRFInput(prf: PRFInput): import("../objc/authentication-services/as-authorization-public-key-credential-prf-assertion-input-valuesas-authorization-public-key-credential-prf-assertion-input-values.js")._ASAuthorizationPublicKeyCredentialPRFAssertionInputValues;