2 <title>Federated Credential Management API network request tests.
</title>
3 <link rel=
"help" href=
"https://fedidcg.github.io/FedCM">
4 <script src=
"/resources/testharness.js"></script>
5 <script src=
"/resources/testharnessreport.js"></script>
6 <script src=
"/service-workers/service-worker/resources/test-helpers.sub.js"></script>
10 <script type=
"module">
11 import {request_options_with_mediation_required,
12 alt_request_options_with_mediation_required,
13 request_options_with_mediation_optional,
16 set_fedcm_cookie} from './support/fedcm-helper.sub.js';
18 function loadUrlInIframe(url) {
19 let iframe = document.createElement(
"iframe");
20 return new Promise(resolve =
> {
22 iframe.onload = function() { resolve(iframe); };
23 document.body.appendChild(iframe);
27 fedcm_test(async t =
> {
28 const cred = await navigator.credentials.get(request_options_with_mediation_required());
29 assert_equals(cred.token,
"token");
30 },
"Successfully obtaining token should resolve the promise.");
32 fedcm_test(async t =
> {
33 const first = navigator.credentials.get(request_options_with_mediation_required());
34 const second = navigator.credentials.get(alt_request_options_with_mediation_required());
36 // We have to call promise_rejects_dom here, because if we call it after
37 // the promise gets rejected, the unhandled rejection event handler is called
38 // and fails the test even if we handle the rejection later.
39 const rej = promise_rejects_dom(t, 'AbortError', second);
41 const first_cred = await first;
42 assert_equals(first_cred.token,
"token");
46 "When there's a pending request, a second `get` call should be rejected. ");
48 fedcm_test(async t =
> {
49 let test_options = request_options_with_mediation_required();
50 test_options.identity.providers = [];
51 const cred = navigator.credentials.get(test_options);
52 return promise_rejects_js(t, TypeError, cred);
53 },
"Reject when provider list is empty");
55 fedcm_test(async t =
> {
56 let test_options = request_options_with_mediation_required();
57 delete test_options.identity.providers[
0].configURL;
58 const cred = navigator.credentials.get(test_options);
59 return promise_rejects_js(t, TypeError, cred);
60 },
"Reject when configURL is missing" );
62 fedcm_test(async t =
> {
63 let test_options = request_options_with_mediation_required();
64 test_options.identity.providers[
0].configURL = 'test';
65 const cred = navigator.credentials.get(test_options);
66 return promise_rejects_dom(t,
"InvalidStateError", cred);
67 },
"Reject when configURL is invalid");
69 fedcm_test(async t =
> {
70 let test_options = request_options_with_mediation_required();
71 test_options.identity.providers[
0].clientId = '';
72 const cred = navigator.credentials.get(test_options);
73 return promise_rejects_dom(t,
"InvalidStateError", cred);
74 },
"Reject when clientId is empty");
76 fedcm_test(async t =
> {
77 let test_options = request_options_with_mediation_required();
78 assert_true(
"nonce" in test_options.identity.providers[
0]);
79 delete test_options.identity.providers[
0].nonce;
80 const cred = await navigator.credentials.get(test_options);
81 assert_equals(cred.token,
"token");
82 },
"nonce is not required in FederatedIdentityProvider.");
84 fedcm_test(async t =
> {
85 let test_options = request_options_with_mediation_required();
86 delete test_options.identity.providers[
0].clientId;
87 const cred = navigator.credentials.get(test_options);
88 return promise_rejects_js(t, TypeError, cred);
89 },
"Reject when clientId is missing" );
91 fedcm_test(async t =
> {
92 let controller = new AbortController();
93 let test_options = request_options_with_mediation_required();
94 test_options.signal = controller.signal;
95 const cred = navigator.credentials.get(test_options);
97 return promise_rejects_dom(t, 'AbortError', cred);
98 },
"Test the abort signal");
100 fedcm_test(async t =
> {
101 let controller = new AbortController();
102 let test_options = request_options_with_mediation_required();
103 test_options.signal = controller.signal;
104 const first_cred = navigator.credentials.get(test_options);
106 await promise_rejects_dom(t, 'AbortError', first_cred);
108 const second_cred = await navigator.credentials.get(request_options_with_mediation_required());
109 assert_equals(second_cred.token,
"token");
110 },
"Get after abort should work");
112 fedcm_test(async t =
> {
113 let test_options = request_options_with_mediation_required('manifest-not-in-list.json');
114 const cred = navigator.credentials.get(test_options);
115 return promise_rejects_dom(t, 'NetworkError', cred);
116 }, 'Test that the promise is rejected if the manifest is not in the manifest list');
118 fedcm_test(async t =
> {
119 let test_options = request_options_with_mediation_required(
"manifest_redirect_accounts.json");
120 await select_manifest(t, test_options);
122 const cred = navigator.credentials.get(test_options);
123 return promise_rejects_dom(t, 'NetworkError', cred);
124 }, 'Test that promise is rejected if accounts endpoint redirects');
125 // A malicious site might impersonate an IDP, redirecting the accounts endpoint to a
126 // legitimate IDP in order to get the list of user accounts.
128 fedcm_test(async t =
> {
129 let test_options = request_options_with_mediation_required(
"manifest_redirect_token.json");
130 await select_manifest(t, test_options);
132 const cred = navigator.credentials.get(test_options);
133 return promise_rejects_dom(t, 'NetworkError', cred);
134 }, 'Test that token endpoint does not follow redirects');
135 // The token endpoint should not follow redirects because the user has not consented
136 // to share their identity with the redirect destination.
138 fedcm_test(async t =
> {
139 // Reset the client_metadata fetch count.
140 const clear_metadata_count_path = `support/fedcm/client_metadata_clear_count.py`;
141 await fetch(clear_metadata_count_path);
143 const cred = await navigator.credentials.get(request_options_with_mediation_required());
144 assert_equals(cred.token,
"token");
146 await new Promise(resolve =
> {
147 let popup_window = window.open('support/fedcm/client_metadata.py?skip_checks=
1');
148 const popup_window_load_handler = (event) =
> {
149 popup_window.removeEventListener('load', popup_window_load_handler);
150 popup_window.close();
153 popup_window.addEventListener('load', popup_window_load_handler);
156 const client_metadata_counter = await fetch(clear_metadata_count_path);
157 const client_metadata_counter_text = await client_metadata_counter.text()
158 assert_equals(client_metadata_counter_text,
"2");
159 }, 'Test client_metadata request');
161 // - Headers sent to client metadata endpoint. (Counter is not incremented if the headers are
163 // - That the client metadata response is not cached. If the client metadata response were
164 // cached, when the user visits the IDP as a first party, the IDP would be able to determine the
165 // last RP the user visited regardless of whether the user granted consent via the FedCM prompt.
167 fedcm_test(async t =
> {
168 const service_worker_url = 'support/fedcm/intercept_service_worker.js';
169 const sw_scope_url = '/credential-management/support/fedcm/';
170 // URL for querying number of page loads observed by service worker.
171 const query_sw_intercepts_url = 'support/fedcm/query_service_worker_intercepts.html';
172 const page_in_sw_scope_url = 'support/fedcm/simple.html';
174 const sw_registration = await service_worker_unregister_and_register(
175 t, service_worker_url, sw_scope_url);
176 t.add_cleanup(() =
> service_worker_unregister(t, sw_scope_url));
177 await wait_for_state(t, sw_registration.installing, 'activated');
179 // Verify that service worker works.
180 await loadUrlInIframe(page_in_sw_scope_url);
181 let query_sw_iframe = await loadUrlInIframe(query_sw_intercepts_url);
182 assert_equals(query_sw_iframe.contentDocument.body.textContent,
"1");
184 await set_fedcm_cookie();
185 const cred = await navigator.credentials.get(request_options_with_mediation_required());
186 assert_equals(cred.token,
"token");
188 // Use cache buster query parameter to avoid cached response.
189 let query_sw_iframe2 = await loadUrlInIframe(query_sw_intercepts_url +
"?2");
190 assert_equals(query_sw_iframe2.contentDocument.body.textContent,
"1");
191 }, 'Test that service worker cannot observe fetches performed by FedCM API');
193 fedcm_test(async t =
> {
194 let test_options = request_options_with_mediation_optional(
"manifest_with_single_account.json");
195 await select_manifest(t, test_options);
197 // Signs in john_doe so that they will be a returning user
198 let cred = await navigator.credentials.get(test_options);
199 assert_equals(cred.token,
"account_id=john_doe");
201 test_options = request_options_with_mediation_optional(
"manifest_with_two_accounts.json");
202 await select_manifest(t, test_options);
204 // There are two accounts
"Jane" and
"John" returned in that order. Without
205 // auto re-authn, the first account
"Jane" would be selected and an token
206 // would be issued to that account. However, since
"John" is returning and
207 //
"Jane" is a new user, the second account
"John" will be selected.
208 cred = await navigator.credentials.get(test_options);
209 assert_equals(cred.token,
"account_id=john_doe");
210 },
"Test that the returning account from the two accounts will be auto re-authenticated.");