/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const TEST_URL = "https://example.com/browser/dom/u2f/tests/browser/tab_u2f_result.html"; async function assertStatus(tab, expected) { let actual = await SpecialPowers.spawn( tab.linkedBrowser, [], async function() { return content.document.getElementById("status").value; } ); is(actual, expected, "u2f request " + expected); } async function waitForStatus(tab, expected) { /* eslint-disable no-shadow */ await SpecialPowers.spawn(tab.linkedBrowser, [[expected]], async function( expected ) { return ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("status").value == expected; }); }); /* eslint-enable no-shadow */ await assertStatus(tab, expected); } function startMakeCredentialRequest(tab) { let challenge = crypto.getRandomValues(new Uint8Array(16)); challenge = bytesToBase64UrlSafe(challenge); /* eslint-disable no-shadow */ return SpecialPowers.spawn(tab.linkedBrowser, [[challenge]], async function([ challenge, ]) { let appId = content.location.origin; let request = { version: "U2F_V2", challenge }; let status = content.document.getElementById("status"); content.u2f.register(appId, [request], [], result => { status.value = result.errorCode ? "aborted" : completed; }); status.value = "pending"; }); /* eslint-enable no-shadow */ } function startGetAssertionRequest(tab) { let challenge = crypto.getRandomValues(new Uint8Array(16)); challenge = bytesToBase64UrlSafe(challenge); let keyHandle = crypto.getRandomValues(new Uint8Array(16)); keyHandle = bytesToBase64UrlSafe(keyHandle); /* eslint-disable no-shadow */ return SpecialPowers.spawn( tab.linkedBrowser, [[challenge, keyHandle]], async function([challenge, keyHandle]) { let appId = content.location.origin; let key = { version: "U2F_V2", keyHandle }; let status = content.document.getElementById("status"); content.u2f.sign(appId, challenge, [key], result => { status.value = result.errorCode ? "aborted" : completed; }); status.value = "pending"; } ); /* eslint-enable no-shadow */ } // Test that MakeCredential() and GetAssertion() requests // are aborted when the current tab loses its focus. add_task(async function test_abort() { // Enable the USB token. Services.prefs.setBoolPref("security.webauth.u2f", true); Services.prefs.setBoolPref( "security.webauth.webauthn_enable_softtoken", false ); Services.prefs.setBoolPref("security.webauth.webauthn_enable_usbtoken", true); Services.prefs.setBoolPref( "security.webauth.webauthn_enable_android_fido2", false ); // Create a new tab for the MakeCredential() request. let tab_create = await BrowserTestUtils.openNewForegroundTab( gBrowser, TEST_URL ); // Start the request. await startMakeCredentialRequest(tab_create); await assertStatus(tab_create, "pending"); // Open another tab and switch to it. The first will lose focus. let tab_get = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); // Start a GetAssertion() request in the second tab. That will abort the first. await startGetAssertionRequest(tab_get); await waitForStatus(tab_get, "pending"); await waitForStatus(tab_create, "aborted"); // Start a second request in the second tab. It should remain pending. await startGetAssertionRequest(tab_get); await waitForStatus(tab_get, "pending"); // Switch back to the first tab. The second should still be pending await BrowserTestUtils.switchTab(gBrowser, tab_create); await assertStatus(tab_get, "pending"); // Switch back to the get tab. The second should remain pending await BrowserTestUtils.switchTab(gBrowser, tab_get); await assertStatus(tab_get, "pending"); // Close tabs. BrowserTestUtils.removeTab(tab_create); BrowserTestUtils.removeTab(tab_get); // Cleanup. Services.prefs.clearUserPref("security.webauth.u2f"); Services.prefs.clearUserPref("security.webauth.webauthn_enable_softtoken"); Services.prefs.clearUserPref("security.webauth.webauthn_enable_usbtoken"); });