summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webauthn/public-key-credential-to-json.https.window.js
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/webauthn/public-key-credential-to-json.https.window.js')
-rw-r--r--testing/web-platform/tests/webauthn/public-key-credential-to-json.https.window.js157
1 files changed, 157 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webauthn/public-key-credential-to-json.https.window.js b/testing/web-platform/tests/webauthn/public-key-credential-to-json.https.window.js
new file mode 100644
index 0000000000..8de3b8c3cd
--- /dev/null
+++ b/testing/web-platform/tests/webauthn/public-key-credential-to-json.https.window.js
@@ -0,0 +1,157 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/resources/utils.js
+// META: script=helpers.js
+
+function assertObjectKeysEq(a, b) {
+ let a_keys = new Set(Object.keys(a));
+ let b_keys = new Set(Object.keys(b));
+ assert_true(
+ a_keys.length == b_keys.length && [...a_keys].every(k => b_keys.has(k)),
+ `keys differ: ${a_keys} != ${b_keys}`);
+}
+
+// Returns the JSON encoding for `value`. If `value` is a function, `optParent`
+// is the object to which execution should be bound.
+function convertValue(value, optParent) {
+ switch (typeof value) {
+ case 'undefined':
+ case 'boolean':
+ case 'number':
+ case 'bigint':
+ case 'string':
+ case 'symbol':
+ return value;
+ case 'function':
+ return value.apply(optParent);
+ case 'object':
+ if (value.__proto__.constructor === Object) {
+ var result = {};
+ Object.entries(value).map((k, v) => {
+ result[k] = convertValue(k, v);
+ });
+ return result;
+ }
+ if (value instanceof Array) {
+ return value.map(convertValue);
+ }
+ if (value instanceof ArrayBuffer) {
+ return base64urlEncode(new Uint8Array(value));
+ }
+ throw `can't convert value ${value} in ${parent}`;
+ default:
+ throw `${value} has unexpected type`;
+ }
+}
+
+// Conversion spec for a single attribute.
+// @typedef {Object} ConvertParam
+// @property {string} name - The name of the attribute to convert from
+// @property {string=} target - The name of the attribute to convert to, if
+// different from `name`
+// @property {function=} func - Method to convert this property. Defaults to
+// convertValue().
+
+// Returns the JSON object for `obj`.
+//
+// @param obj
+// @param {Array<(string|ConvertParam)>} keys - The names of parameters in
+// `obj` to convert, or instances of ConvertParam for complex cases.
+function convertObject(obj, params) {
+ let result = {};
+ params.forEach((param) => {
+ switch (typeof (param)) {
+ case 'string':
+ assert_true(param in obj, `missing ${param}`);
+ if (obj[param] !== null) {
+ result[param] = convertValue(obj[param], obj);
+ }
+ break;
+ case 'object':
+ assert_true(param.name in obj, `missing ${param.name}`);
+ const val = obj[param.name];
+ const target_key = param.target || param.name;
+ const convert_func = param.func || convertValue;
+ try {
+ result[target_key] =
+ convert_func(((typeof val) == 'function' ? val.apply(obj) : val));
+ } catch (e) {
+ throw `failed to convert ${param.name}: ${e}`
+ }
+ break;
+ default:
+ throw `invalid key ${param}`;
+ }
+ });
+ return result;
+}
+
+// Converts an AuthenticatorResponse instance into a JSON object.
+// @param {!AuthenticatorResponse}
+function authenticatorResponseToJson(response) {
+ assert_true(
+ (response instanceof AuthenticatorAttestationResponse) ||
+ (response instanceof AuthenticatorAssertionResponse));
+ const isAttestation = (response instanceof AuthenticatorAttestationResponse);
+ const keys =
+ (isAttestation ?
+ [
+ 'clientDataJSON', 'attestationObject',
+ {name: 'getTransports', target: 'transports'}
+ ] :
+ ['clientDataJSON', 'authenticatorData', 'signature', 'userHandle']);
+ return convertObject(response, keys);
+}
+
+// Converts a PublicKeyCredential instance to a JSON object.
+// @param {!PublicKeyCredential}
+function publicKeyCredentialToJson(cred) {
+ const keys = [
+ 'id', 'rawId', {name: 'response', func: authenticatorResponseToJson},
+ 'authenticatorAttachment',
+ {name: 'getClientExtensionResults', target: 'clientExtensionResults'},
+ 'type'
+ ];
+ return convertObject(cred, keys);
+}
+
+// Returns a copy of `jsonObj`, which must be a JSON type, with object keys
+// recursively sorted in lexicographic order; or simply `jsonObj` if it is not
+// an instance of Object.
+function deepSortKeys(jsonObj) {
+ if (typeof jsonObj !== 'object' || jsonObj === null ||
+ jsonObj.__proto__.constructor !== Object ||
+ Object.keys(jsonObj).length === 0) {
+ return jsonObj;
+ }
+ return Object.keys(jsonObj).sort().reduce((acc, key) => {
+ acc[key] = deepSortKeys(jsonObj[key]);
+ return acc;
+ }, {});
+}
+
+// Asserts that `actual` and `expected`, which are both JSON types, are equal.
+// The object key order is ignored for comparison.
+function assertJsonEquals(actual, expected, optMsg) {
+ assert_equals(
+ JSON.stringify(deepSortKeys(actual)),
+ JSON.stringify(deepSortKeys(expected)), optMsg);
+}
+
+virtualAuthenticatorPromiseTest(
+ async t => {
+ let credential = await createCredential();
+ assertJsonEquals(
+ credential.toJSON(), publicKeyCredentialToJson(credential));
+
+ let assertion = await assertCredential(credential);
+ assertJsonEquals(
+ assertion.toJSON(), publicKeyCredentialToJson(assertion));
+ },
+ {
+ protocol: 'ctap2_1',
+ transport: 'usb',
+ },
+ 'toJSON()');