1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
|
export const manifest_origin = "https://{{host}}:{{ports[https][0]}}";
export const alt_manifest_origin = 'https://{{hosts[alt][]}}:{{ports[https][0]}}';
export const same_site_manifest_origin = 'https://{{hosts[][www1]}}:{{ports[https][0]}}';
export const default_manifest_path = '/credential-management/support/fedcm/manifest.py';
export function open_and_wait_for_popup(origin, path) {
return new Promise(resolve => {
let popup_window = window.open(origin + path);
// We rely on the popup page to send us a message when done.
const popup_message_handler = (event) => {
// We use new URL() to ensure the two origins are normalized the same
// way (especially so that default ports are handled identically).
if (new URL(event.origin).toString() == new URL(origin).toString()) {
popup_window.close();
window.removeEventListener('message', popup_message_handler);
resolve();
}
};
window.addEventListener('message', popup_message_handler);
});
}
// Set the identity provider cookie.
export function set_fedcm_cookie(host) {
if (host == undefined) {
document.cookie = 'cookie=1; SameSite=None; Path=/credential-management/support; Secure';
return Promise.resolve();
} else {
return open_and_wait_for_popup(host, '/credential-management/support/set_cookie');
}
}
// Set the alternate identity provider cookie.
export function set_alt_fedcm_cookie() {
return set_fedcm_cookie(alt_manifest_origin);
}
export function mark_signed_in(origin = manifest_origin) {
return open_and_wait_for_popup(origin, '/credential-management/support/mark_signedin');
}
export function mark_signed_out(origin = manifest_origin) {
return open_and_wait_for_popup(origin, '/credential-management/support/mark_signedout');
}
// Returns FedCM CredentialRequestOptions for which navigator.credentials.get()
// succeeds.
export function request_options_with_mediation_required(manifest_filename, origin = manifest_origin) {
if (manifest_filename === undefined) {
manifest_filename = "manifest.py";
}
const manifest_path = `${origin}/\
credential-management/support/fedcm/${manifest_filename}`;
return {
identity: {
providers: [{
configURL: manifest_path,
clientId: '1',
nonce: '2'
}]
},
mediation: 'required'
};
}
// Returns alternate FedCM CredentialRequestOptions for which navigator.credentials.get()
// succeeds.
export function alt_request_options_with_mediation_required(manifest_filename) {
return request_options_with_mediation_required(manifest_filename, alt_manifest_origin);
}
// Returns FedCM CredentialRequestOptions with auto re-authentication.
// succeeds.
export function request_options_with_mediation_optional(manifest_filename) {
let options = alt_request_options_with_mediation_required(manifest_filename);
// Approved client
options.identity.providers[0].clientId = '123';
options.mediation = 'optional';
return options;
}
export function request_options_with_context(manifest_filename, context) {
if (manifest_filename === undefined) {
manifest_filename = "manifest.py";
}
const manifest_path = `${manifest_origin}/\
credential-management/support/fedcm/${manifest_filename}`;
return {
identity: {
providers: [{
configURL: manifest_path,
clientId: '1',
nonce: '2'
}],
context: context
},
mediation: 'required'
};
}
export function request_options_with_two_idps(mediation = 'required') {
const first_config = `${manifest_origin}${default_manifest_path}`;
const second_config = `${alt_manifest_origin}${default_manifest_path}`;
return {
identity: {
providers: [{
configURL: first_config,
clientId: '123',
nonce: 'N1'
},
{
configURL: second_config,
clientId: '456',
nonce: 'N2'
}],
},
mediation: mediation
};
}
// Test wrapper which does FedCM-specific setup.
export function fedcm_test(test_func, test_name) {
promise_test(async t => {
// Ensure we start from a clean slate.
await test_driver.delete_all_cookies();
// Turn off delays that are not useful in tests.
try {
await test_driver.set_fedcm_delay_enabled(false);
} catch (e) {
// Failure is not critical; it just might slow down tests.
}
await set_fedcm_cookie();
await set_alt_fedcm_cookie();
await test_func(t);
}, test_name);
}
function select_manifest_impl(manifest_url) {
const url_query = (manifest_url === undefined)
? '' : `?manifest_url=${manifest_url}`;
return new Promise(resolve => {
const img = document.createElement('img');
img.src = `/credential-management/support/fedcm/select_manifest_in_root_manifest.py${url_query}`;
img.addEventListener('error', resolve);
document.body.appendChild(img);
});
}
// Sets the manifest returned by the next fetch of /.well-known/web_identity
// select_manifest() only affects the next fetch and not any subsequent fetches
// (ex second next fetch).
export function select_manifest(test, test_options) {
// Add cleanup in case that /.well-known/web_identity is not fetched at all.
test.add_cleanup(async () => {
await select_manifest_impl();
});
const manifest_url = test_options.identity.providers[0].configURL;
return select_manifest_impl(manifest_url);
}
export function request_options_with_login_hint(manifest_filename, login_hint) {
let options = request_options_with_mediation_required(manifest_filename);
options.identity.providers[0].loginHint = login_hint;
return options;
}
export function request_options_with_domain_hint(manifest_filename, domain_hint) {
let options = request_options_with_mediation_required(manifest_filename);
options.identity.providers[0].domainHint = domain_hint;
return options;
}
export function fedcm_get_dialog_type_promise(t) {
return new Promise(resolve => {
async function helper() {
// Try to get the dialog type. If the UI is not up yet, we'll catch an exception
// and try again in 100ms.
try {
const type = await window.test_driver.get_fedcm_dialog_type();
resolve(type);
} catch (ex) {
t.step_timeout(helper, 100);
}
}
helper();
});
}
export function fedcm_get_title_promise(t) {
return new Promise(resolve => {
async function helper() {
// Try to get the title. If the UI is not up yet, we'll catch an exception
// and try again in 100ms.
try {
const title = await window.test_driver.get_fedcm_dialog_title();
resolve(title);
} catch (ex) {
t.step_timeout(helper, 100);
}
}
helper();
});
}
export function fedcm_select_account_promise(t, account_index) {
return new Promise(resolve => {
async function helper() {
// Try to select the account. If the UI is not up yet, we'll catch an exception
// and try again in 100ms.
try {
await window.test_driver.select_fedcm_account(account_index);
resolve();
} catch (ex) {
t.step_timeout(helper, 100);
}
}
helper();
});
}
export function fedcm_get_and_select_first_account(t, options) {
const credentialPromise = navigator.credentials.get(options);
fedcm_select_account_promise(t, 0);
return credentialPromise;
}
export function fedcm_error_dialog_dismiss(t) {
return new Promise(resolve => {
async function helper() {
// Try to select the account. If the UI is not up yet, we'll catch an exception
// and try again in 100ms.
try {
let type = await fedcm_get_dialog_type_promise(t);
assert_equals(type, "Error");
await window.test_driver.cancel_fedcm_dialog();
resolve();
} catch (ex) {
t.step_timeout(helper, 100);
}
}
helper();
});
}
export function fedcm_error_dialog_click_button(t, button) {
return new Promise(resolve => {
async function helper() {
// Try to select the account. If the UI is not up yet, we'll catch an exception
// and try again in 100ms.
try {
let type = await fedcm_get_dialog_type_promise(t);
assert_equals(type, "Error");
await window.test_driver.click_fedcm_dialog_button(button);
resolve();
} catch (ex) {
t.step_timeout(helper, 100);
}
}
helper();
});
}
export function disconnect_options(accountHint, manifest_filename) {
if (manifest_filename === undefined) {
manifest_filename = "manifest.py";
}
const manifest_path = `${manifest_origin}/\
credential-management/support/fedcm/${manifest_filename}`;
return {
configURL: manifest_path,
clientId: '1',
accountHint: accountHint
};
}
export function alt_disconnect_options(accountHint, manifest_filename) {
if (manifest_filename === undefined) {
manifest_filename = "manifest.py";
}
const manifest_path = `${alt_manifest_origin}/\
credential-management/support/fedcm/${manifest_filename}`;
return {
configURL: manifest_path,
clientId: '1',
accountHint: accountHint
};
}
|