UNPKG

@turnkey/core

Version:

A core JavaScript web and React Native package for interfacing with Turnkey's infrastructure.

139 lines (134 loc) 6.49 kB
'use strict'; var utils = require('../../utils.js'); var webauthnStamper = require('@turnkey/webauthn-stamper'); var encoding = require('@turnkey/encoding'); var http = require('@turnkey/http'); var uuid = require('uuid'); function _interopNamespaceDefaultOnly (e) { return Object.freeze({ __proto__: null, default: e }); } let PasskeyStamperModule; class CrossPlatformPasskeyStamper { constructor(config) { /** * Create a passkey for an end-user, taking care of various lower-level details. * * @returns {Promise<Passkey>} */ this.createWebPasskey = async (config = {}) => { const challenge = utils.generateRandomBuffer(); const encodedChallenge = encoding.base64StringToBase64UrlEncodedString(btoa(String.fromCharCode(...new Uint8Array(challenge)))); const authenticatorUserId = utils.generateRandomBuffer(); // WebAuthn credential options options can be found here: // https://www.w3.org/TR/webauthn-2/#sctn-sample-registration // // All pubkey algorithms can be found here: https://www.iana.org/assignments/cose/cose.xhtml#algorithms // Turnkey only supports ES256 (-7) and RS256 (-257) // // The pubkey type only supports one value, "public-key" // See https://www.w3.org/TR/webauthn-2/#enumdef-publickeycredentialtype for more details // TODO: consider un-nesting these config params const webauthnConfig = { publicKey: { rp: { id: config.publicKey?.rp?.id ?? this.config.rpId, name: config.publicKey?.rp?.name ?? "", }, challenge: config.publicKey?.challenge ?? challenge, pubKeyCredParams: config.publicKey?.pubKeyCredParams ?? [ { type: "public-key", alg: -7, }, { type: "public-key", alg: -257, }, ], user: { id: config.publicKey?.user?.id ?? authenticatorUserId, name: config.publicKey?.user?.name ?? "Default User", displayName: config.publicKey?.user?.displayName ?? "Default User", }, authenticatorSelection: { authenticatorAttachment: config.publicKey?.authenticatorSelection?.authenticatorAttachment ?? undefined, // default to empty requireResidentKey: config.publicKey?.authenticatorSelection?.requireResidentKey ?? true, residentKey: config.publicKey?.authenticatorSelection?.residentKey ?? "required", userVerification: config.publicKey?.authenticatorSelection?.userVerification ?? "preferred", }, }, }; const attestation = await http.getWebAuthnAttestation(webauthnConfig); return { encodedChallenge: config.publicKey?.challenge ? encoding.base64StringToBase64UrlEncodedString(config.publicKey?.challenge) : encodedChallenge, attestation, }; }; this.createReactNativePasskey = async (config = {}) => { const { name, displayName, authenticatorName } = config; const { createPasskey } = PasskeyStamperModule; // We do a 'selective' import when initializing the stamper. This is safe to do here. if (!createPasskey) { throw new Error("Ensure you have @turnkey/react-native-passkey-stamper installed and linked correctly. Are you not on React Native?"); } return await createPasskey({ rp: { id: this.config.rpId, name: this.config.rpName ?? "Turnkey", }, user: { id: uuid.v4(), name, displayName, }, authenticatorName: authenticatorName ?? name ?? "End-User Passkey", }); }; // Use init method to set up the stamper based on the platform. It's async, so can't be done in the constructor. this.config = config; } async init() { if (utils.isWeb()) { const { default: WindowWrapper } = await Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefaultOnly(require('../../__polyfills__/window.js')); }); this.stamper = new webauthnStamper.WebauthnStamper({ ...this.config, rpId: this.config.rpId ?? WindowWrapper.location.hostname, }); } else if (utils.isReactNative()) { try { // Dynamic import to prevent bundling the native module in web environments. let PasskeyStamper; try { PasskeyStamperModule = require("@turnkey/react-native-passkey-stamper"); PasskeyStamper = PasskeyStamperModule.PasskeyStamper; } catch { throw new Error("Please install react-native-passkeys and @turnkey/react-native-passkey-stamper in your app to use passkeys."); } this.stamper = new PasskeyStamper({ ...this.config, rpId: this.config.rpId, allowCredentials: this.config.allowCredentials?.map((cred) => ({ id: encoding.uint8ArrayToHexString(cred.id), type: cred.type, transports: cred.transports, })), }); } catch (error) { throw new Error(`Failed to load passkey stamper for react-native: ${error}`); } } else { throw new Error("Unsupported platform for passkey stamper"); } } async stamp(payload) { return await this.stamper.stamp(payload); } } exports.CrossPlatformPasskeyStamper = CrossPlatformPasskeyStamper; //# sourceMappingURL=base.js.map