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