electron-webauthn 1.1.0 → 1.1.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/README.md +16 -1
- package/dist/list/handler.js +18 -12
- package/package.json +3 -3
- package/dist/helpers/objc.d.ts +0 -5
- package/dist/helpers/objc.js +0 -10
package/README.md
CHANGED
|
@@ -21,6 +21,7 @@ This package provides JavaScript bindings to Apple's AuthenticationServices fram
|
|
|
21
21
|
- Credential creation (registration) with attestation
|
|
22
22
|
- Credential authentication (assertions) with existing credentials
|
|
23
23
|
- Seamless integration with Electron's native window system
|
|
24
|
+
- Passkey listing via `listPasskeys` (macOS 13.3+)
|
|
24
25
|
- PRF (Pseudo-Random Function) extension support
|
|
25
26
|
- Large Blob extension support for reading/writing credential-specific data
|
|
26
27
|
- User verification preference configuration (preferred, required, discouraged)
|
|
@@ -83,7 +84,7 @@ This library implements the W3C WebAuthn standard using Apple's native Authentic
|
|
|
83
84
|
|
|
84
85
|
## Error Handling
|
|
85
86
|
|
|
86
|
-
|
|
87
|
+
`createCredential`, `getCredential`, and `listPasskeys` all return a result object with a `success` field. Always check this field:
|
|
87
88
|
|
|
88
89
|
```typescript
|
|
89
90
|
const result = await createCredential(publicKeyOptions, additionalOptions);
|
|
@@ -118,6 +119,19 @@ if (!result.success) {
|
|
|
118
119
|
console.log("Credential ID:", result.data.credentialId);
|
|
119
120
|
```
|
|
120
121
|
|
|
122
|
+
`listPasskeys` returns an `Error` object (not a string code) when unsuccessful:
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
const result = await listPasskeys("example.com");
|
|
126
|
+
|
|
127
|
+
if (!result.success) {
|
|
128
|
+
console.error("Failed to list passkeys:", result.error.message);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
console.log(`Found ${result.credentials.length} passkeys`);
|
|
133
|
+
```
|
|
134
|
+
|
|
121
135
|
### Common Error Scenarios
|
|
122
136
|
|
|
123
137
|
#### For Both Registration and Authentication
|
|
@@ -141,6 +155,7 @@ console.log("Credential ID:", result.data.credentialId);
|
|
|
141
155
|
|
|
142
156
|
- ✅ WebAuthn credential creation (registration/attestation)
|
|
143
157
|
- ✅ WebAuthn assertions (authentication with existing credentials)
|
|
158
|
+
- ✅ Passkey listing with `listPasskeys` (macOS 13.3+, requires `com.apple.developer.web-browser.public-key-credential` entitlement)
|
|
144
159
|
- ✅ Cross-platform authenticators (external security keys like YubiKey)
|
|
145
160
|
- ✅ Platform authenticators (Touch ID, Face ID)
|
|
146
161
|
- ✅ Discoverable credentials (resident keys)
|
package/dist/list/handler.js
CHANGED
|
@@ -3,7 +3,13 @@ import { bufferFromNSDataDirect } from "objcjs-types/nsdata";
|
|
|
3
3
|
import { NSStringFromString } from "objcjs-types/helpers";
|
|
4
4
|
import { ASAuthorizationWebBrowserPublicKeyCredentialManager, ASAuthorizationWebBrowserPublicKeyCredentialManagerAuthorizationState, } from "objcjs-types/AuthenticationServices";
|
|
5
5
|
import { isAtLeast, version, formatVersion, getOSVersion, } from "objcjs-types/osversion";
|
|
6
|
-
import {
|
|
6
|
+
import { enumFromValue, makePromise1Result } from "objcjs-types/helpers";
|
|
7
|
+
const LOGGING_ENABLED = false;
|
|
8
|
+
function log(...args) {
|
|
9
|
+
if (!LOGGING_ENABLED)
|
|
10
|
+
return;
|
|
11
|
+
console.log(...args);
|
|
12
|
+
}
|
|
7
13
|
export async function listPasskeys(relyingPartyId) {
|
|
8
14
|
try {
|
|
9
15
|
const minVersion = version(13, 3);
|
|
@@ -13,26 +19,26 @@ export async function listPasskeys(relyingPartyId) {
|
|
|
13
19
|
}
|
|
14
20
|
const manager = ASAuthorizationWebBrowserPublicKeyCredentialManager.alloc().init();
|
|
15
21
|
const authState = manager.authorizationStateForPlatformCredentials();
|
|
16
|
-
|
|
22
|
+
log(`[listPasskeys] Authorization state: ${authState}`);
|
|
17
23
|
if (authState ===
|
|
18
24
|
ASAuthorizationWebBrowserPublicKeyCredentialManagerAuthorizationState.NotDetermined) {
|
|
19
|
-
|
|
20
|
-
const newStateValue = await
|
|
25
|
+
log("[listPasskeys] Authorization not determined, requesting...");
|
|
26
|
+
const newStateValue = await makePromise1Result(manager.requestAuthorizationForPublicKeyCredentials$.bind(manager));
|
|
21
27
|
const newState = enumFromValue(ASAuthorizationWebBrowserPublicKeyCredentialManagerAuthorizationState, newStateValue);
|
|
22
|
-
|
|
28
|
+
log(`[listPasskeys] Authorization request completed, new state: ${newState}`);
|
|
23
29
|
}
|
|
24
30
|
else if (authState ===
|
|
25
31
|
ASAuthorizationWebBrowserPublicKeyCredentialManagerAuthorizationState.Denied) {
|
|
26
32
|
throw new Error("Authorization DENIED - user must grant permission in System Settings > Privacy & Security");
|
|
27
33
|
}
|
|
28
34
|
else {
|
|
29
|
-
|
|
35
|
+
log("[listPasskeys] Authorization already granted");
|
|
30
36
|
}
|
|
31
37
|
const rpIdString = NSStringFromString(relyingPartyId);
|
|
32
|
-
|
|
33
|
-
const credentialsArray = await
|
|
38
|
+
log(`[listPasskeys] Calling platformCredentialsForRelyingParty: ${relyingPartyId}`);
|
|
39
|
+
const credentialsArray = await makePromise1Result(manager.platformCredentialsForRelyingParty$completionHandler$.bind(manager), rpIdString);
|
|
34
40
|
const count = credentialsArray.count();
|
|
35
|
-
|
|
41
|
+
log(`[listPasskeys] platformCredentials returned ${count} entries`);
|
|
36
42
|
const credentials = [];
|
|
37
43
|
for (let i = 0; i < count; i++) {
|
|
38
44
|
const cred = credentialsArray.objectAtIndex$(i);
|
|
@@ -41,7 +47,7 @@ export async function listPasskeys(relyingPartyId) {
|
|
|
41
47
|
const userHandleData = cred.userHandle();
|
|
42
48
|
const credentialId = bufferToBase64Url(bufferFromNSDataDirect(credentialIdData));
|
|
43
49
|
const userHandle = bufferToBase64Url(bufferFromNSDataDirect(userHandleData));
|
|
44
|
-
|
|
50
|
+
log(`[listPasskeys] Found credential: name=${userName}, id=${credentialId.substring(0, 20)}...`);
|
|
45
51
|
credentials.push({
|
|
46
52
|
id: credentialId,
|
|
47
53
|
rpId: relyingPartyId,
|
|
@@ -49,14 +55,14 @@ export async function listPasskeys(relyingPartyId) {
|
|
|
49
55
|
userHandle,
|
|
50
56
|
});
|
|
51
57
|
}
|
|
52
|
-
|
|
58
|
+
log(`[listPasskeys] Returning ${credentials.length} results`);
|
|
53
59
|
return {
|
|
54
60
|
success: true,
|
|
55
61
|
credentials,
|
|
56
62
|
};
|
|
57
63
|
}
|
|
58
64
|
catch (error) {
|
|
59
|
-
console.error("[listPasskeys]
|
|
65
|
+
console.error("[listPasskeys] ", error);
|
|
60
66
|
return {
|
|
61
67
|
success: false,
|
|
62
68
|
error: error instanceof Error ? error : new Error(String(error)),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electron-webauthn",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"repository": "https://github.com/iamEvanYT/electron-webauthn.git",
|
|
5
5
|
"homepage": "https://github.com/iamEvanYT/electron-webauthn#readme",
|
|
6
6
|
"description": "Add support for WebAuthn for Electron.",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@oslojs/webauthn": "^1.0.0",
|
|
33
|
-
"objc-js": "^1.3.
|
|
34
|
-
"objcjs-types": "^0.5.
|
|
33
|
+
"objc-js": "^1.3.1",
|
|
34
|
+
"objcjs-types": "^0.5.1"
|
|
35
35
|
},
|
|
36
36
|
"trustedDependencies": [
|
|
37
37
|
"objc-js"
|
package/dist/helpers/objc.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export declare function enumFromValue<T extends Record<string, number>>(enumObj: T, value: number): keyof T | undefined;
|
|
2
|
-
export declare function makePromise<T, Args extends unknown[]>(...argsAndFunc: [
|
|
3
|
-
...args: Args,
|
|
4
|
-
func: (...args: [...Args, callback: (result: T) => void]) => void
|
|
5
|
-
]): Promise<T>;
|
package/dist/helpers/objc.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export function enumFromValue(enumObj, value) {
|
|
2
|
-
return Object.keys(enumObj).find((key) => enumObj[key] === value);
|
|
3
|
-
}
|
|
4
|
-
export function makePromise(...argsAndFunc) {
|
|
5
|
-
const func = argsAndFunc[argsAndFunc.length - 1];
|
|
6
|
-
const args = argsAndFunc.slice(0, -1);
|
|
7
|
-
return new Promise((resolve) => {
|
|
8
|
-
func(...args, resolve);
|
|
9
|
-
});
|
|
10
|
-
}
|