/* Any copyright is dedicated to the Public Domain. https://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; let bounceTrackingProtection = Cc[ "@mozilla.org/bounce-tracking-protection;1" ].getService(Ci.nsIBounceTrackingProtection); // Import helpers from webauthn tests. Services.scriptloader.loadSubScript( "chrome://mochitests/content/browser/dom/webauthn/tests/browser/head.js", this ); // Variant of promiseWebAuthnGetAssertionDiscoverable from head.js which can // target a BrowsingContext. This is needed for running the test code in an // iframe. function promiseWebAuthnGetAssertionDiscoverableBC( target, mediation = "optional", extensions = {} ) { return SpecialPowers.spawn( target, [extensions, mediation], async (extensions, mediation) => { let challenge = content.crypto.getRandomValues(new Uint8Array(16)); let publicKey = { challenge, extensions, rpId: content.document.domain, allowCredentials: [], }; // Can't return the promise directly, that results in a structured clone // error. That's fine because this test doesn't care about the result. It // only cares whether the request was successful or whether any error was // thrown. await content.navigator.credentials.get({ publicKey, mediation }); } ); } /** * This file tests that webauthn request triggers BTP user activation. */ /** * Test that a webauthn request triggers BTP user activation. * * @param {Function} triggerFn - Function that runs the webauthn request. * @param {string} userActivationSiteHost - The expected user activation site host in BTP. */ async function runWebAuthTest(triggerFn, userActivationSiteHost) { is( bounceTrackingProtection.testGetUserActivationHosts({}).length, 0, "No user activation hosts initially." ); await triggerFn(); let userActivationHosts = bounceTrackingProtection.testGetUserActivationHosts( {} ); is( userActivationHosts.length, 1, "One user activation host after webauth request." ); is( userActivationHosts[0].siteHost, userActivationSiteHost, `User activation host is ${userActivationSiteHost}.` ); bounceTrackingProtection.clearAll(); } /** * Wrapper around runWebAuthTest for the case where the webauthn request is made * in an iframe. * * @param {string} topLevelSite - The top level site where the iframe is inserted. * @param {string} iframeSite - The site of the iframe. */ async function runWebAuthTestIframe(topLevelSite, iframeSite) { await runWebAuthTest(() => { return BrowserTestUtils.withNewTab( `https://${topLevelSite}`, async browser => { info( `Inserting an iframe with target ${iframeSite} under top level ${topLevelSite}.` ); let iframeBC = await SpecialPowers.spawn( browser, [iframeSite], async iframeSite => { let iframe = content.document.createElement("iframe"); let loadedPromise = ContentTaskUtils.waitForEvent(iframe, "load"); iframe.src = `https://${iframeSite}`; iframe.allow = "publickey-credentials-get *"; content.document.body.appendChild(iframe); await loadedPromise; return iframe.browsingContext; } ); info( "Request assertion. Using the virtual authenticator means we do not get a prompt." ); await promiseWebAuthnGetAssertionDiscoverableBC(iframeBC).catch( arrivingHereIsBad ); } ); }, topLevelSite); } add_setup(async function () { bounceTrackingProtection.clearAll(); await SpecialPowers.pushPrefEnv({ set: [ [ "privacy.bounceTrackingProtection.mode", Ci.nsIBounceTrackingProtection.MODE_ENABLED, ], ["security.webauth.webauthn_enable_softtoken", true], ["security.webauth.webauthn_enable_usbtoken", false], ], }); let authenticatorId = add_virtual_authenticator(); await addCredential(authenticatorId, "example.com"); await addCredential(authenticatorId, "example.org"); }); add_task(async function test_web_auth_triggers_btp_user_activation() { await runWebAuthTest(() => { return BrowserTestUtils.withNewTab("https://example.com", async browser => { info( "Request assertion. Using the virtual authenticator means we do not get a prompt." ); await promiseWebAuthnGetAssertionDiscoverable( gBrowser.getTabForBrowser(browser) ).catch(arrivingHereIsBad); }); }, "example.com"); }); add_task( async function test_web_auth_triggers_btp_user_activation_iframe_cross_site() { await runWebAuthTestIframe("example.org", "example.com"); } ); add_task( async function test_web_auth_triggers_btp_user_activation_iframe_same_site() { await runWebAuthTestIframe("example.org", "example.org"); } );