diff options
Diffstat (limited to 'toolkit/identity/IdentityProvider.jsm')
-rw-r--r-- | toolkit/identity/IdentityProvider.jsm | 496 |
1 files changed, 0 insertions, 496 deletions
diff --git a/toolkit/identity/IdentityProvider.jsm b/toolkit/identity/IdentityProvider.jsm deleted file mode 100644 index 11529bfba..000000000 --- a/toolkit/identity/IdentityProvider.jsm +++ /dev/null @@ -1,496 +0,0 @@ -/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* 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/. */ - -"use strict"; - -const Cu = Components.utils; -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/identity/LogUtils.jsm"); -Cu.import("resource://gre/modules/identity/Sandbox.jsm"); - -this.EXPORTED_SYMBOLS = ["IdentityProvider"]; -const FALLBACK_PROVIDER = "browserid.org"; - -XPCOMUtils.defineLazyModuleGetter(this, - "jwcrypto", - "resource://gre/modules/identity/jwcrypto.jsm"); - -function log(...aMessageArgs) { - Logger.log.apply(Logger, ["IDP"].concat(aMessageArgs)); -} -function reportError(...aMessageArgs) { - Logger.reportError.apply(Logger, ["IDP"].concat(aMessageArgs)); -} - - -function IdentityProviderService() { - XPCOMUtils.defineLazyModuleGetter(this, - "_store", - "resource://gre/modules/identity/IdentityStore.jsm", - "IdentityStore"); - - this.reset(); -} - -IdentityProviderService.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]), - _sandboxConfigured: false, - - observe: function observe(aSubject, aTopic, aData) { - switch (aTopic) { - case "quit-application-granted": - Services.obs.removeObserver(this, "quit-application-granted"); - this.shutdown(); - break; - } - }, - - reset: function IDP_reset() { - // Clear the provisioning flows. Provision flows contain an - // identity, idpParams (how to reach the IdP to provision and - // authenticate), a callback (a completion callback for when things - // are done), and a provisioningFrame (which is the provisioning - // sandbox). Additionally, two callbacks will be attached: - // beginProvisioningCallback and genKeyPairCallback. - this._provisionFlows = {}; - - // Clear the authentication flows. Authentication flows attach - // to provision flows. In the process of provisioning an id, it - // may be necessary to authenticate with an IdP. The authentication - // flow maintains the state of that authentication process. - this._authenticationFlows = {}; - }, - - getProvisionFlow: function getProvisionFlow(aProvId, aErrBack) { - let provFlow = this._provisionFlows[aProvId]; - if (provFlow) { - return provFlow; - } - - let err = "No provisioning flow found with id " + aProvId; - log("ERROR:", err); - if (typeof aErrBack === 'function') { - aErrBack(err); - } - - return undefined; - }, - - shutdown: function RP_shutdown() { - this.reset(); - - if (this._sandboxConfigured) { - // Tear down message manager listening on the hidden window - Cu.import("resource://gre/modules/DOMIdentity.jsm"); - DOMIdentity._configureMessages(Services.appShell.hiddenDOMWindow, false); - this._sandboxConfigured = false; - } - - Services.obs.removeObserver(this, "quit-application-granted"); - }, - - get securityLevel() { - return 1; - }, - - get certDuration() { - switch (this.securityLevel) { - default: - return 3600; - } - }, - - /** - * Provision an Identity - * - * @param aIdentity - * (string) the email we're logging in with - * - * @param aIDPParams - * (object) parameters of the IdP - * - * @param aCallback - * (function) callback to invoke on completion - * with first-positional parameter the error. - */ - _provisionIdentity: function _provisionIdentity(aIdentity, aIDPParams, aProvId, aCallback) { - let provPath = aIDPParams.idpParams.provisioning; - let url = Services.io.newURI("https://" + aIDPParams.domain, null, null).resolve(provPath); - log("_provisionIdentity: identity:", aIdentity, "url:", url); - - // If aProvId is not null, then we already have a flow - // with a sandbox. Otherwise, get a sandbox and create a - // new provision flow. - - if (aProvId) { - // Re-use an existing sandbox - log("_provisionIdentity: re-using sandbox in provisioning flow with id:", aProvId); - this._provisionFlows[aProvId].provisioningSandbox.reload(); - - } else { - this._createProvisioningSandbox(url, function createdSandbox(aSandbox) { - // create a provisioning flow, using the sandbox id, and - // stash callback associated with this provisioning workflow. - - let provId = aSandbox.id; - this._provisionFlows[provId] = { - identity: aIdentity, - idpParams: aIDPParams, - securityLevel: this.securityLevel, - provisioningSandbox: aSandbox, - callback: function doCallback(aErr) { - aCallback(aErr, provId); - }, - }; - - log("_provisionIdentity: Created sandbox and provisioning flow with id:", provId); - // XXX bug 769862 - provisioning flow should timeout after N seconds - - }.bind(this)); - } - }, - - // DOM Methods - /** - * the provisioning iframe sandbox has called navigator.id.beginProvisioning() - * - * @param aCaller - * (object) the iframe sandbox caller with all callbacks and - * other information. Callbacks include: - * - doBeginProvisioningCallback(id, duration_s) - * - doGenKeyPairCallback(pk) - */ - beginProvisioning: function beginProvisioning(aCaller) { - log("beginProvisioning:", aCaller.id); - - // Expect a flow for this caller already to be underway. - let provFlow = this.getProvisionFlow(aCaller.id, aCaller.doError); - - // keep the caller object around - provFlow.caller = aCaller; - - let identity = provFlow.identity; - let frame = provFlow.provisioningFrame; - - // Determine recommended length of cert. - let duration = this.certDuration; - - // Make a record that we have begun provisioning. This is required - // for genKeyPair. - provFlow.didBeginProvisioning = true; - - // Let the sandbox know to invoke the callback to beginProvisioning with - // the identity and cert length. - return aCaller.doBeginProvisioningCallback(identity, duration); - }, - - /** - * the provisioning iframe sandbox has called - * navigator.id.raiseProvisioningFailure() - * - * @param aProvId - * (int) the identifier of the provisioning flow tied to that sandbox - * @param aReason - */ - raiseProvisioningFailure: function raiseProvisioningFailure(aProvId, aReason) { - reportError("Provisioning failure", aReason); - - // look up the provisioning caller and its callback - let provFlow = this.getProvisionFlow(aProvId); - - // Sandbox is deleted in _cleanUpProvisionFlow in case we re-use it. - - // This may be either a "soft" or "hard" fail. If it's a - // soft fail, we'll flow through setAuthenticationFlow, where - // the provision flow data will be copied into a new auth - // flow. If it's a hard fail, then the callback will be - // responsible for cleaning up the now defunct provision flow. - - // invoke the callback with an error. - provFlow.callback(aReason); - }, - - /** - * When navigator.id.genKeyPair is called from provisioning iframe sandbox. - * Generates a keypair for the current user being provisioned. - * - * @param aProvId - * (int) the identifier of the provisioning caller tied to that sandbox - * - * It is an error to call genKeypair without receiving the callback for - * the beginProvisioning() call first. - */ - genKeyPair: function genKeyPair(aProvId) { - // Look up the provisioning caller and make sure it's valid. - let provFlow = this.getProvisionFlow(aProvId); - - if (!provFlow.didBeginProvisioning) { - let errStr = "ERROR: genKeyPair called before beginProvisioning"; - log(errStr); - provFlow.callback(errStr); - return; - } - - // Ok generate a keypair - jwcrypto.generateKeyPair(jwcrypto.ALGORITHMS.DS160, function gkpCb(err, kp) { - log("in gkp callback"); - if (err) { - log("ERROR: genKeyPair:", err); - provFlow.callback(err); - return; - } - - provFlow.kp = kp; - - // Serialize the publicKey of the keypair and send it back to the - // sandbox. - log("genKeyPair: generated keypair for provisioning flow with id:", aProvId); - provFlow.caller.doGenKeyPairCallback(provFlow.kp.serializedPublicKey); - }.bind(this)); - }, - - /** - * When navigator.id.registerCertificate is called from provisioning iframe - * sandbox. - * - * Sets the certificate for the user for which a certificate was requested - * via a preceding call to beginProvisioning (and genKeypair). - * - * @param aProvId - * (integer) the identifier of the provisioning caller tied to that - * sandbox - * - * @param aCert - * (String) A JWT representing the signed certificate for the user - * being provisioned, provided by the IdP. - */ - registerCertificate: function registerCertificate(aProvId, aCert) { - log("registerCertificate:", aProvId, aCert); - - // look up provisioning caller, make sure it's valid. - let provFlow = this.getProvisionFlow(aProvId); - - if (!provFlow.caller) { - reportError("registerCertificate", "No provision flow or caller"); - return; - } - if (!provFlow.kp) { - let errStr = "Cannot register a certificate without a keypair"; - reportError("registerCertificate", errStr); - provFlow.callback(errStr); - return; - } - - // store the keypair and certificate just provided in IDStore. - this._store.addIdentity(provFlow.identity, provFlow.kp, aCert); - - // Great success! - provFlow.callback(null); - - // Clean up the flow. - this._cleanUpProvisionFlow(aProvId); - }, - - /** - * Begin the authentication process with an IdP - * - * @param aProvId - * (int) the identifier of the provisioning flow which failed - * - * @param aCallback - * (function) to invoke upon completion, with - * first-positional-param error. - */ - _doAuthentication: function _doAuthentication(aProvId, aIDPParams) { - log("_doAuthentication: provId:", aProvId, "idpParams:", aIDPParams); - // create an authentication caller and its identifier AuthId - // stash aIdentity, idpparams, and callback in it. - - // extract authentication URL from idpParams - let authPath = aIDPParams.idpParams.authentication; - let authURI = Services.io.newURI("https://" + aIDPParams.domain, null, null).resolve(authPath); - - // beginAuthenticationFlow causes the "identity-auth" topic to be - // observed. Since it's sending a notification to the DOM, there's - // no callback. We wait for the DOM to trigger the next phase of - // provisioning. - this._beginAuthenticationFlow(aProvId, authURI); - - // either we bind the AuthID to the sandbox ourselves, or UX does that, - // in which case we need to tell UX the AuthId. - // Currently, the UX creates the UI and gets the AuthId from the window - // and sets is with setAuthenticationFlow - }, - - /** - * The authentication frame has called navigator.id.beginAuthentication - * - * IMPORTANT: the aCaller is *always* non-null, even if this is called from - * a regular content page. We have to make sure, on every DOM call, that - * aCaller is an expected authentication-flow identifier. If not, we throw - * an error or something. - * - * @param aCaller - * (object) the authentication caller - * - */ - beginAuthentication: function beginAuthentication(aCaller) { - log("beginAuthentication: caller id:", aCaller.id); - - // Begin the authentication flow after having concluded a provisioning - // flow. The aCaller that the DOM gives us will have the same ID as - // the provisioning flow we just concluded. (see setAuthenticationFlow) - let authFlow = this._authenticationFlows[aCaller.id]; - if (!authFlow) { - return aCaller.doError("beginAuthentication: no flow for caller id", aCaller.id); - } - - authFlow.caller = aCaller; - - let identity = this._provisionFlows[authFlow.provId].identity; - - // tell the UI to start the authentication process - log("beginAuthentication: authFlow:", aCaller.id, "identity:", identity); - return authFlow.caller.doBeginAuthenticationCallback(identity); - }, - - /** - * The auth frame has called navigator.id.completeAuthentication - * - * @param aAuthId - * (int) the identifier of the authentication caller tied to that sandbox - * - */ - completeAuthentication: function completeAuthentication(aAuthId) { - log("completeAuthentication:", aAuthId); - - // look up the AuthId caller, and get its callback. - let authFlow = this._authenticationFlows[aAuthId]; - if (!authFlow) { - reportError("completeAuthentication", "No auth flow with id", aAuthId); - return; - } - let provId = authFlow.provId; - - // delete caller - delete authFlow['caller']; - delete this._authenticationFlows[aAuthId]; - - let provFlow = this.getProvisionFlow(provId); - provFlow.didAuthentication = true; - let subject = { - rpId: provFlow.rpId, - identity: provFlow.identity, - }; - Services.obs.notifyObservers({ wrappedJSObject: subject }, "identity-auth-complete", aAuthId); - }, - - /** - * The auth frame has called navigator.id.cancelAuthentication - * - * @param aAuthId - * (int) the identifier of the authentication caller - * - */ - cancelAuthentication: function cancelAuthentication(aAuthId) { - log("cancelAuthentication:", aAuthId); - - // look up the AuthId caller, and get its callback. - let authFlow = this._authenticationFlows[aAuthId]; - if (!authFlow) { - reportError("cancelAuthentication", "No auth flow with id:", aAuthId); - return; - } - let provId = authFlow.provId; - - // delete caller - delete authFlow['caller']; - delete this._authenticationFlows[aAuthId]; - - let provFlow = this.getProvisionFlow(provId); - provFlow.didAuthentication = true; - Services.obs.notifyObservers(null, "identity-auth-complete", aAuthId); - - // invoke callback with ERROR. - let errStr = "Authentication canceled by IDP"; - log("ERROR: cancelAuthentication:", errStr); - provFlow.callback(errStr); - }, - - /** - * Called by the UI to set the ID and caller for the authentication flow after it gets its ID - */ - setAuthenticationFlow: function(aAuthId, aProvId) { - // this is the transition point between the two flows, - // provision and authenticate. We tell the auth flow which - // provisioning flow it is started from. - log("setAuthenticationFlow: authId:", aAuthId, "provId:", aProvId); - this._authenticationFlows[aAuthId] = { provId: aProvId }; - this._provisionFlows[aProvId].authId = aAuthId; - }, - - /** - * Load the provisioning URL in a hidden frame to start the provisioning - * process. - */ - _createProvisioningSandbox: function _createProvisioningSandbox(aURL, aCallback) { - log("_createProvisioningSandbox:", aURL); - - if (!this._sandboxConfigured) { - // Configure message manager listening on the hidden window - Cu.import("resource://gre/modules/DOMIdentity.jsm"); - DOMIdentity._configureMessages(Services.appShell.hiddenDOMWindow, true); - this._sandboxConfigured = true; - } - - new Sandbox(aURL, aCallback); - }, - - /** - * Load the authentication UI to start the authentication process. - */ - _beginAuthenticationFlow: function _beginAuthenticationFlow(aProvId, aURL) { - log("_beginAuthenticationFlow:", aProvId, aURL); - let propBag = {provId: aProvId}; - - Services.obs.notifyObservers({wrappedJSObject:propBag}, "identity-auth", aURL); - }, - - /** - * Clean up a provision flow and the authentication flow and sandbox - * that may be attached to it. - */ - _cleanUpProvisionFlow: function _cleanUpProvisionFlow(aProvId) { - log('_cleanUpProvisionFlow:', aProvId); - let prov = this._provisionFlows[aProvId]; - - // Clean up the sandbox, if there is one. - if (prov.provisioningSandbox) { - let sandbox = this._provisionFlows[aProvId]['provisioningSandbox']; - if (sandbox.free) { - log('_cleanUpProvisionFlow: freeing sandbox'); - sandbox.free(); - } - delete this._provisionFlows[aProvId]['provisioningSandbox']; - } - - // Clean up a related authentication flow, if there is one. - if (this._authenticationFlows[prov.authId]) { - delete this._authenticationFlows[prov.authId]; - } - - // Finally delete the provision flow - delete this._provisionFlows[aProvId]; - } - -}; - -this.IdentityProvider = new IdentityProviderService(); |