Source code

Revision control

Other Tools

1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
* License, v. 2.0. If a copy of the MPL was not distributed with this
3
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
"use strict";
5
var EXPORTED_SYMBOLS = ["FxAccountsConfig"];
6
7
const { RESTRequest } = ChromeUtils.import(
9
);
10
const { log } = ChromeUtils.import(
12
);
13
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
14
const { XPCOMUtils } = ChromeUtils.import(
16
);
17
18
ChromeUtils.defineModuleGetter(
19
this,
20
"fxAccounts",
22
);
23
24
ChromeUtils.defineModuleGetter(
25
this,
26
"EnsureFxAccountsWebChannel",
28
);
29
30
XPCOMUtils.defineLazyPreferenceGetter(
31
this,
32
"ROOT_URL",
33
"identity.fxaccounts.remote.root"
34
);
35
XPCOMUtils.defineLazyPreferenceGetter(
36
this,
37
"CONTEXT_PARAM",
38
"identity.fxaccounts.contextParam"
39
);
40
XPCOMUtils.defineLazyPreferenceGetter(
41
this,
42
"REQUIRES_HTTPS",
43
"identity.fxaccounts.allowHttp",
44
false,
45
null,
46
val => !val
47
);
48
49
const CONFIG_PREFS = [
50
"identity.fxaccounts.remote.root",
51
"identity.fxaccounts.auth.uri",
52
"identity.fxaccounts.remote.oauth.uri",
53
"identity.fxaccounts.remote.profile.uri",
54
"identity.fxaccounts.remote.pairing.uri",
55
"identity.sync.tokenserver.uri",
56
];
57
58
var FxAccountsConfig = {
59
async promiseEmailURI(email, entrypoint, extraParams = {}) {
60
return this._buildURL("", {
61
extraParams: { entrypoint, email, ...extraParams },
62
});
63
},
64
65
async promiseConnectAccountURI(entrypoint, extraParams = {}) {
66
return this._buildURL("", {
67
extraParams: { entrypoint, action: "email", ...extraParams },
68
});
69
},
70
71
async promiseForceSigninURI(entrypoint, extraParams = {}) {
72
return this._buildURL("force_auth", {
73
extraParams: { entrypoint, ...extraParams },
74
addAccountIdentifiers: true,
75
});
76
},
77
78
async promiseManageURI(entrypoint, extraParams = {}) {
79
return this._buildURL("settings", {
80
extraParams: { entrypoint, ...extraParams },
81
addAccountIdentifiers: true,
82
});
83
},
84
85
async promiseChangeAvatarURI(entrypoint, extraParams = {}) {
86
return this._buildURL("settings/avatar/change", {
87
extraParams: { entrypoint, ...extraParams },
88
addAccountIdentifiers: true,
89
});
90
},
91
92
async promiseManageDevicesURI(entrypoint, extraParams = {}) {
93
return this._buildURL("settings/clients", {
94
extraParams: { entrypoint, ...extraParams },
95
addAccountIdentifiers: true,
96
});
97
},
98
99
async promiseConnectDeviceURI(entrypoint, extraParams = {}) {
100
return this._buildURL("connect_another_device", {
101
extraParams: { entrypoint, ...extraParams },
102
addAccountIdentifiers: true,
103
});
104
},
105
106
async promisePairingURI(extraParams = {}) {
107
return this._buildURL("pair", {
108
extraParams,
109
includeDefaultParams: false,
110
});
111
},
112
113
async promiseOAuthURI(extraParams = {}) {
114
return this._buildURL("oauth", {
115
extraParams,
116
includeDefaultParams: false,
117
});
118
},
119
120
async promiseMetricsFlowURI(entrypoint, extraParams = {}) {
121
return this._buildURL("metrics-flow", {
122
extraParams: { entrypoint, ...extraParams },
123
includeDefaultParams: false,
124
});
125
},
126
127
get defaultParams() {
128
return { service: "sync", context: CONTEXT_PARAM };
129
},
130
131
/**
132
* @param path should be parsable by the URL constructor first parameter.
133
* @param {bool} [options.includeDefaultParams] If true include the default search params.
134
* @param {Object.<string, string>} [options.extraParams] Additionnal search params.
135
* @param {bool} [options.addAccountIdentifiers] if true we add the current logged-in user uid and email to the search params.
136
*/
137
async _buildURL(
138
path,
139
{
140
includeDefaultParams = true,
141
extraParams = {},
142
addAccountIdentifiers = false,
143
}
144
) {
145
await this.ensureConfigured();
146
const url = new URL(path, ROOT_URL);
147
if (REQUIRES_HTTPS && url.protocol != "https:") {
148
throw new Error("Firefox Accounts server must use HTTPS");
149
}
150
const params = {
151
...(includeDefaultParams ? this.defaultParams : null),
152
...extraParams,
153
};
154
for (let [k, v] of Object.entries(params)) {
155
url.searchParams.append(k, v);
156
}
157
if (addAccountIdentifiers) {
158
const accountData = await this.getSignedInUser();
159
if (!accountData) {
160
return null;
161
}
162
url.searchParams.append("uid", accountData.uid);
163
url.searchParams.append("email", accountData.email);
164
}
165
return url.href;
166
},
167
168
async _buildURLFromString(href, extraParams = {}) {
169
const url = new URL(href);
170
for (let [k, v] of Object.entries(extraParams)) {
171
url.searchParams.append(k, v);
172
}
173
return url.href;
174
},
175
176
resetConfigURLs() {
177
let autoconfigURL = this.getAutoConfigURL();
178
if (!autoconfigURL) {
179
return;
180
}
181
// They have the autoconfig uri pref set, so we clear all the prefs that we
182
// will have initialized, which will leave them pointing at production.
183
for (let pref of CONFIG_PREFS) {
184
Services.prefs.clearUserPref(pref);
185
}
186
// Reset the webchannel.
187
EnsureFxAccountsWebChannel();
188
},
189
190
getAutoConfigURL() {
191
let pref = Services.prefs.getCharPref(
192
"identity.fxaccounts.autoconfig.uri",
193
""
194
);
195
if (!pref) {
196
// no pref / empty pref means we don't bother here.
197
return "";
198
}
199
let rootURL = Services.urlFormatter.formatURL(pref);
200
if (rootURL.endsWith("/")) {
201
rootURL = rootURL.slice(0, -1);
202
}
203
return rootURL;
204
},
205
206
async ensureConfigured() {
207
let isSignedIn = !!(await this.getSignedInUser());
208
if (!isSignedIn) {
209
await this.fetchConfigURLs();
210
}
211
},
212
213
// Read expected client configuration from the fxa auth server
214
// (from `identity.fxaccounts.autoconfig.uri`/.well-known/fxa-client-configuration)
215
// and replace all the relevant our prefs with the information found there.
216
// This is only done before sign-in and sign-up, and even then only if the
217
// `identity.fxaccounts.autoconfig.uri` preference is set.
218
async fetchConfigURLs() {
219
let rootURL = this.getAutoConfigURL();
220
if (!rootURL) {
221
return;
222
}
223
let configURL = rootURL + "/.well-known/fxa-client-configuration";
224
let request = new RESTRequest(configURL);
225
request.setHeader("Accept", "application/json");
226
227
// Catch and rethrow the error inline.
228
let resp = await request.get().catch(e => {
229
log.error(`Failed to get configuration object from "${configURL}"`, e);
230
throw e;
231
});
232
if (!resp.success) {
233
log.error(
234
`Received HTTP response code ${
235
resp.status
236
} from configuration object request`
237
);
238
if (resp.body) {
239
log.debug("Got error response", resp.body);
240
}
241
throw new Error(
242
`HTTP status ${resp.status} from configuration object request`
243
);
244
}
245
246
log.debug("Got successful configuration response", resp.body);
247
try {
248
// Update the prefs directly specified by the config.
249
let config = JSON.parse(resp.body);
250
let authServerBase = config.auth_server_base_url;
251
if (!authServerBase.endsWith("/v1")) {
252
authServerBase += "/v1";
253
}
254
Services.prefs.setCharPref(
255
"identity.fxaccounts.auth.uri",
256
authServerBase
257
);
258
Services.prefs.setCharPref(
259
"identity.fxaccounts.remote.oauth.uri",
260
config.oauth_server_base_url + "/v1"
261
);
262
// At the time of landing this, our servers didn't yet answer with pairing_server_base_uri.
263
// Remove this condition check once Firefox 68 is stable.
264
if (config.pairing_server_base_uri) {
265
Services.prefs.setCharPref(
266
"identity.fxaccounts.remote.pairing.uri",
267
config.pairing_server_base_uri
268
);
269
}
270
Services.prefs.setCharPref(
271
"identity.fxaccounts.remote.profile.uri",
272
config.profile_server_base_url + "/v1"
273
);
274
Services.prefs.setCharPref(
275
"identity.sync.tokenserver.uri",
276
config.sync_tokenserver_base_url + "/1.0/sync/1.5"
277
);
278
Services.prefs.setCharPref("identity.fxaccounts.remote.root", rootURL);
279
280
// Ensure the webchannel is pointed at the correct uri
281
EnsureFxAccountsWebChannel();
282
} catch (e) {
283
log.error(
284
"Failed to initialize configuration preferences from autoconfig object",
285
e
286
);
287
throw e;
288
}
289
},
290
291
// For test purposes, returns a Promise.
292
getSignedInUser() {
293
return fxAccounts.getSignedInUser();
294
},
295
};