Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(176)

Unified Diff: chrome/browser/resources/gaia_auth_host/authenticator.js

Issue 1004753004: cros: Port SAML support to webview sign-in. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: handle loadabort to fix test Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/gaia_auth_host/authenticator.js
diff --git a/chrome/browser/resources/gaia_auth_host/authenticator.js b/chrome/browser/resources/gaia_auth_host/authenticator.js
index a1296a4758019dc885983f75563788d15ebcd7f6..e2dc8b69d3b1f5ff64cea31a317d3d5f3eb9ff6e 100644
--- a/chrome/browser/resources/gaia_auth_host/authenticator.js
+++ b/chrome/browser/resources/gaia_auth_host/authenticator.js
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+<include src="saml_handler.js">
+
/**
* @fileoverview An UI component to authenciate to Chrome. The component hosts
* IdP web pages in a webview. A client who is interested in monitoring
@@ -9,6 +11,7 @@
* cr.login.GaiaAuthHost.Listener as defined in this file. After initialization,
* call {@code load} to start the authentication flow.
*/
+
cr.define('cr.login', function() {
'use strict';
@@ -21,7 +24,6 @@ cr.define('cr.login', function() {
'chrome-extension://mfffpogegjflfpflabcdkioaeobkgjik/success.html';
var SIGN_IN_HEADER = 'google-accounts-signin';
var EMBEDDED_FORM_HEADER = 'google-accounts-embedded';
- var SAML_HEADER = 'google-accounts-saml';
var LOCATION_HEADER = 'location';
var SET_COOKIE_HEADER = 'set-cookie';
var OAUTH_CODE_COOKIE = 'oauth_code';
@@ -85,7 +87,7 @@ cr.define('cr.login', function() {
this.sessionIndex_ = null;
this.chooseWhatToSync_ = false;
this.skipForNow_ = false;
- this.authFlow_ = AuthFlow.DEFAULT;
+ this.authFlow = AuthFlow.DEFAULT;
this.loaded_ = false;
this.idpOrigin_ = null;
this.continueUrl_ = null;
@@ -95,6 +97,26 @@ cr.define('cr.login', function() {
this.trusted_ = true;
this.oauth_code_ = null;
+ this.samlHandler_ = new cr.login.SamlHandler(this.webview_);
+ this.confirmPasswordCallback = null;
+ this.noPasswordCallback = null;
+ this.insecureContentBlockedCallback = null;
+ this.samlApiUsedCallback = null;
+ this.missingGaiaInfoCallback = null;
+ this.needPassword = true;
+ this.samlHandler_.addEventListener(
+ 'insecureContentBlocked',
+ this.onInsecureContentBlocked_.bind(this));
+ this.samlHandler_.addEventListener(
+ 'authPageLoaded',
+ this.onAuthPageLoaded_.bind(this));
+ Object.defineProperty(this, 'authDomain', {
+ get: (function() {
+ return this.samlHandler_.authDomain;
+ }).bind(this),
+ enumerable: true
+ });
+
this.webview_.addEventListener('droplink', this.onDropLink_.bind(this));
this.webview_.addEventListener(
'newwindow', this.onNewWindow_.bind(this));
@@ -120,8 +142,6 @@ cr.define('cr.login', function() {
'popstate', this.onPopState_.bind(this), false);
}
- // TODO(guohui,xiyuan): no need to inherit EventTarget once we deprecate the
- // old event-based signin flow.
Authenticator.prototype = Object.create(cr.EventTarget.prototype);
/**
@@ -140,7 +160,12 @@ cr.define('cr.login', function() {
this.initialFrameUrl_ = this.constructInitialFrameUrl_(data);
this.reloadUrl_ = data.frameUrl || this.initialFrameUrl_;
- this.authFlow_ = AuthFlow.DEFAULT;
+ this.authFlow = AuthFlow.DEFAULT;
+ this.samlHandler_.reset();
+ // Don't block insecure content for desktop flow because it lands on
+ // http. Otherwise, block insecure content as long as gaia is https.
+ this.samlHandler_.blockInsecureContent = authMode != AuthMode.DESKTOP &&
+ this.idpOrigin_.indexOf('https://') == 0;
this.webview_.src = this.reloadUrl_;
@@ -152,7 +177,8 @@ cr.define('cr.login', function() {
*/
Authenticator.prototype.reload = function() {
this.webview_.src = this.reloadUrl_;
- this.authFlow_ = AuthFlow.DEFAULT;
+ this.authFlow = AuthFlow.DEFAULT;
+ this.samlHandler_.reset();
this.loaded_ = false;
};
@@ -190,7 +216,7 @@ cr.define('cr.login', function() {
if (currentUrl.indexOf('ntp=1') >= 0)
this.skipForNow_ = true;
- this.onAuthCompleted_();
+ this.maybeCompleteAuth_();
return;
}
@@ -215,7 +241,6 @@ cr.define('cr.login', function() {
}
this.updateHistoryState_(currentUrl);
-
};
/**
@@ -278,8 +303,6 @@ cr.define('cr.login', function() {
this.email_ = signinDetails['email'].slice(1, -1);
this.gaiaId_ = signinDetails['obfuscatedid'].slice(1, -1);
this.sessionIndex_ = signinDetails['sessionindex'];
- } else if (headerName == SAML_HEADER) {
- this.authFlow_ = AuthFlow.SAML;
} else if (headerName == LOCATION_HEADER) {
// If the "choose what to sync" checkbox was clicked, then the continue
// URL will contain a source=3 field.
@@ -306,6 +329,11 @@ cr.define('cr.login', function() {
return;
}
+ // Gaia messages must be an object with 'method' property.
+ if (typeof e.data != 'object' || !e.data.hasOwnProperty('method')) {
+ return;
+ }
+
var msg = e.data;
if (msg.method == 'attemptLogin') {
this.email_ = msg.email;
@@ -321,15 +349,77 @@ cr.define('cr.login', function() {
};
/**
- * Invoked to process authentication completion.
+ * Invoked by the hosting page to verify the Saml password.
+ */
+ Authenticator.prototype.verifyConfirmedPassword = function(password) {
+ if (!this.samlHandler_.verifyConfirmedPassword(password)) {
+ // Invoke confirm password callback asynchronously because the
+ // verification was based on messages and caller (GaiaSigninScreen)
+ // does not expect it to be called immediately.
+ // TODO(xiyuan): Change to synchronous call when iframe based code
+ // is removed.
+ var invokeConfirmPassword = (function() {
+ this.confirmPasswordCallback(this.samlHandler_.scrapedPasswordCount);
+ }).bind(this);
+ window.setTimeout(invokeConfirmPassword, 0);
+ return;
+ }
+
+ this.password_ = password;
+ this.onAuthCompleted_();
+ };
+
+ /**
+ * Check Saml flow and start password confirmation flow if needed. Otherwise,
+ * continue with auto completion.
* @private
*/
- Authenticator.prototype.onAuthCompleted_ = function() {
- if (!this.email_ && !this.skipForNow_) {
+ Authenticator.prototype.maybeCompleteAuth_ = function() {
+ var missingGaiaInfo = !this.email_ || !this.gaiaId_ || !this.sessionIndex_;
+ if (missingGaiaInfo && !this.skipForNow_) {
+ if (this.missingGaiaInfoCallback)
+ this.missingGaiaInfoCallback();
+
this.webview_.src = this.initialFrameUrl_;
return;
}
+ if (this.authFlow != AuthFlow.SAML) {
+ this.onAuthCompleted_();
+ return;
+ }
+
+ if (this.samlHandler_.samlApiUsed) {
+ if (this.samlApiUsedCallback) {
+ this.samlApiUsedCallback();
+ }
+ this.password_ = this.samlHandler_.apiPasswordBytes;
+ } else if (this.samlHandler_.scrapedPasswordCount == 0) {
+ if (this.noPasswordCallback) {
+ this.noPasswordCallback(this.email_);
+ } else {
+ console.error('Authenticator: No password scraped for SAML.');
+ }
+ return;
+ } else if (this.needPassword) {
+ if (this.confirmPasswordCallback) {
+ // Confirm scraped password. The flow follows in
+ // verifyConfirmedPassword.
+ this.confirmPasswordCallback(this.samlHandler_.scrapedPasswordCount);
+ return;
+ }
+ }
+
+ this.onAuthCompleted_();
+ };
+
+ /**
+ * Invoked to process authentication completion.
+ * @private
+ */
+ Authenticator.prototype.onAuthCompleted_ = function() {
+ assert(this.skipForNow_ ||
+ (this.email_ && this.gaiaId_ && this.sessionIndex_));
this.dispatchEvent(
new CustomEvent('authCompleted',
// TODO(rsorokin): get rid of the stub values.
@@ -337,7 +427,7 @@ cr.define('cr.login', function() {
gaiaId: this.gaiaId_ || '',
password: this.password_ || '',
authCode: this.oauth_code_,
- usingSAML: this.authFlow_ == AuthFlow.SAML,
+ usingSAML: this.authFlow == AuthFlow.SAML,
chooseWhatToSync: this.chooseWhatToSync_,
skipForNow: this.skipForNow_,
sessionIndex: this.sessionIndex_ || '',
@@ -345,6 +435,34 @@ cr.define('cr.login', function() {
};
/**
+ * Invoked when |samlHandler_| fires 'insecureContentBlocked' event.
+ * @private
+ */
+ Authenticator.prototype.onInsecureContentBlocked_ = function(e) {
+ if (this.insecureContentBlockedCallback) {
+ this.insecureContentBlockedCallback(e.detail.url);
+ } else {
+ console.error('Authenticator: Insecure content blocked.');
+ }
+ };
+
+ /**
+ * Invoked when |samlHandler_| fires 'authPageLoaded' event.
+ * @private
+ */
+ Authenticator.prototype.onAuthPageLoaded_ = function(e) {
+ if (!e.detail.isSAMLPage)
+ return;
+
+ if (this.authFlow != AuthFlow.SAML) {
+ this.authFlow = AuthFlow.SAML;
+ } else {
+ // Force an authFlowChanged event to update UI with updated auth doamin.
+ cr.dispatchPropertyChange(this, 'authFlow');
+ }
+ };
+
+ /**
* Invoked when a link is dropped on the webview.
* @private
*/
@@ -393,13 +511,18 @@ cr.define('cr.login', function() {
* @private
*/
Authenticator.prototype.onLoadCommit_ = function(e) {
- // TODO(rsorokin): Investigate whether this breaks SAML.
if (this.oauth_code_) {
this.skipForNow_ = true;
- this.onAuthCompleted_();
+ this.maybeCompleteAuth_();
}
};
+ /**
+ * The current auth flow of the hosted auth page.
+ * @type {AuthFlow}
+ */
+ cr.defineProperty(Authenticator, 'authFlow');
+
Authenticator.AuthFlow = AuthFlow;
Authenticator.AuthMode = AuthMode;
Authenticator.SUPPORTED_PARAMS = SUPPORTED_PARAMS;
« no previous file with comments | « chrome/browser/resources/gaia_auth/saml_injected.js ('k') | chrome/browser/resources/gaia_auth_host/post_message_channel.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698