Skip to content

Commit

Permalink
Reland "webauthn: Update abort handling to take an abort reason"
Browse files Browse the repository at this point in the history
This is a reland of 626cf945c1cb51b2b51cf145d5be7cdaf6e2879b.

Fix test failures in linux-bfcache-rel to pass isUserConsenting false
to fix a race between resolving it and aborting.

Original change's description:
> webauthn: Update abort handling to take an abort reason
>
> AbortController now has a `reason` [1] parameter which
> has been added to Web Authentication [2], Credential Management [3],
> and Web OTP [4]. We should update CredentialsContainer to take
> this `reason` to the promise then aborting a request.
>
> [1] https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/reason
> [2] w3c/webauthn#1706
> [3] w3c/webappsec-credential-management#196
> [4] WICG/web-otp#57
>
> Bug: 1272541, 1329938
> Change-Id: Idb9ef40d6a97ae240ae3a28892a426e4c0ffbfef
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3715820
> Commit-Queue: Euisang Lim <eui-sang.lim@samsung.com>
> Commit-Queue: Nina Satragno <nsatragno@chromium.org>
> Reviewed-by: Nina Satragno <nsatragno@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1039866}

Bug: 1272541, 1329938, 1357621
Change-Id: I3861d5c2440519592e0438e8f4df774372a7c0ba
  • Loading branch information
amoseui authored and chromium-wpt-export-bot committed Aug 31, 2022
1 parent 1fb26b6 commit 12c459f
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 1 deletion.
8 changes: 8 additions & 0 deletions credential-management/otpcredential-get-basics.https.html
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,12 @@
{otp: {transport: ["sms"]}, signal: signal}));
}, 'Should abort request');

promise_test(async t => {
const controller = new AbortController();
const signal = controller.signal;

controller.abort('CustomError');
await promise_rejects_exactly(t, 'CustomError', navigator.credentials.get(
{otp: {transport: ["sms"]}, signal: signal}));
}, 'Should abort request with reason');
</script>
13 changes: 13 additions & 0 deletions secure-payment-confirmation/enrollment.https.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@
.runTest('Payment credential does not allow residentKey to be "discouraged".', "NotSupportedError");
new CreatePaymentCredentialsTest({authenticatorAttachment: 'cross-platform'})
.runTest('Payment credential requires authenticatorAttachment to be "platform", not "cross-platform".', "NotSupportedError");

// abort creates
let abortController = new AbortController();
abortController.abort();
new CreatePaymentCredentialsTest()
.modify("options.signal", abortController.signal)
.runTest("Payment credential abort without reason", "AbortError");

