akey-electron-webauthn 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 iamEvan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,223 @@
1
+ # electron-webauthn
2
+
3
+ Add native WebAuthn/FIDO2 support to Electron on macOS using its AuthenticationServices framework.
4
+
5
+ ## Overview
6
+
7
+ `electron-webauthn` allows you to process WebAuthn requests on macOS Electron. Simply plug any `publicKeyOptions` from the standard `navigator.credentials.get()` or `navigator.credentials.create()` directly into this library's functions and they'll work with the native macOS authenticators.
8
+
9
+ This package provides JavaScript bindings to Apple's AuthenticationServices framework, allowing you to perform WebAuthn credential creation (registration) and assertions (authentication/signing) in your Electron applications using W3C WebAuthn-compliant APIs.
10
+
11
+ The root `electron-webauthn` package is a cross-platform shim that lazy-loads `@electron-webauthn/macos` only on macOS. This keeps Linux/Windows packaging safe for apps that include but do not use WebAuthn outside macOS.
12
+
13
+ > [!NOTE]
14
+ > Although this library is called `electron-webauthn`, it can be used in Node.js and Bun projects as well, not just Electron.
15
+
16
+ ## Features
17
+
18
+ - Native WebAuthn support for Electron on macOS
19
+ - Support for platform authenticators (Touch ID, Face ID)
20
+ - Support for cross-platform authenticators (external security keys)
21
+ - TypeScript first with complete type definitions
22
+ - **W3C WebAuthn-compliant API** - drop in any standard `publicKeyOptions` and it just works
23
+ - Credential creation (registration) with attestation
24
+ - Credential authentication (assertions) with existing credentials
25
+ - Seamless integration with Electron's native window system
26
+ - Passkey listing via `listPasskeys` (macOS 13.3+)
27
+ - Explicit passkey-listing authorization APIs with `getListPasskeyAuthorizationStatus` and `requestListPasskeyAuthorization`
28
+ - PRF (Pseudo-Random Function) extension support
29
+ - Large Blob extension support for reading/writing credential-specific data
30
+ - User verification preference configuration (preferred, required, discouraged)
31
+ - Proper origin validation with public suffix list support
32
+ - Cross-origin iframe support with `topFrameOrigin`
33
+ - Per-credential PRF evaluation with `evalByCredential`
34
+ - Resident key (discoverable credential) support
35
+
36
+ ## Installation
37
+
38
+ ### Prerequisites
39
+
40
+ The following prerequisites apply when running on macOS:
41
+
42
+ - Node.js / Bun
43
+ - Xcode Command Line Tools (Run `xcode-select --install` to install)
44
+ - `pkg-config` from Homebrew (Run `brew install pkgconf` to install)
45
+
46
+ ### Install using npm, bun, pnpm, or yarn
47
+
48
+ ```bash
49
+ npm install electron-webauthn
50
+ # or
51
+ bun add electron-webauthn
52
+ # or
53
+ pnpm add electron-webauthn
54
+ # or
55
+ yarn add electron-webauthn
56
+ ```
57
+
58
+ ### Requirements
59
+
60
+ - Electron 28+
61
+ - macOS 12.0+
62
+ - TypeScript 5+ (peer dependency)
63
+
64
+ ## Quick Start
65
+
66
+ See the [Quick Start Guide](./docs/quick-start.md) for detailed examples on credential creation and authentication.
67
+
68
+ ## Configure Entitlements and Provisioning
69
+
70
+ See the [Entitlements and Provisioning Guide](./docs/entitlements-and-provisioning.md) for detailed instructions on how to configure entitlements and provisioning for your Electron application.
71
+
72
+ ## API Reference
73
+
74
+ See the [API Reference](./docs/api-reference.md) for detailed documentation on all available functions and types.
75
+
76
+ ## Advanced Examples
77
+
78
+ See the [Advanced Examples](./docs/advanced-examples.md) for detailed examples on how to use the library with more complex use cases.
79
+
80
+ ## Architecture
81
+
82
+ This library implements the W3C WebAuthn standard using Apple's native AuthenticationServices framework. It provides a standards-compliant API that works seamlessly with existing WebAuthn servers and libraries. Under the hood, it:
83
+
84
+ - Validates origins and Relying Party IDs according to the WebAuthn specification
85
+ - Generates proper client data with `crossOrigin` field support (fixing Apple's default behavior)
86
+ - Handles both platform (Touch ID/Face ID) and cross-platform (security keys) authenticators simultaneously
87
+ - Properly manages WebAuthn extensions (PRF, Large Blob)
88
+ - Returns base64url-encoded values ready to send to your server for verification
89
+
90
+ ## Error Handling
91
+
92
+ `createCredential`, `getCredential`, `getListPasskeyAuthorizationStatus`, `requestListPasskeyAuthorization`, and `listPasskeys` all return a result object with a `success` field. Always check this field:
93
+
94
+ ```typescript
95
+ const result = await createCredential(publicKeyOptions, additionalOptions);
96
+ // or
97
+ const result = await getCredential(publicKeyOptions, additionalOptions);
98
+
99
+ if (!result.success) {
100
+ // Handle specific error types
101
+ switch (result.error) {
102
+ case "TypeError":
103
+ console.error("Invalid parameters provided");
104
+ break;
105
+ case "NotAllowedError":
106
+ console.error("User cancelled or operation not allowed");
107
+ break;
108
+ case "SecurityError":
109
+ console.error("Origin or rpId validation failed");
110
+ break;
111
+ case "AbortError":
112
+ console.error("Operation was aborted");
113
+ break;
114
+ case "InvalidStateError":
115
+ console.error(
116
+ "Authenticator is in invalid state (e.g., credential already registered)"
117
+ );
118
+ break;
119
+ }
120
+ return;
121
+ }
122
+
123
+ // Success - process the credential
124
+ console.log("Credential ID:", result.data.credentialId);
125
+ ```
126
+
127
+ `listPasskeys` returns an `Error` object (not a string code) when unsuccessful:
128
+
129
+ ```typescript
130
+ const permission = await getListPasskeyAuthorizationStatus();
131
+ if (permission.success && permission.status === "notDetermined") {
132
+ await requestListPasskeyAuthorization();
133
+ }
134
+
135
+ const result = await listPasskeys("example.com", {
136
+ requestAuthorization: false,
137
+ });
138
+
139
+ if (!result.success) {
140
+ console.error("Failed to list passkeys:", result.error.message);
141
+ return;
142
+ }
143
+
144
+ console.log(`Found ${result.credentials.length} passkeys`);
145
+ ```
146
+
147
+ ### Common Error Scenarios
148
+
149
+ #### For Both Registration and Authentication
150
+
151
+ - **TypeError**: Invalid parameter types (missing required fields, wrong data types)
152
+ - **NotAllowedError**: User cancelled the prompt or the authenticator failed
153
+ - **SecurityError**: Origin doesn't match rpId, invalid origin format, or rpId is a public suffix
154
+ - **AbortError**: Operation timeout or explicitly aborted
155
+
156
+ #### Registration-Specific (`createCredential`)
157
+
158
+ - **InvalidStateError**: The authenticator attempted to register a credential that matches one in the `excludeCredentials` list (prevents duplicate registrations)
159
+
160
+ #### Authentication-Specific (`getCredential`)
161
+
162
+ - **NotAllowedError**: No valid credentials available for the specified rpId
163
+
164
+ #### Passkey Authorization / Listing
165
+
166
+ - **`getListPasskeyAuthorizationStatus()`**: Returns `authorized`, `denied`, or `notDetermined` without prompting
167
+ - **`requestListPasskeyAuthorization()`**: Shows the macOS permission prompt only when the current state is `notDetermined`
168
+ - **`listPasskeys(..., { requestAuthorization: false })`**: Returns an `Error` when permission has not been decided yet instead of triggering the prompt automatically
169
+
170
+ ## Feature Support
171
+
172
+ **Currently Supported:**
173
+
174
+ - ✅ WebAuthn credential creation (registration/attestation)
175
+ - ✅ WebAuthn assertions (authentication with existing credentials)
176
+ - ✅ Passkey listing with `listPasskeys` (macOS 13.3+, requires `com.apple.developer.web-browser.public-key-credential` entitlement)
177
+ - ✅ Cross-platform authenticators (external security keys like YubiKey)
178
+ - ✅ Platform authenticators (Touch ID, Face ID)
179
+ - ✅ Discoverable credentials (resident keys)
180
+ - ✅ Attestation formats (none, indirect, direct)
181
+ - ✅ Duplicate credential prevention with `excludeCredentials`
182
+ - ✅ PRF (Pseudo-Random Function) extension
183
+ - ✅ Global evaluation (`eval`) for both registration and authentication
184
+ - ✅ Per-credential evaluation (`evalByCredential`) for authentication
185
+ - ✅ Large Blob extension
186
+ - ✅ Support indication during registration
187
+ - ✅ Reading blobs during authentication
188
+ - ✅ Writing blobs during authentication
189
+ - ✅ credProps extension (credential properties)
190
+ - ✅ User verification preferences (required, preferred, discouraged)
191
+ - ✅ Credential filtering with `allowCredentials`
192
+ - ✅ Proper origin and rpId validation
193
+ - ✅ Cross-origin iframe support
194
+ - ✅ W3C WebAuthn-compliant client data generation
195
+
196
+ **Not Yet Supported:**
197
+
198
+ - ❌ Conditional UI (autofill/conditional mediation)
199
+ - ❌ Other WebAuthn extensions (credProtect, minPinLength, hmac-secret, etc.)
200
+
201
+ ### Security Recommendations
202
+
203
+ - **Implement public suffix list validation** using `tldts` or similar library
204
+
205
+ ## Known Limitations
206
+
207
+ - **PRF and Large Blob extensions**: Only supported on platform authenticators (Touch ID/Face ID), not security keys on macOS
208
+ - **Attestation formats**: macOS typically returns "none" attestation even when "direct" or "indirect" is requested, unless the authenticator specifically supports it
209
+
210
+ ## License
211
+
212
+ See [LICENSE](./LICENSE) file for details.
213
+
214
+ ## Contributing
215
+
216
+ Contributions are welcome! Please feel free to submit issues and pull requests.
217
+
218
+ ## Resources
219
+
220
+ - [W3C WebAuthn Level 3 Specification](https://www.w3.org/TR/webauthn-3/)
221
+ - [WebAuthn Guide](https://webauthn.guide/)
222
+ - [Electron Documentation](https://www.electronjs.org/docs)
223
+ - [objc-js Library](https://github.com/iamEvanYT/objc-js)
@@ -0,0 +1,10 @@
1
+ import type { CreateCredentialResult, GetCredentialResult, ListPasskeysOptions, ListPasskeysError, ListPasskeysResult, PasskeyAuthorizationError, PasskeyAuthorizationResult, WebauthnCreateRequestOptions, WebauthnGetRequestOptions, WebauthnModule } from "@electron-webauthn/types";
2
+ export type { CreateCredentialErrorCodes, CreateCredentialResult, CreateCredentialSuccessData, CreateCredentialSuccessResult, CreateCredentialErrorResult, GetCredentialErrorCodes, GetCredentialResult, GetCredentialSuccessData, GetCredentialSuccessResult, GetCredentialErrorResult, ListPasskeysOptions, ListPasskeysError, ListPasskeysResult, PasskeyAuthorizationError, PasskeyAuthorizationResult, PasskeyAuthorizationStatus, PasskeyCredential, WebauthnCreateRequestOptions, WebauthnGetRequestOptions, WebauthnModule, } from "@electron-webauthn/types";
3
+ type MacosLoader = () => Promise<WebauthnModule>;
4
+ export declare function createCredential(publicKeyOptions: PublicKeyCredentialCreationOptions | undefined, additionalOptions: WebauthnCreateRequestOptions): Promise<CreateCredentialResult>;
5
+ export declare function getCredential(publicKeyOptions: PublicKeyCredentialRequestOptions | undefined, additionalOptions: WebauthnGetRequestOptions): Promise<GetCredentialResult>;
6
+ export declare function getListPasskeyAuthorizationStatus(): Promise<PasskeyAuthorizationResult | PasskeyAuthorizationError>;
7
+ export declare function requestListPasskeyAuthorization(): Promise<PasskeyAuthorizationResult | PasskeyAuthorizationError>;
8
+ export declare function listPasskeys(relyingPartyId: string, options?: ListPasskeysOptions): Promise<ListPasskeysResult | ListPasskeysError>;
9
+ export declare function __setMacosLoaderForTesting(loader: MacosLoader | null): void;
10
+ export declare function __setPlatformForTesting(platform: NodeJS.Platform | null): void;
package/dist/index.js ADDED
@@ -0,0 +1,96 @@
1
+ const unsupportedGetResult = {
2
+ success: false,
3
+ error: "NotAllowedError",
4
+ errorObject: new Error("electron-webauthn is only available on macOS. Install @electron-webauthn/macos on Darwin hosts."),
5
+ };
6
+ const unsupportedCreateResult = {
7
+ success: false,
8
+ error: "NotAllowedError",
9
+ errorObject: new Error("electron-webauthn is only available on macOS. Install @electron-webauthn/macos on Darwin hosts."),
10
+ };
11
+ const unsupportedListResult = {
12
+ success: false,
13
+ error: new Error("electron-webauthn is only available on macOS. Install @electron-webauthn/macos on Darwin hosts."),
14
+ };
15
+ const unsupportedPasskeyAuthorizationResult = {
16
+ success: false,
17
+ error: new Error("electron-webauthn is only available on macOS. Install @electron-webauthn/macos on Darwin hosts."),
18
+ };
19
+ let moduleCache = null;
20
+ let modulePromise = null;
21
+ let platformOverride = null;
22
+ const defaultLoader = () => import("@electron-webauthn/macos");
23
+ let macosLoader = defaultLoader;
24
+ function runtimePlatform() {
25
+ return platformOverride ?? process.platform;
26
+ }
27
+ async function getMacosModule() {
28
+ if (runtimePlatform() !== "darwin") {
29
+ return null;
30
+ }
31
+ if (moduleCache) {
32
+ return moduleCache;
33
+ }
34
+ if (modulePromise) {
35
+ return modulePromise;
36
+ }
37
+ modulePromise = (async () => {
38
+ try {
39
+ const module = await macosLoader();
40
+ moduleCache = module;
41
+ return module;
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ finally {
47
+ modulePromise = null;
48
+ }
49
+ })();
50
+ return modulePromise;
51
+ }
52
+ export async function createCredential(publicKeyOptions, additionalOptions) {
53
+ const module = await getMacosModule();
54
+ if (!module) {
55
+ return unsupportedCreateResult;
56
+ }
57
+ return module.createCredential(publicKeyOptions, additionalOptions);
58
+ }
59
+ export async function getCredential(publicKeyOptions, additionalOptions) {
60
+ const module = await getMacosModule();
61
+ if (!module) {
62
+ return unsupportedGetResult;
63
+ }
64
+ return module.getCredential(publicKeyOptions, additionalOptions);
65
+ }
66
+ export async function getListPasskeyAuthorizationStatus() {
67
+ const module = await getMacosModule();
68
+ if (!module) {
69
+ return unsupportedPasskeyAuthorizationResult;
70
+ }
71
+ return module.getListPasskeyAuthorizationStatus();
72
+ }
73
+ export async function requestListPasskeyAuthorization() {
74
+ const module = await getMacosModule();
75
+ if (!module) {
76
+ return unsupportedPasskeyAuthorizationResult;
77
+ }
78
+ return module.requestListPasskeyAuthorization();
79
+ }
80
+ export async function listPasskeys(relyingPartyId, options) {
81
+ const module = await getMacosModule();
82
+ if (!module) {
83
+ return unsupportedListResult;
84
+ }
85
+ return module.listPasskeys(relyingPartyId, options);
86
+ }
87
+ export function __setMacosLoaderForTesting(loader) {
88
+ macosLoader = loader ?? defaultLoader;
89
+ moduleCache = null;
90
+ modulePromise = null;
91
+ }
92
+ export function __setPlatformForTesting(platform) {
93
+ platformOverride = platform;
94
+ moduleCache = null;
95
+ modulePromise = null;
96
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "akey-electron-webauthn",
3
+ "version": "1.3.1",
4
+ "description": "Cross-platform loader for native WebAuthn support on macOS.",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "dist/**"
18
+ ],
19
+ "type": "module",
20
+ "workspaces": [
21
+ "packages/*"
22
+ ],
23
+ "scripts": {
24
+ "build": "rm -rf dist && tsc -p tsconfig.json",
25
+ "build:macos": "cd packages/macos && bun run build",
26
+ "build:all": "bun run build && bun run build:macos",
27
+ "test": "bun test"
28
+ },
29
+ "devDependencies": {
30
+ "@types/bun": "latest"
31
+ },
32
+ "peerDependencies": {
33
+ "typescript": "^6.0.3"
34
+ },
35
+ "dependencies": {
36
+ "@electron-webauthn/types": "^1.3.0"
37
+ },
38
+ "optionalDependencies": {
39
+ "@electron-webauthn/macos": "^1.3.0"
40
+ }
41
+ }