| Index: chrome/browser/resources/gaia_auth/background.js
|
| diff --git a/chrome/browser/resources/gaia_auth/background.js b/chrome/browser/resources/gaia_auth/background.js
|
| deleted file mode 100644
|
| index 160b31268ef8d9dca12e875bfc7d958bd866026f..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/resources/gaia_auth/background.js
|
| +++ /dev/null
|
| @@ -1,463 +0,0 @@
|
| -// Copyright 2013 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -/**
|
| - * @fileoverview
|
| - * A background script of the auth extension that bridges the communication
|
| - * between the main and injected scripts.
|
| - *
|
| - * Here is an overview of the communication flow when SAML is being used:
|
| - * 1. The main script sends the |startAuth| signal to this background script,
|
| - * indicating that the authentication flow has started and SAML pages may be
|
| - * loaded from now on.
|
| - * 2. A script is injected into each SAML page. The injected script sends three
|
| - * main types of messages to this background script:
|
| - * a) A |pageLoaded| message is sent when the page has been loaded. This is
|
| - * forwarded to the main script as |onAuthPageLoaded|.
|
| - * b) If the SAML provider supports the credential passing API, the API calls
|
| - * are sent to this background script as |apiCall| messages. These
|
| - * messages are forwarded unmodified to the main script.
|
| - * c) The injected script scrapes passwords. They are sent to this background
|
| - * script in |updatePassword| messages. The main script can request a list
|
| - * of the scraped passwords by sending the |getScrapedPasswords| message.
|
| - */
|
| -
|
| -/**
|
| - * BackgroundBridgeManager maintains an array of BackgroundBridge, indexed by
|
| - * the associated tab id.
|
| - */
|
| -function BackgroundBridgeManager() {
|
| - this.bridges_ = {};
|
| -}
|
| -
|
| -BackgroundBridgeManager.prototype = {
|
| - CONTINUE_URL_BASE: 'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik' +
|
| - '/success.html',
|
| - // Maps a tab id to its associated BackgroundBridge.
|
| - bridges_: null,
|
| -
|
| - run: function() {
|
| - chrome.runtime.onConnect.addListener(this.onConnect_.bind(this));
|
| -
|
| - chrome.webRequest.onBeforeRequest.addListener(
|
| - function(details) {
|
| - if (this.bridges_[details.tabId])
|
| - return this.bridges_[details.tabId].onInsecureRequest(details.url);
|
| - }.bind(this),
|
| - {urls: ['http://*/*', 'file://*/*', 'ftp://*/*']},
|
| - ['blocking']);
|
| -
|
| - chrome.webRequest.onBeforeSendHeaders.addListener(
|
| - function(details) {
|
| - if (this.bridges_[details.tabId])
|
| - return this.bridges_[details.tabId].onBeforeSendHeaders(details);
|
| - else
|
| - return {requestHeaders: details.requestHeaders};
|
| - }.bind(this),
|
| - {urls: ['*://*/*'], types: ['sub_frame']},
|
| - ['blocking', 'requestHeaders']);
|
| -
|
| - chrome.webRequest.onHeadersReceived.addListener(
|
| - function(details) {
|
| - if (this.bridges_[details.tabId])
|
| - return this.bridges_[details.tabId].onHeadersReceived(details);
|
| - }.bind(this),
|
| - {urls: ['*://*/*'], types: ['sub_frame']},
|
| - ['blocking', 'responseHeaders']);
|
| -
|
| - chrome.webRequest.onCompleted.addListener(
|
| - function(details) {
|
| - if (this.bridges_[details.tabId])
|
| - this.bridges_[details.tabId].onCompleted(details);
|
| - }.bind(this),
|
| - {urls: ['*://*/*', this.CONTINUE_URL_BASE + '*'], types: ['sub_frame']},
|
| - ['responseHeaders']);
|
| - },
|
| -
|
| - onConnect_: function(port) {
|
| - var tabId = this.getTabIdFromPort_(port);
|
| - if (!this.bridges_[tabId])
|
| - this.bridges_[tabId] = new BackgroundBridge(tabId);
|
| - if (port.name == 'authMain') {
|
| - this.bridges_[tabId].setupForAuthMain(port);
|
| - port.onDisconnect.addListener(function() {
|
| - delete this.bridges_[tabId];
|
| - }.bind(this));
|
| - } else if (port.name == 'injected') {
|
| - this.bridges_[tabId].setupForInjected(port);
|
| - } else {
|
| - console.error('Unexpected connection, port.name=' + port.name);
|
| - }
|
| - },
|
| -
|
| - getTabIdFromPort_: function(port) {
|
| - return port.sender.tab ? port.sender.tab.id : -1;
|
| - }
|
| -};
|
| -
|
| -/**
|
| - * BackgroundBridge allows the main script and the injected script to
|
| - * collaborate. It forwards credentials API calls to the main script and
|
| - * maintains a list of scraped passwords.
|
| - * @param {string} tabId The associated tab ID.
|
| - */
|
| -function BackgroundBridge(tabId) {
|
| - this.tabId_ = tabId;
|
| - this.passwordStore_ = {};
|
| -}
|
| -
|
| -BackgroundBridge.prototype = {
|
| - // The associated tab ID. Only used for debugging now.
|
| - tabId: null,
|
| -
|
| - // The initial URL loaded in the gaia iframe. We only want to handle
|
| - // onCompleted() for the frame that loaded this URL.
|
| - initialFrameUrlWithoutParams: null,
|
| -
|
| - // On process onCompleted() requests that come from this frame Id.
|
| - frameId: -1,
|
| -
|
| - isDesktopFlow_: false,
|
| -
|
| - // Whether the extension is loaded in a constrained window.
|
| - // Set from main auth script.
|
| - isConstrainedWindow_: null,
|
| -
|
| - // Email of the newly authenticated user based on the gaia response header
|
| - // 'google-accounts-signin'.
|
| - email_: null,
|
| -
|
| - // Gaia Id of the newly authenticated user based on the gaia response
|
| - // header 'google-accounts-signin'.
|
| - gaiaId_: null,
|
| -
|
| - // Session index of the newly authenticated user based on the gaia response
|
| - // header 'google-accounts-signin'.
|
| - sessionIndex_: null,
|
| -
|
| - // Gaia URL base that is set from main auth script.
|
| - gaiaUrl_: null,
|
| -
|
| - // Whether to abort the authentication flow and show an error messagen when
|
| - // content served over an unencrypted connection is detected.
|
| - blockInsecureContent_: false,
|
| -
|
| - // Whether auth flow has started. It is used as a signal of whether the
|
| - // injected script should scrape passwords.
|
| - authStarted_: false,
|
| -
|
| - // Whether SAML flow is going.
|
| - isSAML_: false,
|
| -
|
| - passwordStore_: null,
|
| -
|
| - channelMain_: null,
|
| - channelInjected_: null,
|
| -
|
| - /**
|
| - * Sets up the communication channel with the main script.
|
| - */
|
| - setupForAuthMain: function(port) {
|
| - this.channelMain_ = new Channel();
|
| - this.channelMain_.init(port);
|
| -
|
| - // Registers for desktop related messages.
|
| - this.channelMain_.registerMessage(
|
| - 'initDesktopFlow', this.onInitDesktopFlow_.bind(this));
|
| -
|
| - // Registers for SAML related messages.
|
| - this.channelMain_.registerMessage(
|
| - 'setGaiaUrl', this.onSetGaiaUrl_.bind(this));
|
| - this.channelMain_.registerMessage(
|
| - 'setBlockInsecureContent', this.onSetBlockInsecureContent_.bind(this));
|
| - this.channelMain_.registerMessage(
|
| - 'resetAuth', this.onResetAuth_.bind(this));
|
| - this.channelMain_.registerMessage(
|
| - 'startAuth', this.onAuthStarted_.bind(this));
|
| - this.channelMain_.registerMessage(
|
| - 'getScrapedPasswords',
|
| - this.onGetScrapedPasswords_.bind(this));
|
| - this.channelMain_.registerMessage(
|
| - 'apiResponse', this.onAPIResponse_.bind(this));
|
| -
|
| - this.channelMain_.send({
|
| - 'name': 'channelConnected'
|
| - });
|
| - },
|
| -
|
| - /**
|
| - * Sets up the communication channel with the injected script.
|
| - */
|
| - setupForInjected: function(port) {
|
| - this.channelInjected_ = new Channel();
|
| - this.channelInjected_.init(port);
|
| -
|
| - this.channelInjected_.registerMessage(
|
| - 'apiCall', this.onAPICall_.bind(this));
|
| - this.channelInjected_.registerMessage(
|
| - 'updatePassword', this.onUpdatePassword_.bind(this));
|
| - this.channelInjected_.registerMessage(
|
| - 'pageLoaded', this.onPageLoaded_.bind(this));
|
| - this.channelInjected_.registerMessage(
|
| - 'getSAMLFlag', this.onGetSAMLFlag_.bind(this));
|
| - },
|
| -
|
| - /**
|
| - * Handler for 'initDesktopFlow' signal sent from the main script.
|
| - * Only called in desktop mode.
|
| - */
|
| - onInitDesktopFlow_: function(msg) {
|
| - this.isDesktopFlow_ = true;
|
| - this.gaiaUrl_ = msg.gaiaUrl;
|
| - this.isConstrainedWindow_ = msg.isConstrainedWindow;
|
| - this.initialFrameUrlWithoutParams = msg.initialFrameUrlWithoutParams;
|
| - },
|
| -
|
| - /**
|
| - * Handler for webRequest.onCompleted. It 1) detects loading of continue URL
|
| - * and notifies the main script of signin completion; 2) detects if the
|
| - * current page could be loaded in a constrained window and signals the main
|
| - * script of switching to full tab if necessary.
|
| - */
|
| - onCompleted: function(details) {
|
| - // Only monitors requests in the gaia frame. The gaia frame is the one
|
| - // where the initial frame URL completes.
|
| - if (details.url.lastIndexOf(
|
| - this.initialFrameUrlWithoutParams, 0) == 0) {
|
| - this.frameId = details.frameId;
|
| - }
|
| - if (this.frameId == -1) {
|
| - // If for some reason the frameId could not be set above, just make sure
|
| - // the frame is more than two levels deep (since the gaia frame is at
|
| - // least three levels deep).
|
| - if (details.parentFrameId <= 0)
|
| - return;
|
| - } else if (details.frameId != this.frameId) {
|
| - return;
|
| - }
|
| -
|
| - if (details.url.lastIndexOf(backgroundBridgeManager.CONTINUE_URL_BASE, 0) ==
|
| - 0) {
|
| - var skipForNow = false;
|
| - if (details.url.indexOf('ntp=1') >= 0)
|
| - skipForNow = true;
|
| -
|
| - // TOOD(guohui): For desktop SAML flow, show password confirmation UI.
|
| - var passwords = this.onGetScrapedPasswords_();
|
| - var msg = {
|
| - 'name': 'completeLogin',
|
| - 'email': this.email_,
|
| - 'gaiaId': this.gaiaId_,
|
| - 'password': passwords[0],
|
| - 'sessionIndex': this.sessionIndex_,
|
| - 'skipForNow': skipForNow
|
| - };
|
| - this.channelMain_.send(msg);
|
| - } else if (this.isConstrainedWindow_) {
|
| - // The header google-accounts-embedded is only set on gaia domain.
|
| - if (this.gaiaUrl_ && details.url.lastIndexOf(this.gaiaUrl_) == 0) {
|
| - var headers = details.responseHeaders;
|
| - for (var i = 0; headers && i < headers.length; ++i) {
|
| - if (headers[i].name.toLowerCase() == 'google-accounts-embedded')
|
| - return;
|
| - }
|
| - }
|
| - var msg = {
|
| - 'name': 'switchToFullTab',
|
| - 'url': details.url
|
| - };
|
| - this.channelMain_.send(msg);
|
| - }
|
| - },
|
| -
|
| - /**
|
| - * Handler for webRequest.onBeforeRequest, invoked when content served over an
|
| - * unencrypted connection is detected. Determines whether the request should
|
| - * be blocked and if so, signals that an error message needs to be shown.
|
| - * @param {string} url The URL that was blocked.
|
| - * @return {!Object} Decision whether to block the request.
|
| - */
|
| - onInsecureRequest: function(url) {
|
| - if (!this.blockInsecureContent_)
|
| - return {};
|
| - this.channelMain_.send({name: 'onInsecureContentBlocked', url: url});
|
| - return {cancel: true};
|
| - },
|
| -
|
| - /**
|
| - * Handler or webRequest.onHeadersReceived. It reads the authenticated user
|
| - * email from google-accounts-signin-header.
|
| - * @return {!Object} Modified request headers.
|
| - */
|
| - onHeadersReceived: function(details) {
|
| - var headers = details.responseHeaders;
|
| -
|
| - if (this.gaiaUrl_ && details.url.lastIndexOf(this.gaiaUrl_) == 0) {
|
| - for (var i = 0; headers && i < headers.length; ++i) {
|
| - if (headers[i].name.toLowerCase() == 'google-accounts-signin') {
|
| - var headerValues = headers[i].value.toLowerCase().split(',');
|
| - var signinDetails = {};
|
| - headerValues.forEach(function(e) {
|
| - var pair = e.split('=');
|
| - signinDetails[pair[0].trim()] = pair[1].trim();
|
| - });
|
| - // Remove "" around.
|
| - this.email_ = signinDetails['email'].slice(1, -1);
|
| - this.gaiaId_ = signinDetails['obfuscatedid'].slice(1, -1);
|
| - this.sessionIndex_ = signinDetails['sessionindex'];
|
| - break;
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (!this.isDesktopFlow_) {
|
| - // Check whether GAIA headers indicating the start or end of a SAML
|
| - // redirect are present. If so, synthesize cookies to mark these points.
|
| - for (var i = 0; headers && i < headers.length; ++i) {
|
| - if (headers[i].name.toLowerCase() == 'google-accounts-saml') {
|
| - var action = headers[i].value.toLowerCase();
|
| - if (action == 'start') {
|
| - this.isSAML_ = true;
|
| - // GAIA is redirecting to a SAML IdP. Any cookies contained in the
|
| - // current |headers| were set by GAIA. Any cookies set in future
|
| - // requests will be coming from the IdP. Append a cookie to the
|
| - // current |headers| that marks the point at which the redirect
|
| - // occurred.
|
| - headers.push({name: 'Set-Cookie',
|
| - value: 'google-accounts-saml-start=now'});
|
| - return {responseHeaders: headers};
|
| - } else if (action == 'end') {
|
| - this.isSAML_ = false;
|
| - // The SAML IdP has redirected back to GAIA. Add a cookie that marks
|
| - // the point at which the redirect occurred occurred. It is
|
| - // important that this cookie be prepended to the current |headers|
|
| - // because any cookies contained in the |headers| were already set
|
| - // by GAIA, not the IdP. Due to limitations in the webRequest API,
|
| - // it is not trivial to prepend a cookie:
|
| - //
|
| - // The webRequest API only allows for deleting and appending
|
| - // headers. To prepend a cookie (C), three steps are needed:
|
| - // 1) Delete any headers that set cookies (e.g., A, B).
|
| - // 2) Append a header which sets the cookie (C).
|
| - // 3) Append the original headers (A, B).
|
| - //
|
| - // Due to a further limitation of the webRequest API, it is not
|
| - // possible to delete a header in step 1) and append an identical
|
| - // header in step 3). To work around this, a trailing semicolon is
|
| - // added to each header before appending it. Trailing semicolons are
|
| - // ignored by Chrome in cookie headers, causing the modified headers
|
| - // to actually set the original cookies.
|
| - var otherHeaders = [];
|
| - var cookies = [{name: 'Set-Cookie',
|
| - value: 'google-accounts-saml-end=now'}];
|
| - for (var j = 0; j < headers.length; ++j) {
|
| - if (headers[j].name.toLowerCase().startsWith('set-cookie')) {
|
| - var header = headers[j];
|
| - header.value += ';';
|
| - cookies.push(header);
|
| - } else {
|
| - otherHeaders.push(headers[j]);
|
| - }
|
| - }
|
| - return {responseHeaders: otherHeaders.concat(cookies)};
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - return {};
|
| - },
|
| -
|
| - /**
|
| - * Handler for webRequest.onBeforeSendHeaders.
|
| - * @return {!Object} Modified request headers.
|
| - */
|
| - onBeforeSendHeaders: function(details) {
|
| - if (!this.isDesktopFlow_ && this.gaiaUrl_ &&
|
| - details.url.startsWith(this.gaiaUrl_)) {
|
| - details.requestHeaders.push({
|
| - name: 'X-Cros-Auth-Ext-Support',
|
| - value: 'SAML'
|
| - });
|
| - }
|
| - return {requestHeaders: details.requestHeaders};
|
| - },
|
| -
|
| - /**
|
| - * Handler for 'setGaiaUrl' signal sent from the main script.
|
| - */
|
| - onSetGaiaUrl_: function(msg) {
|
| - this.gaiaUrl_ = msg.gaiaUrl;
|
| - },
|
| -
|
| - /**
|
| - * Handler for 'setBlockInsecureContent' signal sent from the main script.
|
| - */
|
| - onSetBlockInsecureContent_: function(msg) {
|
| - this.blockInsecureContent_ = msg.blockInsecureContent;
|
| - },
|
| -
|
| - /**
|
| - * Handler for 'resetAuth' signal sent from the main script.
|
| - */
|
| - onResetAuth_: function() {
|
| - this.authStarted_ = false;
|
| - this.passwordStore_ = {};
|
| - this.isSAML_ = false;
|
| - },
|
| -
|
| - /**
|
| - * Handler for 'authStarted' signal sent from the main script.
|
| - */
|
| - onAuthStarted_: function() {
|
| - this.authStarted_ = true;
|
| - this.passwordStore_ = {};
|
| - this.isSAML_ = false;
|
| - },
|
| -
|
| - /**
|
| - * Handler for 'getScrapedPasswords' request sent from the main script.
|
| - * @return {Array<string>} The array with de-duped scraped passwords.
|
| - */
|
| - onGetScrapedPasswords_: function() {
|
| - var passwords = {};
|
| - for (var property in this.passwordStore_) {
|
| - passwords[this.passwordStore_[property]] = true;
|
| - }
|
| - return Object.keys(passwords);
|
| - },
|
| -
|
| - /**
|
| - * Handler for 'apiResponse' signal sent from the main script. Passes on the
|
| - * |msg| to the injected script.
|
| - */
|
| - onAPIResponse_: function(msg) {
|
| - this.channelInjected_.send(msg);
|
| - },
|
| -
|
| - onAPICall_: function(msg) {
|
| - this.channelMain_.send(msg);
|
| - },
|
| -
|
| - onUpdatePassword_: function(msg) {
|
| - if (!this.authStarted_)
|
| - return;
|
| -
|
| - this.passwordStore_[msg.id] = msg.password;
|
| - },
|
| -
|
| - onPageLoaded_: function(msg) {
|
| - if (this.channelMain_)
|
| - this.channelMain_.send({name: 'onAuthPageLoaded',
|
| - url: msg.url,
|
| - isSAMLPage: this.isSAML_});
|
| - },
|
| -
|
| - onGetSAMLFlag_: function(msg) {
|
| - return this.isSAML_;
|
| - }
|
| -};
|
| -
|
| -var backgroundBridgeManager = new BackgroundBridgeManager();
|
| -backgroundBridgeManager.run();
|
|
|