UNPKG

@rize-labs/paas

Version:

Passkey management module for all your passkey requirements

171 lines 7.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.signMessageViaPassKeys = exports.checkAuth = exports.register = void 0; const base64url = require("./utils/base64url-arraybuffer"); const uuid_1 = require("uuid"); const axios_1 = require("axios"); const routes_1 = require("./constants/routes"); const ethers_1 = require("ethers"); const EllipticCurve__factory_1 = require("./typechain/factories/EllipticCurve__factory"); const index_1 = require("./constants/index"); /** * Registers a new Passkey by creating a public key credential. * @returns public keys (x,y) co-ordinate and encodedId in a json. */ const register = async () => { const uuid = (0, uuid_1.v4)(); const chanllenge = (0, uuid_1.v4)(); const isPlatformSupported = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(); const authenticationSupport = isPlatformSupported ? "platform" : "cross-platform"; const publicKeyParams = { challenge: Uint8Array.from(chanllenge, (c) => c.charCodeAt(0)), rp: { name: "Banana Passkey Signer", }, user: { id: Uint8Array.from(uuid, (c) => c.charCodeAt(0)), name: "passkey-signer", displayName: "Banana SDK", }, pubKeyCredParams: [{ type: "public-key", alg: -7 }], authenticatorSelection: { authenticatorAttachment: authenticationSupport, userVerification: "required", }, timeout: 60000, attestation: "none", }; let publicKeyCredential; try { publicKeyCredential = await navigator.credentials.create({ publicKey: publicKeyParams, }); } catch (err) { console.log("algo not supported, trying again", err); // @ts-ignore publicKeyParams.authenticatorSelection.authenticatorAttachment = "cross-platform"; publicKeyCredential = await navigator.credentials.create({ publicKey: publicKeyParams, }); } if (publicKeyCredential === null) { // alert('Failed to get credential') return Promise.reject(new Error("Failed to create credential")); } const response = await (0, axios_1.default)({ url: routes_1.REGISTRATION_LAMBDA_URL, method: "post", params: { aObject: JSON.stringify(Array.from(new Uint8Array(publicKeyCredential.response.attestationObject))), rawId: JSON.stringify( //@ts-ignore Array.from(new Uint8Array(publicKeyCredential?.rawId))), }, }); return { q0: response.data.message.q0hexString, q1: response.data.message.q1hexString, encodedId: response.data.message.encodedId, }; }; exports.register = register; /** * Checks the authentication of a user by signing a message with Passkeys and verifying the signature. * @param message - The message to be signed. * @param encodedId - The encoded ID associated with the Passkeys. * @param eoaAddress - Public Key with (x,y) co-ordinates of the point on curve * @returns boolean value indicating whether the signature is valid or not. */ const checkAuth = async (message, encodedId, eoaAddress) => { const { signature, messageSigned } = await (0, exports.signMessageViaPassKeys)({ message, encodedId, isMessageSignedNeeded: true }); const resp = await verifySignature(messageSigned, signature, eoaAddress); return resp; }; exports.checkAuth = checkAuth; /** * Checks that the signature is valid for the given message and EOA public key. * @param messageSigned text message signed by the user along with the random salt added by webauthn * @param signature signature of the message * @param eoaAddress Public Key with (x,y) co-ordinates of the point on curve * @returns */ const verifySignature = async (messageSigned, signature, eoaAddress) => { const rValue = ethers_1.ethers.BigNumber.from("0x" + signature.slice(2, 66)); const sValue = ethers_1.ethers.BigNumber.from("0x" + signature.slice(66, 132)); const ellipticCurve = EllipticCurve__factory_1.EllipticCurve__factory.connect(index_1.ELLIPTIC_CURVE_ADDRESS, new ethers_1.ethers.providers.JsonRpcProvider(index_1.JSON_RPC_PROVIDER)); const isVerified = await ellipticCurve.validateSignature(messageSigned, [rValue, sValue], eoaAddress); return isVerified; }; /** * Signs a text using passkeys * @param message - text message to be signed * @param encodedId - identifier for passkeys * @param isMessageSignedNeeded - flag indicating if the signed message is needed. * @returns signature and signed message depending on the value of isMessageSignedNeeded . */ const signMessageViaPassKeys = async ({ message, encodedId, isMessageSignedNeeded }) => { const decodedId = base64url.decode(encodedId); const credential = await navigator.credentials.get({ publicKey: { allowCredentials: [ { id: decodedId, type: "public-key", }, ], challenge: Uint8Array.from(message, (c) => c.charCodeAt(0)).buffer, // Set the required authentication factors userVerification: "required", }, }); if (credential === null) { // alert('Failed to get credential') return Promise.reject(new Error("Failed to get credential")); } //@ts-ignore const response = credential.response; const clientDataJSON = Buffer.from(response.clientDataJSON); let signatureValid = false; let signature; while (!signatureValid) { signature = await (0, axios_1.default)({ url: routes_1.VERIFICATION_LAMBDA_URL, method: "post", params: { authDataRaw: JSON.stringify(Array.from(new Uint8Array(response.authenticatorData))), cData: JSON.stringify(Array.from(new Uint8Array(response.clientDataJSON))), signature: JSON.stringify(Array.from(new Uint8Array(response.signature))), }, }); if (signature.data.message.processStatus === "success") { signatureValid = true; } } const value = clientDataJSON.toString("hex").slice(72, 248); const clientDataJsonRequestId = ethers_1.ethers.utils.keccak256("0x" + value); //@ts-ignore const finalSignatureWithMessage = signature.data.message.finalSignature + clientDataJsonRequestId.slice(2); const abi = ethers_1.ethers.utils.defaultAbiCoder; const decoded = abi.decode(["uint256", "uint256", "uint256"], finalSignatureWithMessage); const signedMessage = decoded[2]; if (isMessageSignedNeeded) { const rHex = decoded[0].toHexString(); const sHex = decoded[1].toHexString(); const finalSignature = rHex + sHex.slice(2); return { //! signature signature: finalSignature, messageSigned: signedMessage }; } return { //! signature + message signature: finalSignatureWithMessage, }; }; exports.signMessageViaPassKeys = signMessageViaPassKeys; //# sourceMappingURL=WebAuthn.js.map