@coinbase/onchaintestkit
Version:
End-to-end testing toolkit for blockchain applications, powered by Playwright
124 lines (123 loc) • 5.03 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PasskeyAuthenticator = void 0;
class PasskeyAuthenticator {
constructor(page) {
this.isInitialized = false;
this.page = page;
}
/**
* Set a new Playwright Page for this authenticator and re-create the CDPSession.
* This allows reusing the authenticator across different popups/pages.
*/
async setPage(page) {
this.page = page;
// Re-create the CDPSession for the new page
this.cdpSession = await this.page.context().newCDPSession(this.page);
await this.cdpSession.send("WebAuthn.enable");
// Note: isInitialized and authenticatorId are NOT reset here.
// You may want to call initialize() again if you need a new authenticator for the new page.
}
async initialize(options = {}) {
this.cdpSession = await this.page.context().newCDPSession(this.page);
await this.cdpSession.send("WebAuthn.enable");
const defaultOptions = {
protocol: "ctap2",
transport: "usb",
hasResidentKey: true,
hasUserVerification: true,
isUserVerified: true,
automaticPresenceSimulation: true,
};
const finalOptions = { ...defaultOptions, ...options };
const result = await this.cdpSession.send("WebAuthn.addVirtualAuthenticator", { options: finalOptions });
this.authenticatorId = result.authenticatorId;
this.isInitialized = true;
}
async simulateSuccessfulPasskeyInput(operationTrigger) {
if (!this.isInitialized || !this.authenticatorId || !this.cdpSession) {
throw new Error("Authenticator not initialized");
}
const cdpSession = this.cdpSession;
const authenticatorId = this.authenticatorId;
await cdpSession.send("WebAuthn.setUserVerified", {
authenticatorId,
isUserVerified: true,
});
await cdpSession.send("WebAuthn.setAutomaticPresenceSimulation", {
authenticatorId,
enabled: true,
});
const eventPromise = new Promise((resolve, reject) => {
const addedHandler = (_event) => {
cdpSession.off("WebAuthn.credentialAdded", addedHandler);
cdpSession.off("WebAuthn.credentialAsserted", assertedHandler);
resolve();
};
const assertedHandler = (_event) => {
cdpSession.off("WebAuthn.credentialAdded", addedHandler);
cdpSession.off("WebAuthn.credentialAsserted", assertedHandler);
resolve();
};
cdpSession.on("WebAuthn.credentialAdded", addedHandler);
cdpSession.on("WebAuthn.credentialAsserted", assertedHandler);
setTimeout(async () => {
cdpSession.off("WebAuthn.credentialAdded", addedHandler);
cdpSession.off("WebAuthn.credentialAsserted", assertedHandler);
try {
await this.getCredentials();
}
catch (_err) { }
reject(new Error("WebAuthn operation timed out"));
}, 30000);
});
try {
await operationTrigger();
await eventPromise;
}
finally {
await cdpSession.send("WebAuthn.setAutomaticPresenceSimulation", {
authenticatorId,
enabled: false,
});
}
}
async getCredentials() {
if (!this.isInitialized || !this.authenticatorId || !this.cdpSession) {
throw new Error("Authenticator not initialized");
}
const cdpSession = this.cdpSession;
const authenticatorId = this.authenticatorId;
const result = await cdpSession.send("WebAuthn.getCredentials", {
authenticatorId,
});
return result.credentials.map((cred) => ({
credentialId: cred.credentialId,
isResidentCredential: cred.isResidentCredential,
rpId: cred.rpId || "",
privateKey: cred.privateKey,
userHandle: cred.userHandle || "",
signCount: cred.signCount,
}));
}
async exportCredentials() {
return this.getCredentials();
}
async importCredential(cred) {
if (!this.isInitialized || !this.authenticatorId || !this.cdpSession) {
throw new Error("Authenticator not initialized");
}
await this.cdpSession.send("WebAuthn.addCredential", {
authenticatorId: this.authenticatorId,
credential: {
credentialId: cred.credentialId,
isResidentCredential: cred.isResidentCredential,
rpId: cred.rpId,
privateKey: cred.privateKey,
userHandle: cred.userHandle,
signCount: cred.signCount,
},
});
}
}
exports.PasskeyAuthenticator = PasskeyAuthenticator;