electron-webauthn 1.1.1 → 1.3.0
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 +20 -2
- package/dist/index.d.ts +10 -3
- package/dist/index.js +96 -3
- package/package.json +16 -12
- package/dist/additional-objc/ASCPublicKeyCredentialDescriptor.d.ts +0 -12
- package/dist/additional-objc/ASCPublicKeyCredentialDescriptor.js +0 -2
- package/dist/create/authorization-controller.d.ts +0 -10
- package/dist/create/authorization-controller.js +0 -77
- package/dist/create/handler.d.ts +0 -43
- package/dist/create/handler.js +0 -224
- package/dist/create/internal-handler.d.ts +0 -35
- package/dist/create/internal-handler.js +0 -207
- package/dist/get/authorization-controller.d.ts +0 -5
- package/dist/get/authorization-controller.js +0 -37
- package/dist/get/handler.d.ts +0 -38
- package/dist/get/handler.js +0 -137
- package/dist/get/internal-handler.d.ts +0 -24
- package/dist/get/internal-handler.js +0 -169
- package/dist/helpers/client-data.d.ts +0 -14
- package/dist/helpers/client-data.js +0 -36
- package/dist/helpers/index.d.ts +0 -8
- package/dist/helpers/index.js +0 -48
- package/dist/helpers/origin.d.ts +0 -19
- package/dist/helpers/origin.js +0 -106
- package/dist/helpers/presentation.d.ts +0 -1
- package/dist/helpers/presentation.js +0 -11
- package/dist/helpers/prf.d.ts +0 -5
- package/dist/helpers/prf.js +0 -5
- package/dist/helpers/public-key.d.ts +0 -1
- package/dist/helpers/public-key.js +0 -49
- package/dist/helpers/rpid.d.ts +0 -13
- package/dist/helpers/rpid.js +0 -59
- package/dist/helpers/types.d.ts +0 -1
- package/dist/helpers/types.js +0 -1
- package/dist/helpers/validation.d.ts +0 -3
- package/dist/helpers/validation.js +0 -9
- package/dist/list/handler.d.ts +0 -2
- package/dist/list/handler.js +0 -71
- package/dist/list/types.d.ts +0 -14
- package/dist/list/types.js +0 -1
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@ Add native WebAuthn/FIDO2 support to Electron on macOS using its AuthenticationS
|
|
|
8
8
|
|
|
9
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
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
|
+
|
|
11
13
|
> [!NOTE]
|
|
12
14
|
> Although this library is called `electron-webauthn`, it can be used in Node.js and Bun projects as well, not just Electron.
|
|
13
15
|
|
|
@@ -22,6 +24,7 @@ This package provides JavaScript bindings to Apple's AuthenticationServices fram
|
|
|
22
24
|
- Credential authentication (assertions) with existing credentials
|
|
23
25
|
- Seamless integration with Electron's native window system
|
|
24
26
|
- Passkey listing via `listPasskeys` (macOS 13.3+)
|
|
27
|
+
- Explicit passkey-listing authorization APIs with `getListPasskeyAuthorizationStatus` and `requestListPasskeyAuthorization`
|
|
25
28
|
- PRF (Pseudo-Random Function) extension support
|
|
26
29
|
- Large Blob extension support for reading/writing credential-specific data
|
|
27
30
|
- User verification preference configuration (preferred, required, discouraged)
|
|
@@ -34,6 +37,8 @@ This package provides JavaScript bindings to Apple's AuthenticationServices fram
|
|
|
34
37
|
|
|
35
38
|
### Prerequisites
|
|
36
39
|
|
|
40
|
+
The following prerequisites apply when running on macOS:
|
|
41
|
+
|
|
37
42
|
- Node.js / Bun
|
|
38
43
|
- Xcode Command Line Tools (Run `xcode-select --install` to install)
|
|
39
44
|
- `pkg-config` from Homebrew (Run `brew install pkgconf` to install)
|
|
@@ -84,7 +89,7 @@ This library implements the W3C WebAuthn standard using Apple's native Authentic
|
|
|
84
89
|
|
|
85
90
|
## Error Handling
|
|
86
91
|
|
|
87
|
-
`createCredential`, `getCredential`, and `listPasskeys` all return a result object with a `success` field. Always check this field:
|
|
92
|
+
`createCredential`, `getCredential`, `getListPasskeyAuthorizationStatus`, `requestListPasskeyAuthorization`, and `listPasskeys` all return a result object with a `success` field. Always check this field:
|
|
88
93
|
|
|
89
94
|
```typescript
|
|
90
95
|
const result = await createCredential(publicKeyOptions, additionalOptions);
|
|
@@ -122,7 +127,14 @@ console.log("Credential ID:", result.data.credentialId);
|
|
|
122
127
|
`listPasskeys` returns an `Error` object (not a string code) when unsuccessful:
|
|
123
128
|
|
|
124
129
|
```typescript
|
|
125
|
-
const
|
|
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
|
+
});
|
|
126
138
|
|
|
127
139
|
if (!result.success) {
|
|
128
140
|
console.error("Failed to list passkeys:", result.error.message);
|
|
@@ -149,6 +161,12 @@ console.log(`Found ${result.credentials.length} passkeys`);
|
|
|
149
161
|
|
|
150
162
|
- **NotAllowedError**: No valid credentials available for the specified rpId
|
|
151
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
|
+
|
|
152
170
|
## Feature Support
|
|
153
171
|
|
|
154
172
|
**Currently Supported:**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
|
|
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
CHANGED
|
@@ -1,3 +1,96 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electron-webauthn",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"repository": "https://github.com/iamEvanYT/electron-webauthn.git",
|
|
5
5
|
"homepage": "https://github.com/iamEvanYT/electron-webauthn#readme",
|
|
6
|
-
"description": "
|
|
6
|
+
"description": "Cross-platform loader for native WebAuthn support on macOS.",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"module": "dist/index.js",
|
|
9
9
|
"types": "dist/index.d.ts",
|
|
@@ -15,25 +15,29 @@
|
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
|
-
"dist
|
|
18
|
+
"dist",
|
|
19
|
+
"dist/**"
|
|
19
20
|
],
|
|
20
21
|
"type": "module",
|
|
22
|
+
"workspaces": [
|
|
23
|
+
"packages/*"
|
|
24
|
+
],
|
|
21
25
|
"scripts": {
|
|
22
|
-
"build": "rm -rf dist && tsc
|
|
23
|
-
"
|
|
26
|
+
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
27
|
+
"build:macos": "cd packages/macos && bun run build",
|
|
28
|
+
"build:all": "bun run build && bun run build:macos",
|
|
29
|
+
"test": "bun test"
|
|
24
30
|
},
|
|
25
31
|
"devDependencies": {
|
|
26
32
|
"@types/bun": "latest"
|
|
27
33
|
},
|
|
28
34
|
"peerDependencies": {
|
|
29
|
-
"typescript": "^
|
|
35
|
+
"typescript": "^6.0.2"
|
|
30
36
|
},
|
|
31
37
|
"dependencies": {
|
|
32
|
-
"@
|
|
33
|
-
"objc-js": "^1.3.1",
|
|
34
|
-
"objcjs-types": "^0.5.1"
|
|
38
|
+
"@electron-webauthn/types": "^1.3.0"
|
|
35
39
|
},
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
|
|
40
|
+
"optionalDependencies": {
|
|
41
|
+
"@electron-webauthn/macos": "^1.3.0"
|
|
42
|
+
}
|
|
39
43
|
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { _NSArray, _NSData, _NSObject, _NSSecureCoding } from "objcjs-types/Foundation";
|
|
2
|
-
export declare class _ASCPublicKeyCredentialDescriptor extends _NSObject {
|
|
3
|
-
static alloc(): _ASCPublicKeyCredentialDescriptor;
|
|
4
|
-
static new(): _ASCPublicKeyCredentialDescriptor;
|
|
5
|
-
init(): _ASCPublicKeyCredentialDescriptor;
|
|
6
|
-
initWithCredentialID$transports$(credentialID: _NSData, allowedTransports: _NSArray | null): _ASCPublicKeyCredentialDescriptor;
|
|
7
|
-
credentialID(): _NSData;
|
|
8
|
-
transports(): _NSArray | null;
|
|
9
|
-
}
|
|
10
|
-
export interface _ASCPublicKeyCredentialDescriptor extends _NSSecureCoding {
|
|
11
|
-
}
|
|
12
|
-
export declare const ASCPublicKeyCredentialDescriptor: typeof _ASCPublicKeyCredentialDescriptor;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { NobjcObject } from "objc-js";
|
|
2
|
-
import type { ExcludeCredential } from "./internal-handler.js";
|
|
3
|
-
import type { ASAuthorizationController } from "objcjs-types/AuthenticationServices";
|
|
4
|
-
export interface PublicKeyCredentialParams {
|
|
5
|
-
type: "public-key";
|
|
6
|
-
algorithm: number;
|
|
7
|
-
}
|
|
8
|
-
export declare function setControllerState(self: NobjcObject, clientDataHash: Buffer, pubKeyCredParams: PublicKeyCredentialParams[], residentKeyRequired: boolean, excludeCredentialIds: ExcludeCredential[]): void;
|
|
9
|
-
export declare function removeControllerState(self: NobjcObject): void;
|
|
10
|
-
export declare const WebauthnCreateController: typeof ASAuthorizationController;
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { NobjcClass, NobjcObject, getPointer } from "objc-js";
|
|
2
|
-
import { NSDataFromBuffer } from "objcjs-types/nsdata";
|
|
3
|
-
import { NSArrayFromObjects, NSStringFromString } from "objcjs-types/helpers";
|
|
4
|
-
import { NSNumber } from "objcjs-types/Foundation";
|
|
5
|
-
import { ASCPublicKeyCredentialDescriptor } from "../additional-objc/ASCPublicKeyCredentialDescriptor.js";
|
|
6
|
-
const createControllerState = new Map();
|
|
7
|
-
function getObjectPointerString(self) {
|
|
8
|
-
return getPointer(self).toString("base64");
|
|
9
|
-
}
|
|
10
|
-
export function setControllerState(self, clientDataHash, pubKeyCredParams, residentKeyRequired, excludeCredentialIds) {
|
|
11
|
-
const selfPointer = getObjectPointerString(self);
|
|
12
|
-
createControllerState.set(selfPointer, [
|
|
13
|
-
clientDataHash,
|
|
14
|
-
pubKeyCredParams,
|
|
15
|
-
residentKeyRequired,
|
|
16
|
-
excludeCredentialIds,
|
|
17
|
-
]);
|
|
18
|
-
}
|
|
19
|
-
export function removeControllerState(self) {
|
|
20
|
-
const selfPointer = getObjectPointerString(self);
|
|
21
|
-
createControllerState.delete(selfPointer);
|
|
22
|
-
}
|
|
23
|
-
export const WebauthnCreateController = NobjcClass.define({
|
|
24
|
-
name: "WebauthnCreateController",
|
|
25
|
-
superclass: "ASAuthorizationController",
|
|
26
|
-
methods: {
|
|
27
|
-
_requestContextWithRequests$error$: {
|
|
28
|
-
types: "@@:@^@",
|
|
29
|
-
implementation: (self, requests, outError) => {
|
|
30
|
-
const context = NobjcClass.super(self, "_requestContextWithRequests$error$", requests, outError);
|
|
31
|
-
const selfPointer = getObjectPointerString(self);
|
|
32
|
-
if (context && createControllerState.has(selfPointer)) {
|
|
33
|
-
let isSecurityKey = false;
|
|
34
|
-
let registrationOptions = context.platformKeyCredentialCreationOptions();
|
|
35
|
-
if (!registrationOptions) {
|
|
36
|
-
registrationOptions =
|
|
37
|
-
context.securityKeyCredentialCreationOptions();
|
|
38
|
-
isSecurityKey = true;
|
|
39
|
-
}
|
|
40
|
-
const [clientDataHash, pubKeyCredParams, residentKeyRequired, excludeCredentials,] = createControllerState.get(selfPointer);
|
|
41
|
-
registrationOptions.setClientDataHash$(NSDataFromBuffer(clientDataHash));
|
|
42
|
-
registrationOptions.setChallenge$(null);
|
|
43
|
-
const supportedAlgos = [];
|
|
44
|
-
for (const param of pubKeyCredParams) {
|
|
45
|
-
if (param.type === "public-key") {
|
|
46
|
-
const nsNum = NSNumber.numberWithInteger$(param.algorithm);
|
|
47
|
-
supportedAlgos.push(nsNum);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
if (supportedAlgos.length > 0) {
|
|
51
|
-
registrationOptions.setSupportedAlgorithmIdentifiers$(NSArrayFromObjects(supportedAlgos));
|
|
52
|
-
}
|
|
53
|
-
if (!isSecurityKey) {
|
|
54
|
-
registrationOptions.setShouldRequireResidentKey$(residentKeyRequired);
|
|
55
|
-
}
|
|
56
|
-
const excludeList = [];
|
|
57
|
-
for (const cred of excludeCredentials) {
|
|
58
|
-
const transports = [];
|
|
59
|
-
if (cred.transports) {
|
|
60
|
-
for (const transport of cred.transports) {
|
|
61
|
-
transports.push(NSStringFromString(transport));
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
const credentialID = NSDataFromBuffer(cred.id);
|
|
65
|
-
const transportsArray = NSArrayFromObjects(transports);
|
|
66
|
-
const initializedDescriptor = ASCPublicKeyCredentialDescriptor.alloc().initWithCredentialID$transports$(credentialID, transportsArray);
|
|
67
|
-
excludeList.push(initializedDescriptor);
|
|
68
|
-
}
|
|
69
|
-
if (excludeList.length > 0) {
|
|
70
|
-
registrationOptions.setExcludedCredentials$(NSArrayFromObjects(excludeList));
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return context;
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
});
|
package/dist/create/handler.d.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
export type CreateCredentialErrorCodes = "TypeError" | "AbortError" | "NotAllowedError" | "SecurityError" | "InvalidStateError";
|
|
2
|
-
export interface CreateCredentialSuccessData {
|
|
3
|
-
credentialId: string;
|
|
4
|
-
clientDataJSON: string;
|
|
5
|
-
attestationObject: string;
|
|
6
|
-
authData: string;
|
|
7
|
-
publicKey: string;
|
|
8
|
-
publicKeyAlgorithm: number;
|
|
9
|
-
transports: string[];
|
|
10
|
-
extensions: {
|
|
11
|
-
credProps?: {
|
|
12
|
-
rk: boolean;
|
|
13
|
-
};
|
|
14
|
-
prf?: {
|
|
15
|
-
enabled?: boolean;
|
|
16
|
-
results: {
|
|
17
|
-
first?: string;
|
|
18
|
-
second?: string;
|
|
19
|
-
};
|
|
20
|
-
};
|
|
21
|
-
largeBlob?: {
|
|
22
|
-
supported?: boolean;
|
|
23
|
-
};
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
interface WebauthnCreateRequestOptions {
|
|
27
|
-
currentOrigin: string;
|
|
28
|
-
topFrameOrigin: string | undefined;
|
|
29
|
-
isPublicSuffix?: (domain: string) => boolean;
|
|
30
|
-
nativeWindowHandle: Buffer;
|
|
31
|
-
}
|
|
32
|
-
interface CreateCredentialSuccessResult {
|
|
33
|
-
success: true;
|
|
34
|
-
data: CreateCredentialSuccessData;
|
|
35
|
-
}
|
|
36
|
-
interface CreateCredentialErrorResult {
|
|
37
|
-
success: false;
|
|
38
|
-
error: CreateCredentialErrorCodes;
|
|
39
|
-
errorObject?: Error;
|
|
40
|
-
}
|
|
41
|
-
export type CreateCredentialResult = CreateCredentialSuccessResult | CreateCredentialErrorResult;
|
|
42
|
-
export declare function createCredential(publicKeyOptions: PublicKeyCredentialCreationOptions | undefined, additionalOptions: WebauthnCreateRequestOptions): Promise<CreateCredentialResult>;
|
|
43
|
-
export {};
|
package/dist/create/handler.js
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import { bufferSourceToBuffer, bufferToBase64Url } from "../helpers/index.js";
|
|
2
|
-
import { isRpIdAllowedForOrigin } from "../helpers/rpid.js";
|
|
3
|
-
import { isNumber, isObject, isString } from "../helpers/validation.js";
|
|
4
|
-
import { createCredentialInternal, } from "./internal-handler.js";
|
|
5
|
-
function getExtensionsConfiguration(extensionsData) {
|
|
6
|
-
if (!(extensionsData && typeof extensionsData === "object")) {
|
|
7
|
-
return {
|
|
8
|
-
extensions: [],
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
const extensions = [];
|
|
12
|
-
let largeBlobSupport;
|
|
13
|
-
if (isObject(extensionsData.largeBlob)) {
|
|
14
|
-
extensions.push("largeBlob");
|
|
15
|
-
const largeBlobConfig = extensionsData.largeBlob;
|
|
16
|
-
if (largeBlobConfig.support === "required") {
|
|
17
|
-
largeBlobSupport = "required";
|
|
18
|
-
}
|
|
19
|
-
else if (largeBlobConfig.support === "preferred") {
|
|
20
|
-
largeBlobSupport = "preferred";
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
let prf;
|
|
24
|
-
if (isObject(extensionsData.prf)) {
|
|
25
|
-
const prfEval = extensionsData.prf.eval;
|
|
26
|
-
if (prfEval) {
|
|
27
|
-
const first = bufferSourceToBuffer(prfEval.first);
|
|
28
|
-
const second = bufferSourceToBuffer(prfEval.second);
|
|
29
|
-
if (first) {
|
|
30
|
-
prf = {
|
|
31
|
-
first: first ? first : null,
|
|
32
|
-
second: second ? second : undefined,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
console.warn("[electron-webauthn] prf is enabled but prf.first is not valid, skipping PRF evaluation");
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return {
|
|
41
|
-
extensions,
|
|
42
|
-
largeBlobSupport,
|
|
43
|
-
prf,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
export async function createCredential(publicKeyOptions, additionalOptions) {
|
|
47
|
-
if (!publicKeyOptions) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
const rpInfo = publicKeyOptions.rp;
|
|
51
|
-
if (!isObject(rpInfo)) {
|
|
52
|
-
return { success: false, error: "TypeError" };
|
|
53
|
-
}
|
|
54
|
-
let rpId = rpInfo.id;
|
|
55
|
-
if (!rpId) {
|
|
56
|
-
try {
|
|
57
|
-
const url = new URL(additionalOptions.currentOrigin);
|
|
58
|
-
rpId = url.hostname;
|
|
59
|
-
}
|
|
60
|
-
catch { }
|
|
61
|
-
}
|
|
62
|
-
if (!isString(rpId)) {
|
|
63
|
-
return { success: false, error: "TypeError" };
|
|
64
|
-
}
|
|
65
|
-
let timeout = publicKeyOptions.timeout;
|
|
66
|
-
if (!isNumber(timeout) || timeout <= 0) {
|
|
67
|
-
timeout = 10 * 60 * 1000;
|
|
68
|
-
}
|
|
69
|
-
else if (timeout > 60 * 60 * 1000) {
|
|
70
|
-
timeout = 60 * 60 * 1000;
|
|
71
|
-
}
|
|
72
|
-
const challenge = bufferSourceToBuffer(publicKeyOptions.challenge);
|
|
73
|
-
if (!challenge) {
|
|
74
|
-
return { success: false, error: "TypeError" };
|
|
75
|
-
}
|
|
76
|
-
if (!isObject(publicKeyOptions.user)) {
|
|
77
|
-
return { success: false, error: "TypeError" };
|
|
78
|
-
}
|
|
79
|
-
const userName = publicKeyOptions.user.name;
|
|
80
|
-
const userDisplayName = publicKeyOptions.user.displayName;
|
|
81
|
-
if (!isString(userName) || !isString(userDisplayName)) {
|
|
82
|
-
return { success: false, error: "TypeError" };
|
|
83
|
-
}
|
|
84
|
-
const userID = bufferSourceToBuffer(publicKeyOptions.user.id);
|
|
85
|
-
if (!userID) {
|
|
86
|
-
return { success: false, error: "TypeError" };
|
|
87
|
-
}
|
|
88
|
-
const attestationPreference = publicKeyOptions.attestation;
|
|
89
|
-
if (attestationPreference && !isString(attestationPreference)) {
|
|
90
|
-
return { success: false, error: "TypeError" };
|
|
91
|
-
}
|
|
92
|
-
const pubKeyCredParams = publicKeyOptions.pubKeyCredParams;
|
|
93
|
-
const supportedAlgorithmIdentifiers = [];
|
|
94
|
-
if (pubKeyCredParams) {
|
|
95
|
-
if (Array.isArray(pubKeyCredParams)) {
|
|
96
|
-
for (const param of pubKeyCredParams) {
|
|
97
|
-
if (!isObject(param))
|
|
98
|
-
continue;
|
|
99
|
-
if (!isNumber(param.alg))
|
|
100
|
-
continue;
|
|
101
|
-
supportedAlgorithmIdentifiers.push({
|
|
102
|
-
type: "public-key",
|
|
103
|
-
algorithm: param.alg,
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
return { success: false, error: "TypeError" };
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
if (supportedAlgorithmIdentifiers.length === 0) {
|
|
112
|
-
supportedAlgorithmIdentifiers.push({
|
|
113
|
-
type: "public-key",
|
|
114
|
-
algorithm: -7,
|
|
115
|
-
});
|
|
116
|
-
supportedAlgorithmIdentifiers.push({
|
|
117
|
-
type: "public-key",
|
|
118
|
-
algorithm: -257,
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
const excludeCredentials = [];
|
|
122
|
-
if (publicKeyOptions.excludeCredentials &&
|
|
123
|
-
Array.isArray(publicKeyOptions.excludeCredentials)) {
|
|
124
|
-
for (const excludeCredential of publicKeyOptions.excludeCredentials) {
|
|
125
|
-
if (!isObject(excludeCredential))
|
|
126
|
-
continue;
|
|
127
|
-
if (excludeCredential.type !== "public-key")
|
|
128
|
-
continue;
|
|
129
|
-
const idBuffer = bufferSourceToBuffer(excludeCredential.id);
|
|
130
|
-
if (!idBuffer)
|
|
131
|
-
continue;
|
|
132
|
-
excludeCredentials.push({
|
|
133
|
-
id: idBuffer,
|
|
134
|
-
transports: excludeCredential.transports,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
const { extensions, largeBlobSupport, prf } = getExtensionsConfiguration(publicKeyOptions.extensions);
|
|
139
|
-
let residentKeyRequired = false;
|
|
140
|
-
let userVerificationPreference = "preferred";
|
|
141
|
-
let preferredAuthenticatorAttachment = "all";
|
|
142
|
-
if (publicKeyOptions.authenticatorSelection) {
|
|
143
|
-
if (publicKeyOptions.authenticatorSelection.residentKey === "required") {
|
|
144
|
-
residentKeyRequired = true;
|
|
145
|
-
}
|
|
146
|
-
else if (publicKeyOptions.authenticatorSelection.requireResidentKey) {
|
|
147
|
-
residentKeyRequired = true;
|
|
148
|
-
}
|
|
149
|
-
const userVerifyParam = publicKeyOptions.authenticatorSelection.userVerification;
|
|
150
|
-
if (userVerifyParam === "required") {
|
|
151
|
-
userVerificationPreference = "required";
|
|
152
|
-
}
|
|
153
|
-
else if (userVerifyParam === "discouraged") {
|
|
154
|
-
userVerificationPreference = "discouraged";
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
userVerificationPreference = "preferred";
|
|
158
|
-
}
|
|
159
|
-
const attachment = publicKeyOptions.authenticatorSelection.authenticatorAttachment;
|
|
160
|
-
if (attachment === "cross-platform") {
|
|
161
|
-
preferredAuthenticatorAttachment = "cross-platform";
|
|
162
|
-
}
|
|
163
|
-
else if (attachment === "platform") {
|
|
164
|
-
preferredAuthenticatorAttachment = "platform";
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
const { currentOrigin, topFrameOrigin, isPublicSuffix, nativeWindowHandle } = additionalOptions;
|
|
168
|
-
const isRpIdAllowed = isRpIdAllowedForOrigin(currentOrigin, rpId, {
|
|
169
|
-
isPublicSuffix,
|
|
170
|
-
});
|
|
171
|
-
if (!isRpIdAllowed.ok) {
|
|
172
|
-
return { success: false, error: "NotAllowedError" };
|
|
173
|
-
}
|
|
174
|
-
let errorResult = null;
|
|
175
|
-
const result = await createCredentialInternal(rpId, challenge, userName, userID, nativeWindowHandle, currentOrigin, timeout, extensions, attestationPreference, supportedAlgorithmIdentifiers, excludeCredentials, residentKeyRequired, preferredAuthenticatorAttachment, userVerificationPreference, {
|
|
176
|
-
topFrameOrigin,
|
|
177
|
-
largeBlobSupport,
|
|
178
|
-
prf,
|
|
179
|
-
}).catch((error) => {
|
|
180
|
-
errorResult = error;
|
|
181
|
-
if (error.message.includes("(com.apple.AuthenticationServices.AuthorizationError error 1006.)")) {
|
|
182
|
-
return "InvalidStateError";
|
|
183
|
-
}
|
|
184
|
-
if (error.message.startsWith("The operation couldn’t be completed.")) {
|
|
185
|
-
return "NotAllowedError";
|
|
186
|
-
}
|
|
187
|
-
return null;
|
|
188
|
-
});
|
|
189
|
-
if (typeof result === "string") {
|
|
190
|
-
return { success: false, error: result, errorObject: errorResult };
|
|
191
|
-
}
|
|
192
|
-
const data = {
|
|
193
|
-
credentialId: bufferToBase64Url(result.credentialId),
|
|
194
|
-
clientDataJSON: bufferToBase64Url(result.clientDataJSON),
|
|
195
|
-
attestationObject: bufferToBase64Url(result.attestationObject),
|
|
196
|
-
authData: bufferToBase64Url(result.authenticatorData),
|
|
197
|
-
publicKey: bufferToBase64Url(result.publicKey),
|
|
198
|
-
publicKeyAlgorithm: result.publicKeyAlgorithm,
|
|
199
|
-
transports: result.transports,
|
|
200
|
-
extensions: {},
|
|
201
|
-
};
|
|
202
|
-
if (publicKeyOptions.extensions?.credProps) {
|
|
203
|
-
data.extensions.credProps = {
|
|
204
|
-
rk: result.isResidentKey,
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
if (result.isLargeBlobSupported !== null) {
|
|
208
|
-
data.extensions.largeBlob = {
|
|
209
|
-
supported: result.isLargeBlobSupported,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
if (result.isPRFSupported !== null) {
|
|
213
|
-
const prfFirst = result.prfFirst;
|
|
214
|
-
const prfSecond = result.prfSecond;
|
|
215
|
-
data.extensions.prf = {
|
|
216
|
-
enabled: result.isPRFSupported,
|
|
217
|
-
results: {
|
|
218
|
-
first: prfFirst ? bufferToBase64Url(prfFirst) : undefined,
|
|
219
|
-
second: prfSecond ? bufferToBase64Url(prfSecond) : undefined,
|
|
220
|
-
},
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
return { success: true, data };
|
|
224
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { type PRFInput } from "../helpers/prf.js";
|
|
2
|
-
import { type PublicKeyCredentialParams } from "./authorization-controller.js";
|
|
3
|
-
export type AuthenticatorAttachmentWithExtra = AuthenticatorAttachment | "all";
|
|
4
|
-
export interface CreateCredentialResult {
|
|
5
|
-
credentialId: Buffer;
|
|
6
|
-
clientDataJSON: Buffer;
|
|
7
|
-
attestationObject: Buffer;
|
|
8
|
-
authenticatorData: Buffer;
|
|
9
|
-
attachment: AuthenticatorAttachment;
|
|
10
|
-
transports: string[];
|
|
11
|
-
isResidentKey: boolean;
|
|
12
|
-
publicKeyAlgorithm: number;
|
|
13
|
-
publicKey: Buffer;
|
|
14
|
-
isLargeBlobSupported: boolean | null;
|
|
15
|
-
isPRFSupported: boolean | null;
|
|
16
|
-
prfFirst: Buffer | null;
|
|
17
|
-
prfSecond: Buffer | null;
|
|
18
|
-
}
|
|
19
|
-
type CredentialUserVerificationPreference = "required" | "preferred" | "discouraged";
|
|
20
|
-
type CredentialAttestationPreference = "direct" | "enterprise" | "indirect" | "none";
|
|
21
|
-
declare const VALID_EXTENSIONS: readonly ["largeBlob", "prf"];
|
|
22
|
-
export type CredentialCreationExtensions = (typeof VALID_EXTENSIONS)[number];
|
|
23
|
-
export type LargeBlobSupport = "required" | "preferred" | "unspecified";
|
|
24
|
-
interface CreateCredentialAdditionalOptions {
|
|
25
|
-
topFrameOrigin?: string;
|
|
26
|
-
userDisplayName?: string;
|
|
27
|
-
largeBlobSupport?: LargeBlobSupport;
|
|
28
|
-
prf?: PRFInput;
|
|
29
|
-
}
|
|
30
|
-
export interface ExcludeCredential {
|
|
31
|
-
id: Buffer;
|
|
32
|
-
transports?: string[];
|
|
33
|
-
}
|
|
34
|
-
declare function createCredentialInternal(rpid: string, challenge: Buffer, username: string, userID: Buffer, nativeWindowHandle: Buffer, origin: string, timeout: number, enabledExtensions: CredentialCreationExtensions[], attestation: CredentialAttestationPreference, supportedAlgorithmIdentifiers: PublicKeyCredentialParams[], excludeCredentials: ExcludeCredential[], residentKeyRequired?: boolean, preferredAuthenticatorAttachment?: AuthenticatorAttachmentWithExtra, userVerification?: CredentialUserVerificationPreference, additionalOptions?: CreateCredentialAdditionalOptions): Promise<CreateCredentialResult>;
|
|
35
|
-
export { createCredentialInternal };
|