Home | History | Annotate | Download | only in gaia_auth
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 /**
      6  * @fileoverview
      7  * The background script of auth extension that bridges the communications
      8  * between main and injected script.
      9  * Here are the communications along a SAML sign-in flow:
     10  * 1. Main script sends an 'onAuthStarted' signal to indicate the authentication
     11  *    flow is started and SAML pages might be loaded from now on;
     12  * 2. After the 'onAuthTstarted' signal, injected script starts to scraping
     13  *    all password fields on normal page (i.e. http or https) and sends page
     14  *    load signal as well as the passwords to the background script here;
     15  */
     16 
     17 /**
     18  * BackgroundBridge holds the main script's state and the scraped passwords
     19  * from the injected script to help the two collaborate.
     20  */
     21 function BackgroundBridge() {
     22 }
     23 
     24 BackgroundBridge.prototype = {
     25   // Gaia URL base that is set from main auth script.
     26   gaiaUrl_: null,
     27 
     28   // Whether auth flow has started. It is used as a signal of whether the
     29   // injected script should scrape passwords.
     30   authStarted_: false,
     31 
     32   passwordStore_: {},
     33 
     34   channelMain_: null,
     35   channelInjected_: null,
     36 
     37   run: function() {
     38     chrome.runtime.onConnect.addListener(this.onConnect_.bind(this));
     39 
     40     // Workarounds for loading SAML page in an iframe.
     41     chrome.webRequest.onHeadersReceived.addListener(
     42         function(details) {
     43           if (!this.authStarted_)
     44             return;
     45 
     46           var headers = details.responseHeaders;
     47           for (var i = 0; headers && i < headers.length; ++i) {
     48             if (headers[i].name.toLowerCase() == 'x-frame-options') {
     49               headers.splice(i, 1);
     50               break;
     51             }
     52           }
     53           return {responseHeaders: headers};
     54         }.bind(this),
     55         {urls: ['<all_urls>'], types: ['sub_frame']},
     56         ['blocking', 'responseHeaders']);
     57   },
     58 
     59   onConnect_: function(port) {
     60     if (port.name == 'authMain')
     61       this.setupForAuthMain_(port);
     62     else if (port.name == 'injected')
     63       this.setupForInjected_(port);
     64     else
     65       console.error('Unexpected connection, port.name=' + port.name);
     66   },
     67 
     68   /**
     69    * Sets up the communication channel with the main script.
     70    */
     71   setupForAuthMain_: function(port) {
     72     this.channelMain_ = new Channel();
     73     this.channelMain_.init(port);
     74     this.channelMain_.registerMessage(
     75         'setGaiaUrl', this.onSetGaiaUrl_.bind(this));
     76     this.channelMain_.registerMessage(
     77         'resetAuth', this.onResetAuth_.bind(this));
     78     this.channelMain_.registerMessage(
     79         'startAuth', this.onAuthStarted_.bind(this));
     80     this.channelMain_.registerMessage(
     81         'getScrapedPasswords',
     82         this.onGetScrapedPasswords_.bind(this));
     83   },
     84 
     85   /**
     86    * Sets up the communication channel with the injected script.
     87    */
     88   setupForInjected_: function(port) {
     89     this.channelInjected_ = new Channel();
     90     this.channelInjected_.init(port);
     91     this.channelInjected_.registerMessage(
     92         'updatePassword', this.onUpdatePassword_.bind(this));
     93     this.channelInjected_.registerMessage(
     94         'pageLoaded', this.onPageLoaded_.bind(this));
     95   },
     96 
     97   /**
     98    * Handler for 'setGaiaUrl' signal sent from the main script.
     99    */
    100   onSetGaiaUrl_: function(msg) {
    101     this.gaiaUrl_ = msg.gaiaUrl;
    102 
    103     // Set request header to let Gaia know that saml support is on.
    104     chrome.webRequest.onBeforeSendHeaders.addListener(
    105         function(details) {
    106           details.requestHeaders.push({
    107             name: 'X-Cros-Auth-Ext-Support',
    108             value: 'SAML'
    109           });
    110           return {requestHeaders: details.requestHeaders};
    111         },
    112         {urls: [this.gaiaUrl_ + '*'], types: ['sub_frame']},
    113         ['blocking', 'requestHeaders']);
    114   },
    115 
    116   /**
    117    * Handler for 'resetAuth' signal sent from the main script.
    118    */
    119   onResetAuth_: function() {
    120     this.authStarted_ = false;
    121     this.passwordStore_ = {};
    122   },
    123 
    124   /**
    125    * Handler for 'authStarted' signal sent from the main script.
    126    */
    127   onAuthStarted_: function() {
    128     this.authStarted_ = true;
    129     this.passwordStore_ = {};
    130   },
    131 
    132   /**
    133    * Handler for 'getScrapedPasswords' request sent from the main script.
    134    * @return {Array.<string>} The array with de-duped scraped passwords.
    135    */
    136   onGetScrapedPasswords_: function() {
    137     var passwords = {};
    138     for (var property in this.passwordStore_) {
    139       passwords[this.passwordStore_[property]] = true;
    140     }
    141     return Object.keys(passwords);
    142   },
    143 
    144   onUpdatePassword_: function(msg) {
    145     if (!this.authStarted_)
    146       return;
    147 
    148     this.passwordStore_[msg.id] = msg.password;
    149   },
    150 
    151   onPageLoaded_: function(msg) {
    152     this.channelMain_.send({name: 'onAuthPageLoaded', url: msg.url});
    153   }
    154 };
    155 
    156 var backgroundBridge = new BackgroundBridge();
    157 backgroundBridge.run();
    158