1 /* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
2 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * This alternate implementation of IdentityService provides just the
9 * channels for navigator.id, leaving the certificate storage to a
10 * server-provided app.
12 * On b2g, the messages identity-controller-watch, -request, and
13 * -logout, are observed by the component SignInToWebsite.jsm.
18 this.EXPORTED_SYMBOLS = ["IdentityService"];
20 const Cu = Components.utils;
21 const Ci = Components.interfaces;
22 const Cc = Components.classes;
23 const Cr = Components.results;
25 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
26 Cu.import("resource://gre/modules/Services.jsm");
27 Cu.import("resource://gre/modules/identity/LogUtils.jsm");
29 XPCOMUtils.defineLazyModuleGetter(this, "objectCopy",
30 "resource://gre/modules/identity/IdentityUtils.jsm");
32 XPCOMUtils.defineLazyModuleGetter(this, "makeMessageObject",
33 "resource://gre/modules/identity/IdentityUtils.jsm");
35 function log(...aMessageArgs) {
36 Logger.log.apply(Logger, ["minimal core"].concat(aMessageArgs));
38 function reportError(...aMessageArgs) {
39 Logger.reportError.apply(Logger, ["core"].concat(aMessageArgs));
42 function IDService() {
43 Services.obs.addObserver(this, "quit-application-granted", false);
45 // simplify, it's one object
49 // keep track of flows
55 IDService.prototype = {
56 QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
58 observe: function observe(aSubject, aTopic, aData) {
60 case "quit-application-granted":
66 shutdown: function() {
67 Services.obs.removeObserver(this, "quit-application-granted");
71 * Parse an email into username and domain if it is valid, else return null
73 parseEmail: function parseEmail(email) {
74 var match = email.match(/^([^@]+)@([^@^/]+.[a-z]+)$/);
85 * Register a listener for a given windowID as a result of a call to
86 * navigator.id.watch().
89 * (Object) an object that represents the caller document, and
90 * is expected to have properties:
91 * - id (unique, e.g. uuid)
92 * - loggedInUser (string or null)
95 * and a bunch of callbacks
103 watch: function watch(aRpCaller) {
104 // store the caller structure and notify the UI observers
105 this._rpFlows[aRpCaller.id] = aRpCaller;
107 log("flows:", Object.keys(this._rpFlows).join(', '));
109 let options = makeMessageObject(aRpCaller);
110 log("sending identity-controller-watch:", options);
111 Services.obs.notifyObservers({wrappedJSObject: options},"identity-controller-watch", null);
115 * The RP has gone away; remove handles to the hidden iframe.
116 * It's probable that the frame will already have been cleaned up.
118 unwatch: function unwatch(aRpId, aTargetMM) {
119 let rp = this._rpFlows[aRpId];
124 let options = makeMessageObject({
127 messageManager: aTargetMM
129 log("sending identity-controller-unwatch for id", options.id, options.origin);
130 Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-unwatch", null);
132 // Stop sending messages to this window
133 delete this._rpFlows[aRpId];
137 * Initiate a login with user interaction as a result of a call to
138 * navigator.id.request().
141 * (integer) the id of the doc object obtained in .watch()
144 * (Object) options including privacyPolicy, termsOfService
146 request: function request(aRPId, aOptions) {
147 let rp = this._rpFlows[aRPId];
149 reportError("request() called before watch()");
153 // Notify UX to display identity picker.
154 // Pass the doc id to UX so it can pass it back to us later.
155 let options = makeMessageObject(rp);
156 objectCopy(aOptions, options);
157 Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-request", null);
161 * Invoked when a user wishes to logout of a site (for instance, when clicking
162 * on an in-content logout button).
165 * (integer) the id of the doc object obtained in .watch()
168 logout: function logout(aRpCallerId) {
169 let rp = this._rpFlows[aRpCallerId];
171 reportError("logout() called before watch()");
175 let options = makeMessageObject(rp);
176 Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-logout", null);
179 childProcessShutdown: function childProcessShutdown(messageManager) {
180 Object.keys(this._rpFlows).forEach(function(key) {
181 if (this._rpFlows[key]._mm === messageManager) {
182 log("child process shutdown for rp", key, "- deleting flow");
183 delete this._rpFlows[key];
189 * once the UI-and-display-logic components have received
190 * notifications, they call back with direct invocation of the
191 * following functions (doLogin, doLogout, or doReady)
194 doLogin: function doLogin(aRpCallerId, aAssertion, aInternalParams) {
195 let rp = this._rpFlows[aRpCallerId];
197 log("WARNING: doLogin found no rp to go with callerId " + aRpCallerId);
201 rp.doLogin(aAssertion, aInternalParams);
204 doLogout: function doLogout(aRpCallerId) {
205 let rp = this._rpFlows[aRpCallerId];
207 log("WARNING: doLogout found no rp to go with callerId " + aRpCallerId);
211 // Logout from every site with the same origin
212 let origin = rp.origin;
213 Object.keys(this._rpFlows).forEach(function(key) {
214 let rp = this._rpFlows[key];
215 if (rp.origin === origin) {
221 doReady: function doReady(aRpCallerId) {
222 let rp = this._rpFlows[aRpCallerId];
224 log("WARNING: doReady found no rp to go with callerId " + aRpCallerId);
231 doCancel: function doCancel(aRpCallerId) {
232 let rp = this._rpFlows[aRpCallerId];
234 log("WARNING: doCancel found no rp to go with callerId " + aRpCallerId);
242 this.IdentityService = new IDService();