blob: c0364e8543a014d23320904ba06c5637169613e4 [file] [log] [blame]
// Copyright (c) 2012 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.
/**
* Authenticator class wraps the communications between Gaia and its host.
*/
function Authenticator() {
}
/**
* Gaia auth extension url origin.
* @type {string}
*/
Authenticator.THIS_EXTENSION_ORIGIN =
'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik';
/**
* Singleton getter of Authenticator.
* @return {Object} The singleton instance of Authenticator.
*/
Authenticator.getInstance = function() {
if (!Authenticator.instance_) {
Authenticator.instance_ = new Authenticator();
}
return Authenticator.instance_;
};
Authenticator.prototype = {
email_: null,
password_: null,
attemptToken_: null,
// Input params from extension initialization URL.
inputLang_: undefined,
intputEmail_: undefined,
samlPageLoaded_: false,
samlSupportChannel_: null,
GAIA_URL: 'https://accounts.google.com/',
GAIA_PAGE_PATH: 'ServiceLogin?skipvpage=true&sarp=1&rm=hide',
PARENT_PAGE: 'chrome://oobe/',
SERVICE_ID: 'chromeoslogin',
CONTINUE_URL: Authenticator.THIS_EXTENSION_ORIGIN + '/success.html',
initialize: function() {
var params = getUrlSearchParams(location.search);
this.parentPage_ = params.parentPage || this.PARENT_PAGE;
this.gaiaUrl_ = params.gaiaUrl || this.GAIA_URL;
this.inputLang_ = params.hl;
this.inputEmail_ = params.email;
this.service_ = params.service || this.SERVICE_ID;
this.continueUrl_ = params.continueUrl || this.CONTINUE_URL;
this.continueUrlWithoutParams_ =
this.continueUrl_.substring(0, this.continueUrl_.indexOf('?')) ||
this.continueUrl_;
this.inlineMode_ = params.inlineMode;
document.addEventListener('DOMContentLoaded', this.onPageLoad.bind(this));
document.addEventListener('enableSAML', this.onEnableSAML_.bind(this));
},
isGaiaMessage_: function(msg) {
// Not quite right, but good enough.
return this.gaiaUrl_.indexOf(msg.origin) == 0 ||
this.GAIA_URL.indexOf(msg.origin) == 0;
},
isInternalMessage_: function(msg) {
return msg.origin == Authenticator.THIS_EXTENSION_ORIGIN;
},
isParentMessage_: function(msg) {
return msg.origin == this.parentPage_;
},
getFrameUrl_: function() {
var url = this.gaiaUrl_;
url += this.GAIA_PAGE_PATH +
'&service=' + encodeURIComponent(this.service_) +
'&continue=' + encodeURIComponent(this.continueUrl_);
if (this.inputLang_)
url += '&hl=' + encodeURIComponent(this.inputLang_);
if (this.inputEmail_)
url += '&Email=' + encodeURIComponent(this.inputEmail_);
return url;
},
/** Callback when all loads in the gaia webview is complete. */
onWebviewLoadstop_: function(gaiaFrame) {
// Report the current state to the parent which will then update the
// browser history so that later it could respond properly to back/forward.
var msg = {
'method': 'reportState',
'src': gaiaFrame.src
};
window.parent.postMessage(msg, this.parentPage_);
if (gaiaFrame.src.lastIndexOf(
this.gaiaUrl_ + this.GAIA_PAGE_PATH, 0) == 0) {
gaiaFrame.executeScript({file: 'inline_injected.js'}, function() {
// Send an initial message to gaia so that it has an JavaScript
// reference to the embedder.
gaiaFrame.contentWindow.postMessage('', gaiaFrame.src);
});
this.onLoginUILoaded();
} else if (gaiaFrame.src.lastIndexOf(
this.continueUrlWithoutParams_, 0) == 0) {
// Detect when login is finished by the load stop event of the continue
// URL. Cannot reuse the login complete flow in success.html, because
// webview does not support extension pages yet.
gaiaFrame.hidden = true;
msg = {'method': 'completeLogin'};
window.parent.postMessage(msg, this.parentPage_);
}
},
/**
* Callback when the gaia webview attempts to open a new window.
*/
onWebviewNewWindow_: function(gaiaFrame, e) {
window.open(e.targetUrl, '_blank');
e.window.discard();
},
loadFrame_: function() {
var gaiaFrame = $('gaia-frame');
gaiaFrame.src = this.getFrameUrl_();
if (this.inlineMode_) {
gaiaFrame.addEventListener(
'loadstop', this.onWebviewLoadstop_.bind(this, gaiaFrame));
gaiaFrame.addEventListener(
'newwindow', this.onWebviewNewWindow_.bind(this, gaiaFrame));
}
},
completeLogin: function(username, password) {
var msg = {
'method': 'completeLogin',
'email': username,
'password': password
};
window.parent.postMessage(msg, this.parentPage_);
if (this.samlSupportChannel_)
this.samlSupportChannel_.send({name: 'resetAuth'});
},
onPageLoad: function(e) {
window.addEventListener('message', this.onMessage.bind(this), false);
this.loadFrame_();
},
/**
* Invoked when 'enableSAML' event is received to initialize SAML support.
*/
onEnableSAML_: function() {
this.samlPageLoaded_ = false;
this.samlSupportChannel_ = new Channel();
this.samlSupportChannel_.connect('authMain');
this.samlSupportChannel_.registerMessage(
'onAuthPageLoaded', this.onAuthPageLoaded_.bind(this));
this.samlSupportChannel_.send({
name: 'setGaiaUrl',
gaiaUrl: this.gaiaUrl_
});
},
/**
* Invoked when the background page sends 'onHostedPageLoaded' message.
* @param {!Object} msg Details sent with the message.
*/
onAuthPageLoaded_: function(msg) {
this.samlPageLoaded_ = msg.url.indexOf(this.gaiaUrl_) != 0;
window.parent.postMessage({
'method': 'authPageLoaded',
'isSAML': this.samlPageLoaded_
}, this.parentPage_);
},
onLoginUILoaded: function() {
var msg = {
'method': 'loginUILoaded'
};
window.parent.postMessage(msg, this.parentPage_);
},
onConfirmLogin_: function() {
if (!this.samlPageLoaded_) {
this.completeLogin(this.email_, this.password_);
return;
}
this.samlSupportChannel_.sendWithCallback(
{name: 'getScrapedPasswords'},
function(passwords) {
if (passwords.length == 0) {
window.parent.postMessage(
{method: 'noPassword', email: this.email_},
this.parentPage_);
} else {
window.parent.postMessage({method: 'confirmPassword'},
this.parentPage_);
}
}.bind(this));
},
onVerifyConfirmedPassword_: function(password) {
this.samlSupportChannel_.sendWithCallback(
{name: 'getScrapedPasswords'},
function(passwords) {
for (var i = 0; i < passwords.length; ++i) {
if (passwords[i] == password) {
this.completeLogin(this.email_, passwords[i]);
return;
}
}
window.parent.postMessage({method: 'confirmPassword'},
this.parentPage_);
}.bind(this));
},
onMessage: function(e) {
var msg = e.data;
if (msg.method == 'attemptLogin' && this.isGaiaMessage_(e)) {
this.email_ = msg.email;
this.password_ = msg.password;
this.attemptToken_ = msg.attemptToken;
this.samlPageLoaded_ = false;
if (this.samlSupportChannel_)
this.samlSupportChannel_.send({name: 'startAuth'});
} else if (msg.method == 'clearOldAttempts' && this.isGaiaMessage_(e)) {
this.email_ = null;
this.password_ = null;
this.attemptToken_ = null;
this.samlPageLoaded_ = false;
this.onLoginUILoaded();
if (this.samlSupportChannel_)
this.samlSupportChannel_.send({name: 'resetAuth'});
} else if (msg.method == 'confirmLogin' && this.isInternalMessage_(e)) {
if (this.attemptToken_ == msg.attemptToken)
this.onConfirmLogin_();
else
console.error('Authenticator.onMessage: unexpected attemptToken!?');
} else if (msg.method == 'verifyConfirmedPassword' &&
this.isParentMessage_(e)) {
this.onVerifyConfirmedPassword_(msg.password);
} else if (msg.method == 'navigate' &&
this.isParentMessage_(e)) {
$('gaia-frame').src = msg.src;
} else {
console.error('Authenticator.onMessage: unknown message + origin!?');
}
}
};
Authenticator.getInstance().initialize();