summaryrefslogtreecommitdiffstats
path: root/b2g/components/SignInToWebsite.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'b2g/components/SignInToWebsite.jsm')
-rw-r--r--b2g/components/SignInToWebsite.jsm444
1 files changed, 0 insertions, 444 deletions
diff --git a/b2g/components/SignInToWebsite.jsm b/b2g/components/SignInToWebsite.jsm
deleted file mode 100644
index fd1349d46..000000000
--- a/b2g/components/SignInToWebsite.jsm
+++ /dev/null
@@ -1,444 +0,0 @@
-/* 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/. */
-
-/*
- * SignInToWebsite.jsm - UX Controller and means for accessing identity
- * cookies on behalf of relying parties.
- *
- * Currently, the b2g security architecture isolates web applications
- * so that each window has access only to a local cookie jar:
- *
- * To prevent Web apps from interfering with one another, each one is
- * hosted on a separate domain, and therefore may only access the
- * resources associated with its domain. These resources include
- * things such as IndexedDB databases, cookies, offline storage,
- * and so forth.
- *
- * -- https://developer.mozilla.org/en-US/docs/Mozilla/Firefox_OS/Security/Security_model
- *
- * As a result, an authentication system like Persona cannot share its
- * cookie jar with multiple relying parties, and so would require a
- * fresh login request in every window. This would not be a good
- * experience.
- *
- *
- * In order for navigator.id.request() to maintain state in a single
- * cookie jar, we cause all Persona interactions to take place in a
- * content context that is launched by the system application, with the
- * result that Persona has a single cookie jar that all Relying
- * Parties can use. Since of course those Relying Parties cannot
- * reach into the system cookie jar, the Controller in this module
- * provides a way to get messages and data to and fro between the
- * Relying Party in its window context, and the Persona internal api
- * in its context.
- *
- * On the Relying Party's side, say a web page invokes
- * navigator.id.watch(), to register callbacks, and then
- * navigator.id.request() to request an assertion. The navigator.id
- * calls are provided by nsDOMIdentity. nsDOMIdentity messages down
- * to the privileged DOMIdentity code (using cpmm and ppmm message
- * managers). DOMIdentity stores the state of Relying Party flows
- * using an Identity service (MinimalIdentity.jsm), and emits messages
- * requesting Persona functions (doWatch, doReady, doLogout).
- *
- * The Identity service sends these observer messages to the
- * Controller in this module, which in turn triggers content to open a
- * window to host the Persona js. If user interaction is required,
- * content will open the trusty UI. If user interaction is not required,
- * and we only need to get to Persona functions, content will open a
- * hidden iframe. In either case, a window is opened into which the
- * controller causes the script identity.js to be injected. This
- * script provides the glue between the in-page javascript and the
- * pipe back down to the Controller, translating navigator.internal
- * function callbacks into messages sent back to the Controller.
- *
- * As a result, a navigator.internal function in the hosted popup or
- * iframe can call back to the injected identity.js (doReady, doLogin,
- * or doLogout). identity.js callbacks send messages back through the
- * pipe to the Controller. The controller invokes the corresponding
- * function on the Identity Service (doReady, doLogin, or doLogout).
- * The IdentityService calls the corresponding callback for the
- * correct Relying Party, which causes DOMIdentity to send a message
- * up to the Relying Party through nsDOMIdentity
- * (Identity:RP:Watch:OnLogin etc.), and finally, nsDOMIdentity
- * receives these messages and calls the original callback that the
- * Relying Party registered (navigator.id.watch(),
- * navigator.id.request(), or navigator.id.logout()).
- */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["SignInToWebsiteController"];
-
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "getRandomId",
- "resource://gre/modules/identity/IdentityUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
- "resource://gre/modules/identity/MinimalIdentity.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Logger",
- "resource://gre/modules/identity/LogUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
- "resource://gre/modules/SystemAppProxy.jsm");
-
-// The default persona uri; can be overwritten with toolkit.identity.uri pref.
-// Do this if you want to repoint to a different service for testing.
-// There's no point in setting up an observer to monitor the pref, as b2g prefs
-// can only be overwritten when the profie is recreated. So just get the value
-// on start-up.
-var kPersonaUri = "https://firefoxos.persona.org";
-try {
- kPersonaUri = Services.prefs.getCharPref("toolkit.identity.uri");
-} catch(noSuchPref) {
- // stick with the default value
-}
-
-// JS shim that contains the callback functions that
-// live within the identity UI provisioning frame.
-const kIdentityShimFile = "chrome://b2g/content/identity.js";
-
-// Type of MozChromeEvents to handle id dialogs.
-const kOpenIdentityDialog = "id-dialog-open";
-const kDoneIdentityDialog = "id-dialog-done";
-const kCloseIdentityDialog = "id-dialog-close-iframe";
-
-// Observer messages to communicate to shim
-const kIdentityDelegateWatch = "identity-delegate-watch";
-const kIdentityDelegateRequest = "identity-delegate-request";
-const kIdentityDelegateLogout = "identity-delegate-logout";
-const kIdentityDelegateFinished = "identity-delegate-finished";
-const kIdentityDelegateReady = "identity-delegate-ready";
-
-const kIdentityControllerDoMethod = "identity-controller-doMethod";
-
-function log(...aMessageArgs) {
- Logger.log.apply(Logger, ["SignInToWebsiteController"].concat(aMessageArgs));
-}
-
-log("persona uri =", kPersonaUri);
-
-function sendChromeEvent(details) {
- details.uri = kPersonaUri;
- SystemAppProxy.dispatchEvent(details);
-}
-
-function Pipe() {
- this._watchers = [];
-}
-
-Pipe.prototype = {
- init: function pipe_init() {
- Services.obs.addObserver(this, "identity-child-process-shutdown", false);
- Services.obs.addObserver(this, "identity-controller-unwatch", false);
- },
-
- uninit: function pipe_uninit() {
- Services.obs.removeObserver(this, "identity-child-process-shutdown");
- Services.obs.removeObserver(this, "identity-controller-unwatch");
- },
-
- observe: function Pipe_observe(aSubject, aTopic, aData) {
- let options = {};
- if (aSubject) {
- options = aSubject.wrappedJSObject;
- }
- switch (aTopic) {
- case "identity-child-process-shutdown":
- log("pipe removing watchers by message manager");
- this._removeWatchers(null, options.messageManager);
- break;
-
- case "identity-controller-unwatch":
- log("unwatching", options.id);
- this._removeWatchers(options.id, options.messageManager);
- break;
- }
- },
-
- _addWatcher: function Pipe__addWatcher(aId, aMm) {
- log("Adding watcher with id", aId);
- for (let i = 0; i < this._watchers.length; ++i) {
- let watcher = this._watchers[i];
- if (this._watcher.id === aId) {
- watcher.count++;
- return;
- }
- }
- this._watchers.push({id: aId, count: 1, mm: aMm});
- },
-
- _removeWatchers: function Pipe__removeWatcher(aId, aMm) {
- let checkId = aId !== null;
- let index = -1;
- for (let i = 0; i < this._watchers.length; ++i) {
- let watcher = this._watchers[i];
- if (watcher.mm === aMm &&
- (!checkId || (checkId && watcher.id === aId))) {
- index = i;
- break;
- }
- }
-
- if (index !== -1) {
- if (checkId) {
- if (--(this._watchers[index].count) === 0) {
- this._watchers.splice(index, 1);
- }
- } else {
- this._watchers.splice(index, 1);
- }
- }
-
- if (this._watchers.length === 0) {
- log("No more watchers; clean up persona host iframe");
- let detail = {
- type: kCloseIdentityDialog
- };
- log('telling content to close the dialog');
- // tell content to close the dialog
- sendChromeEvent(detail);
- }
- },
-
- communicate: function(aRpOptions, aContentOptions, aMessageCallback) {
- let rpID = aRpOptions.id;
- let rpMM = aRpOptions.mm;
- if (rpMM) {
- this._addWatcher(rpID, rpMM);
- }
-
- log("RP options:", aRpOptions, "\n content options:", aContentOptions);
-
- // This content variable is injected into the scope of
- // kIdentityShimFile, where it is used to access the BrowserID object
- // and its internal API.
- let mm = null;
- let uuid = getRandomId();
- let self = this;
-
- function removeMessageListeners() {
- if (mm) {
- mm.removeMessageListener(kIdentityDelegateFinished, identityDelegateFinished);
- mm.removeMessageListener(kIdentityControllerDoMethod, aMessageCallback);
- }
- }
-
- function identityDelegateFinished() {
- removeMessageListeners();
-
- let detail = {
- type: kDoneIdentityDialog,
- showUI: aContentOptions.showUI || false,
- id: kDoneIdentityDialog + "-" + uuid,
- requestId: aRpOptions.id
- };
- log('received delegate finished; telling content to close the dialog');
- sendChromeEvent(detail);
- self._removeWatchers(rpID, rpMM);
- }
-
- SystemAppProxy.addEventListener("mozContentEvent", function getAssertion(evt) {
- let msg = evt.detail;
- if (!msg.id.match(uuid)) {
- return;
- }
-
- switch (msg.id) {
- case kOpenIdentityDialog + '-' + uuid:
- if (msg.type === 'cancel') {
- // The user closed the dialog. Clean up and call cancel.
- SystemAppProxy.removeEventListener("mozContentEvent", getAssertion);
- removeMessageListeners();
- aMessageCallback({json: {method: "cancel"}});
- } else {
- // The window has opened. Inject the identity shim file containing
- // the callbacks in the content script. This could be either the
- // visible popup that the user interacts with, or it could be an
- // invisible frame.
- let frame = evt.detail.frame;
- let frameLoader = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
- mm = frameLoader.messageManager;
- try {
- mm.loadFrameScript(kIdentityShimFile, true, true);
- log("Loaded shim", kIdentityShimFile);
- } catch (e) {
- log("Error loading", kIdentityShimFile, "as a frame script:", e);
- }
-
- // There are two messages that the delegate can send back: a "do
- // method" event, and a "finished" event. We pass the do-method
- // events straight to the caller for interpretation and handling.
- // If we receive a "finished" event, then the delegate is done, so
- // we shut down the pipe and clean up.
- mm.addMessageListener(kIdentityControllerDoMethod, aMessageCallback);
- mm.addMessageListener(kIdentityDelegateFinished, identityDelegateFinished);
-
- mm.sendAsyncMessage(aContentOptions.message, aRpOptions);
- }
- break;
-
- case kDoneIdentityDialog + '-' + uuid:
- // Received our assertion. The message manager callbacks will handle
- // communicating back to the IDService. All we have to do is remove
- // this listener.
- SystemAppProxy.removeEventListener("mozContentEvent", getAssertion);
- break;
-
- default:
- log("ERROR - Unexpected message: id=" + msg.id + ", type=" + msg.type + ", errorMsg=" + msg.errorMsg);
- break;
- }
-
- });
-
- // Tell content to open the identity iframe or trusty popup. The parameter
- // showUI signals whether user interaction is needed. If it is, content will
- // open a dialog; if not, a hidden iframe. In each case, BrowserID is
- // available in the context.
- let detail = {
- type: kOpenIdentityDialog,
- showUI: aContentOptions.showUI || false,
- id: kOpenIdentityDialog + "-" + uuid,
- requestId: aRpOptions.id
- };
-
- sendChromeEvent(detail);
- }
-
-};
-
-/*
- * The controller sits between the IdentityService used by DOMIdentity
- * and a content process launches an (invisible) iframe or (visible)
- * trusty UI. Using an injected js script (identity.js), the
- * controller enables the content window to access the persona identity
- * storage in the system cookie jar and send events back via the
- * controller into IdentityService and DOM, and ultimately up to the
- * Relying Party, which is open in a different window context.
- */
-this.SignInToWebsiteController = {
-
- /*
- * Initialize the controller. To use a different content communication pipe,
- * such as when mocking it in tests, pass aOptions.pipe.
- */
- init: function SignInToWebsiteController_init(aOptions) {
- aOptions = aOptions || {};
- this.pipe = aOptions.pipe || new Pipe();
- Services.obs.addObserver(this, "identity-controller-watch", false);
- Services.obs.addObserver(this, "identity-controller-request", false);
- Services.obs.addObserver(this, "identity-controller-logout", false);
- },
-
- uninit: function SignInToWebsiteController_uninit() {
- Services.obs.removeObserver(this, "identity-controller-watch");
- Services.obs.removeObserver(this, "identity-controller-request");
- Services.obs.removeObserver(this, "identity-controller-logout");
- },
-
- observe: function SignInToWebsiteController_observe(aSubject, aTopic, aData) {
- log("observe: received", aTopic, "with", aData, "for", aSubject);
- let options = null;
- if (aSubject) {
- options = aSubject.wrappedJSObject;
- }
- switch (aTopic) {
- case "identity-controller-watch":
- this.doWatch(options);
- break;
- case "identity-controller-request":
- this.doRequest(options);
- break;
- case "identity-controller-logout":
- this.doLogout(options);
- break;
- default:
- Logger.reportError("SignInToWebsiteController", "Unknown observer notification:", aTopic);
- break;
- }
- },
-
- /*
- * options: method required - name of method to invoke
- * assertion optional
- */
- _makeDoMethodCallback: function SignInToWebsiteController__makeDoMethodCallback(aRpId) {
- return function SignInToWebsiteController_methodCallback(aOptions) {
- let message = aOptions.json;
- if (typeof message === 'string') {
- message = JSON.parse(message);
- }
-
- switch (message.method) {
- case "ready":
- IdentityService.doReady(aRpId);
- break;
-
- case "login":
- if (message._internalParams) {
- IdentityService.doLogin(aRpId, message.assertion, message._internalParams);
- } else {
- IdentityService.doLogin(aRpId, message.assertion);
- }
- break;
-
- case "logout":
- IdentityService.doLogout(aRpId);
- break;
-
- case "cancel":
- IdentityService.doCancel(aRpId);
- break;
-
- default:
- log("WARNING: wonky method call:", message.method);
- break;
- }
- };
- },
-
- doWatch: function SignInToWebsiteController_doWatch(aRpOptions) {
- // dom prevents watch from being called twice
- let contentOptions = {
- message: kIdentityDelegateWatch,
- showUI: false
- };
- this.pipe.communicate(aRpOptions, contentOptions,
- this._makeDoMethodCallback(aRpOptions.id));
- },
-
- /**
- * The website is requesting login so the user must choose an identity to use.
- */
- doRequest: function SignInToWebsiteController_doRequest(aRpOptions) {
- log("doRequest", aRpOptions);
- let contentOptions = {
- message: kIdentityDelegateRequest,
- showUI: true
- };
- this.pipe.communicate(aRpOptions, contentOptions,
- this._makeDoMethodCallback(aRpOptions.id));
- },
-
- /*
- *
- */
- doLogout: function SignInToWebsiteController_doLogout(aRpOptions) {
- log("doLogout", aRpOptions);
- let contentOptions = {
- message: kIdentityDelegateLogout,
- showUI: false
- };
- this.pipe.communicate(aRpOptions, contentOptions,
- this._makeDoMethodCallback(aRpOptions.id));
- }
-
-};