abortController = new AbortController();
abortController.abort(new Error('error'));
new CreatePaymentCredentialsTest()
.modify("options.signal", abortController.signal)
.runTest("Payment credential abort reason with Error", Error);
}, {
protocol: 'ctap2_1',
transport: 'internal',
Expand Down
109 changes: 109 additions & 0 deletions webauthn/createcredential-abort.https.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>WebAuthn navigator.credentials.create() abort Tests</title>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src=helpers.js></script>
<body></body>
<script>
"use strict";

virtualAuthenticatorPromiseTest(async t => {
const abortController = new AbortController();
const signal = abortController.signal;
abortController.abort();
const promise = createCredential({
options: {
signal: signal,
}
});
return promise_rejects_dom(t, "AbortError", promise);
}, {
protocol: "ctap1/u2f",
transport: "usb",
isUserConsenting: false,
}, "navigator.credentials.create() after abort without reason");

virtualAuthenticatorPromiseTest(async t => {
const abortController = new AbortController();
const signal = abortController.signal;
const promise = createCredential({
options: {
signal: signal,
}
});
abortController.abort();
return promise_rejects_dom(t, "AbortError", promise);
}, {
protocol: "ctap1/u2f",
transport: "usb",
isUserConsenting: false,
}, "navigator.credentials.create() before abort without reason");

virtualAuthenticatorPromiseTest(async t => {
const abortController = new AbortController();
const signal = abortController.signal;
abortController.abort("CustomError");
const promise = createCredential({
options: {
signal: signal,
}
});
return promise_rejects_exactly(t, "CustomError", promise);
}, {
protocol: "ctap1/u2f",
transport: "usb",
isUserConsenting: false,
}, "navigator.credentials.create() after abort reason");

virtualAuthenticatorPromiseTest(async t => {
const abortController = new AbortController();
const signal = abortController.signal;
const promise = createCredential({
options: {
signal: signal,
}
});
abortController.abort("CustomError");
return promise_rejects_exactly(t, "CustomError", promise);
}, {
protocol: "ctap1/u2f",
transport: "usb",
isUserConsenting: false,
}, "navigator.credentials.create() before abort reason");

virtualAuthenticatorPromiseTest(async t => {
const abortController = new AbortController();
const signal = abortController.signal;
abortController.abort(new Error('error'));
const promise = createCredential({
options: {
signal: signal,
}
});
return promise_rejects_js(t, Error, promise);
}, {
protocol: "ctap1/u2f",
transport: "usb",
isUserConsenting: false,
}, "navigator.credentials.create() after abort reason with Error");

virtualAuthenticatorPromiseTest(async t => {
const abortController = new AbortController();
const signal = abortController.signal;
const promise = createCredential({
options: {
signal: signal,
}
});
abortController.abort(new Error('error'));
return promise_rejects_js(t, Error, promise);
}, {
protocol: "ctap1/u2f",
transport: "usb",
isUserConsenting: false,
}, "navigator.credentials.create() before abort reason with Error");
</script>
66 changes: 66 additions & 0 deletions webauthn/getcredential-abort.https.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>WebAuthn navigator.credentials.get() abort Tests</title>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src=helpers.js></script>
<body></body>
<script>
"use strict";

const getParams = {
publicKey: {
id: 'id',
challenge: new Uint8Array(),
}
};

promise_test(async t => {
const abortController = new AbortController();
abortController.abort();
getParams.signal = abortController.signal;
const promise = navigator.credentials.get(getParams);
return promise_rejects_dom(t, "AbortError", promise);
}, "navigator.credentials.get() after abort without reason");

promise_test(async t => {
const abortController = new AbortController();
getParams.signal = abortController.signal;
const promise = navigator.credentials.get(getParams);
abortController.abort();
return promise_rejects_dom(t, "AbortError", promise);
}, "navigator.credentials.get() before abort without reason");

promise_test(async t => {
const abortController = new AbortController();
abortController.abort("CustomError");
getParams.signal = abortController.signal;
const promise = navigator.credentials.get(getParams);
return promise_rejects_exactly(t, "CustomError", promise);
}, "navigator.credentials.get() after abort reason");

promise_test(async t => {
const abortController = new AbortController();
getParams.signal = abortController.signal;
const promise = navigator.credentials.get(getParams);
abortController.abort("CustomError");
return promise_rejects_exactly(t, "CustomError", promise);
}, "navigator.credentials.get() before abort reason");

promise_test(async t => {
const abortController = new AbortController();
abortController.abort(new Error('error'));
getParams.signal = abortController.signal;
const promise = navigator.credentials.get(getParams);
return promise_rejects_js(t, Error, promise);
}, "navigator.credentials.get() after abort reason with Error");

promise_test(async t => {
const abortController = new AbortController();
getParams.signal = abortController.signal;
const promise = navigator.credentials.get(getParams);
abortController.abort(new Error('error'));
return promise_rejects_js(t, Error, promise);
}, "navigator.credentials.get() before abort reason with Error");
</script>
6 changes: 5 additions & 1 deletion webauthn/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ function cloneObject(o) {

function extendObject(dst, src) {
Object.keys(src).forEach(function(key) {
if (isSimpleObject(src[key])) {
if (isSimpleObject(src[key]) && !isAbortSignal(src[key])) {
dst[key] ||= {};
extendObject(dst[key], src[key]);
} else {
Expand All @@ -356,6 +356,10 @@ function isSimpleObject(o) {
!(o instanceof ArrayBuffer));
}

function isAbortSignal(o) {
return (o instanceof AbortSignal);
}

/**
* CreateCredentialTest
*
Expand Down

0 comments on commit 12c459f

Please sign in to comment.