@rize-labs/paas
Version:
Passkey management module for all your passkey requirements
171 lines • 7.2 kB
JavaScript
;
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