summaryrefslogtreecommitdiffstats
path: root/browser/extensions/screenshots/background/auth.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/extensions/screenshots/background/auth.js')
-rw-r--r--browser/extensions/screenshots/background/auth.js224
1 files changed, 224 insertions, 0 deletions
diff --git a/browser/extensions/screenshots/background/auth.js b/browser/extensions/screenshots/background/auth.js
new file mode 100644
index 0000000000..f6cfd0f9aa
--- /dev/null
+++ b/browser/extensions/screenshots/background/auth.js
@@ -0,0 +1,224 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* globals log */
+/* globals main, makeUuid, deviceInfo, analytics, catcher, buildSettings, communication */
+
+"use strict";
+
+this.auth = (function() {
+ const exports = {};
+
+ let registrationInfo;
+ let initialized = false;
+ let authHeader = null;
+ let sentryPublicDSN = null;
+ let abTests = {};
+ let accountId = null;
+
+ const fetchStoredInfo = catcher.watchPromise(
+ browser.storage.local.get(["registrationInfo", "abTests"]).then((result) => {
+ if (result.abTests) {
+ abTests = result.abTests;
+ }
+ if (result.registrationInfo) {
+ registrationInfo = result.registrationInfo;
+ }
+ }));
+
+ function getRegistrationInfo() {
+ if (!registrationInfo) {
+ registrationInfo = generateRegistrationInfo();
+ log.info("Generating new device authentication ID", registrationInfo);
+ browser.storage.local.set({registrationInfo});
+ }
+ return registrationInfo;
+ }
+
+ exports.getDeviceId = function() {
+ return registrationInfo && registrationInfo.deviceId;
+ };
+
+ function generateRegistrationInfo() {
+ const info = {
+ deviceId: `anon${makeUuid()}`,
+ secret: makeUuid(),
+ registered: false,
+ };
+ return info;
+ }
+
+ function register() {
+ return new Promise((resolve, reject) => {
+ const registerUrl = main.getBackend() + "/api/register";
+ // TODO: replace xhr with Fetch #2261
+ const req = new XMLHttpRequest();
+ req.open("POST", registerUrl);
+ req.setRequestHeader("content-type", "application/json");
+ req.onload = catcher.watchFunction(() => {
+ if (req.status === 200) {
+ log.info("Registered login");
+ initialized = true;
+ saveAuthInfo(JSON.parse(req.responseText));
+ resolve(true);
+ analytics.sendEvent("registered");
+ } else {
+ analytics.sendEvent("register-failed", `bad-response-${req.status}`);
+ log.warn("Error in response:", req.responseText);
+ const exc = new Error("Bad response: " + req.status);
+ exc.popupMessage = "LOGIN_ERROR";
+ reject(exc);
+ }
+ });
+ req.onerror = catcher.watchFunction(() => {
+ analytics.sendEvent("register-failed", "connection-error");
+ const exc = new Error("Error contacting server");
+ exc.popupMessage = "LOGIN_CONNECTION_ERROR";
+ reject(exc);
+ });
+ req.send(JSON.stringify({
+ deviceId: registrationInfo.deviceId,
+ secret: registrationInfo.secret,
+ deviceInfo: JSON.stringify(deviceInfo()),
+ }));
+ });
+ }
+
+ function login(options) {
+ const { ownershipCheck, noRegister } = options || {};
+ return new Promise((resolve, reject) => {
+ return fetchStoredInfo.then(() => {
+ const registrationInfo = getRegistrationInfo();
+ const loginUrl = main.getBackend() + "/api/login";
+ // TODO: replace xhr with Fetch #2261
+ const req = new XMLHttpRequest();
+ req.open("POST", loginUrl);
+ req.onload = catcher.watchFunction(() => {
+ if (req.status === 404) {
+ if (noRegister) {
+ resolve(false);
+ } else {
+ resolve(register());
+ }
+ } else if (req.status >= 300) {
+ log.warn("Error in response:", req.responseText);
+ const exc = new Error("Could not log in: " + req.status);
+ exc.popupMessage = "LOGIN_ERROR";
+ analytics.sendEvent("login-failed", `bad-response-${req.status}`);
+ reject(exc);
+ } else if (req.status === 0) {
+ const error = new Error("Could not log in, server unavailable");
+ error.popupMessage = "LOGIN_CONNECTION_ERROR";
+ analytics.sendEvent("login-failed", "connection-error");
+ reject(error);
+ } else {
+ initialized = true;
+ const jsonResponse = JSON.parse(req.responseText);
+ log.info("Screenshots logged in");
+ analytics.sendEvent("login");
+ saveAuthInfo(jsonResponse);
+ if (ownershipCheck) {
+ resolve({isOwner: jsonResponse.isOwner});
+ } else {
+ resolve(true);
+ }
+ }
+ });
+ req.onerror = catcher.watchFunction(() => {
+ analytics.sendEvent("login-failed", "connection-error");
+ const exc = new Error("Connection failed");
+ exc.url = loginUrl;
+ exc.popupMessage = "CONNECTION_ERROR";
+ reject(exc);
+ });
+ req.setRequestHeader("content-type", "application/json");
+ req.send(JSON.stringify({
+ deviceId: registrationInfo.deviceId,
+ secret: registrationInfo.secret,
+ deviceInfo: JSON.stringify(deviceInfo()),
+ ownershipCheck,
+ }));
+ });
+ });
+ }
+
+ function saveAuthInfo(responseJson) {
+ accountId = responseJson.accountId;
+ if (responseJson.sentryPublicDSN) {
+ sentryPublicDSN = responseJson.sentryPublicDSN;
+ }
+ if (responseJson.authHeader) {
+ authHeader = responseJson.authHeader;
+ if (!registrationInfo.registered) {
+ registrationInfo.registered = true;
+ catcher.watchPromise(browser.storage.local.set({registrationInfo}));
+ }
+ }
+ if (responseJson.abTests) {
+ abTests = responseJson.abTests;
+ catcher.watchPromise(browser.storage.local.set({abTests}));
+ }
+ }
+
+ exports.maybeLogin = function() {
+ if (!registrationInfo) {
+ return Promise.resolve();
+ }
+
+ return exports.authHeaders();
+ };
+
+ exports.authHeaders = function() {
+ let initPromise = Promise.resolve();
+ if (!initialized) {
+ initPromise = login();
+ }
+ return initPromise.then(() => {
+ if (authHeader) {
+ return {"x-screenshots-auth": authHeader};
+ }
+ log.warn("No auth header available");
+ return {};
+ });
+ };
+
+ exports.getSentryPublicDSN = function() {
+ return sentryPublicDSN || buildSettings.defaultSentryDsn;
+ };
+
+ exports.getAbTests = function() {
+ return abTests;
+ };
+
+ exports.isRegistered = function() {
+ return registrationInfo && registrationInfo.registered;
+ };
+
+ communication.register("getAuthInfo", (sender, ownershipCheck) => {
+ return fetchStoredInfo.then(() => {
+ // If a device id was never generated, report back accordingly.
+ if (!registrationInfo) {
+ return null;
+ }
+
+ return exports.authHeaders().then((authHeaders) => {
+ let info = registrationInfo;
+ if (info.registered) {
+ return login({ownershipCheck}).then((result) => {
+ return {
+ isOwner: result && result.isOwner,
+ deviceId: registrationInfo.deviceId,
+ accountId,
+ authHeaders,
+ };
+ });
+ }
+ info = Object.assign({authHeaders}, info);
+ return info;
+ });
+ });
+});
+
+ return exports;
+})();