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