Commit d14eb558 authored by bartfab@chromium.org's avatar bartfab@chromium.org

Add credential passing API for Chrome OS SAML login

This CL adds an API that SAML IdPs can use to pass credentials to
Chrome OS during login instead of relying on password scraping.

BUG=127095
TEST=New browser test + manual

Review URL: https://codereview.chromium.org/138133006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@245480 0039d316-1c4b-4281-b951-d872f2087c98
parent ef444425
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "base/command_line.h" #include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chrome_notification_types.h"
...@@ -14,6 +17,7 @@ ...@@ -14,6 +17,7 @@
#include "chrome/browser/chromeos/login/webui_login_display.h" #include "chrome/browser/chromeos/login/webui_login_display.h"
#include "chrome/browser/chromeos/login/wizard_controller.h" #include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_switches.h"
#include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/chromeos_switches.h" #include "chromeos/chromeos_switches.h"
...@@ -52,81 +56,123 @@ const char kUserEmail[] = "bob@example.com"; ...@@ -52,81 +56,123 @@ const char kUserEmail[] = "bob@example.com";
const char kRelayState[] = "RelayState"; const char kRelayState[] = "RelayState";
const char kDefaultIdpHtml[] =
"<form id=IdPForm method=post action=\"$Post\">"
"<input type=hidden name=RelayState value=\"$RelayState\">"
"User: <input type=text id=Email name=Email>"
"Password: <input type=password id=Password name=Password>"
"<input id=Submit type=submit>"
"</form>";
// FakeSamlIdp serves IdP auth form and the form submission. The form is // FakeSamlIdp serves IdP auth form and the form submission. The form is
// served with the template's RelayState placeholder expanded to the real // served with the template's RelayState placeholder expanded to the real
// RelayState parameter from request. The form submission redirects back to // RelayState parameter from request. The form submission redirects back to
// FakeGaia with the same RelayState. // FakeGaia with the same RelayState.
class FakeSamlIdp { class FakeSamlIdp {
public: public:
FakeSamlIdp() : html_template_(kDefaultIdpHtml) {} FakeSamlIdp();
~FakeSamlIdp() {} ~FakeSamlIdp();
void SetUp(const std::string& base_path, const GURL& gaia_url);
void SetLoginHTMLTemplate(const std::string& template_file);
void SetLoginAuthHTMLTemplate(const std::string& template_file);
scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request);
private:
scoped_ptr<HttpResponse> BuildHTMLResponse(const std::string& html_template,
const std::string& relay_state,
const std::string& next_path);
base::FilePath html_template_dir_;
std::string login_path_;
std::string login_auth_path_;
std::string login_html_template_;
std::string login_auth_html_template_;
GURL gaia_assertion_url_;
DISALLOW_COPY_AND_ASSIGN(FakeSamlIdp);
};
FakeSamlIdp::FakeSamlIdp() {
}
FakeSamlIdp::~FakeSamlIdp() {
}
void SetUp(const std::string& base_path, const GURL& gaia_url) { void FakeSamlIdp::SetUp(const std::string& base_path, const GURL& gaia_url) {
base_path_= base_path; base::FilePath test_data_dir;
post_path_ = base_path + "Auth"; ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
html_template_dir_ = test_data_dir.Append("login");
login_path_= base_path;
login_auth_path_ = base_path + "Auth";
gaia_assertion_url_ = gaia_url.Resolve("/SSO"); gaia_assertion_url_ = gaia_url.Resolve("/SSO");
} }
void FakeSamlIdp::SetLoginHTMLTemplate(const std::string& template_file) {
EXPECT_TRUE(base::ReadFileToString(
html_template_dir_.Append(template_file),
&login_html_template_));
}
void FakeSamlIdp::SetLoginAuthHTMLTemplate(const std::string& template_file) {
EXPECT_TRUE(base::ReadFileToString(
html_template_dir_.Append(template_file),
&login_auth_html_template_));
}
scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request) { scoped_ptr<HttpResponse> FakeSamlIdp::HandleRequest(
const HttpRequest& request) {
// The scheme and host of the URL is actually not important but required to // The scheme and host of the URL is actually not important but required to
// get a valid GURL in order to parse |request.relative_url|. // get a valid GURL in order to parse |request.relative_url|.
GURL request_url = GURL("http://localhost").Resolve(request.relative_url); GURL request_url = GURL("http://localhost").Resolve(request.relative_url);
std::string request_path = request_url.path(); std::string request_path = request_url.path();
scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse()); if (request_path == login_path_) {
if (request_path == base_path_) {
std::string relay_state; std::string relay_state;
net::GetValueForKeyInQuery(request_url, kRelayState, &relay_state); net::GetValueForKeyInQuery(request_url, kRelayState, &relay_state);
return BuildHTMLResponse(login_html_template_,
relay_state,
login_auth_path_);
}
std::string response_html = html_template_; if (request_path != login_auth_path_) {
ReplaceSubstringsAfterOffset( // Request not understood.
&response_html, 0, "$RelayState", relay_state); return scoped_ptr<HttpResponse>();
ReplaceSubstringsAfterOffset( }
&response_html, 0, "$Post", post_path_);
http_response->set_code(net::HTTP_OK);
http_response->set_content(response_html);
http_response->set_content_type("text/html");
} else if (request_path == post_path_) {
std::string relay_state; std::string relay_state;
FakeGaia::GetQueryParameter(request.content, kRelayState, &relay_state); FakeGaia::GetQueryParameter(request.content, kRelayState, &relay_state);
GURL redirect_url = gaia_assertion_url_; GURL redirect_url = gaia_assertion_url_;
if (!login_auth_html_template_.empty()) {
return BuildHTMLResponse(login_auth_html_template_,
relay_state,
redirect_url.spec());
}
redirect_url = net::AppendQueryParameter( redirect_url = net::AppendQueryParameter(
redirect_url, "SAMLResponse", "fake_response"); redirect_url, "SAMLResponse", "fake_response");
redirect_url = net::AppendQueryParameter( redirect_url = net::AppendQueryParameter(
redirect_url, kRelayState, relay_state); redirect_url, kRelayState, relay_state);
scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
http_response->set_code(net::HTTP_TEMPORARY_REDIRECT); http_response->set_code(net::HTTP_TEMPORARY_REDIRECT);
http_response->AddCustomHeader("Location", redirect_url.spec()); http_response->AddCustomHeader("Location", redirect_url.spec());
} else {
// Request not understood.
return scoped_ptr<HttpResponse>();
}
return http_response.PassAs<HttpResponse>(); return http_response.PassAs<HttpResponse>();
} }
void set_html_template(const std::string& html_template) { scoped_ptr<HttpResponse> FakeSamlIdp::BuildHTMLResponse(
html_template_ = html_template; const std::string& html_template,
} const std::string& relay_state,
const std::string& next_path) {
std::string response_html = html_template;
ReplaceSubstringsAfterOffset(&response_html, 0, "$RelayState", relay_state);
ReplaceSubstringsAfterOffset(&response_html, 0, "$Post", next_path);
private: scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
std::string base_path_; http_response->set_code(net::HTTP_OK);
std::string post_path_; http_response->set_content(response_html);
std::string html_template_; http_response->set_content_type("text/html");
GURL gaia_assertion_url_; return http_response.PassAs<HttpResponse>();
DISALLOW_COPY_AND_ASSIGN(FakeSamlIdp); }
};
} // namespace } // namespace
...@@ -315,6 +361,7 @@ class SamlTest : public InProcessBrowserTest { ...@@ -315,6 +361,7 @@ class SamlTest : public InProcessBrowserTest {
// visible when SAML IdP page is loaded. And 'cancel' button goes back to // visible when SAML IdP page is loaded. And 'cancel' button goes back to
// gaia on clicking. // gaia on clicking.
IN_PROC_BROWSER_TEST_F(SamlTest, SamlUI) { IN_PROC_BROWSER_TEST_F(SamlTest, SamlUI) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
StartSamlAndWaitForIdpPageLoad(kUserEmail); StartSamlAndWaitForIdpPageLoad(kUserEmail);
// Saml flow UI expectations. // Saml flow UI expectations.
...@@ -337,14 +384,32 @@ IN_PROC_BROWSER_TEST_F(SamlTest, SamlUI) { ...@@ -337,14 +384,32 @@ IN_PROC_BROWSER_TEST_F(SamlTest, SamlUI) {
JsExpect("!$('gaia-signin').classList.contains('saml')"); JsExpect("!$('gaia-signin').classList.contains('saml')");
} }
// Tests the sign-in flow when the credentials passing API is used.
IN_PROC_BROWSER_TEST_F(SamlTest, CredentialPassingAPI) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_api_login.html");
fake_saml_idp()->SetLoginAuthHTMLTemplate("saml_api_login_auth.html");
StartSamlAndWaitForIdpPageLoad(kUserEmail);
// Fill-in the SAML IdP form and submit.
SetSignFormField("Email", "fake_user");
SetSignFormField("Password", "fake_password");
ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
// Login should finish login and a session should start.
content::WindowedNotificationObserver(
chrome::NOTIFICATION_SESSION_STARTED,
content::NotificationService::AllSources()).Wait();
}
// Tests the single password scraped flow. // Tests the single password scraped flow.
IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedSingle) { IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedSingle) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
StartSamlAndWaitForIdpPageLoad(kUserEmail); StartSamlAndWaitForIdpPageLoad(kUserEmail);
// Fill-in the SAML IdP form and submit. // Fill-in the SAML IdP form and submit.
SetSignFormField("Email", "fake_user"); SetSignFormField("Email", "fake_user");
SetSignFormField("Password", "fake_password"); SetSignFormField("Password", "fake_password");
ExecuteJsInSigninFrame("document.getElementById('IdPForm').submit();"); ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
// Lands on confirm password screen. // Lands on confirm password screen.
OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait(); OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
...@@ -362,21 +427,14 @@ IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedSingle) { ...@@ -362,21 +427,14 @@ IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedSingle) {
// Tests the multiple password scraped flow. // Tests the multiple password scraped flow.
IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedMultiple) { IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedMultiple) {
fake_saml_idp()->set_html_template( fake_saml_idp()->SetLoginHTMLTemplate("saml_login_two_passwords.html");
"<form id=IdPForm method=post action=\"$Post\">"
"<input type=hidden name=RelayState value=\"$RelayState\">"
"User: <input type=text id=Email name=Email>"
"Password: <input type=password id=Password name=Password>"
"Password: <input type=password id=Password1 name=Password1>"
"<input id=Submit type=submit>"
"</form>");
StartSamlAndWaitForIdpPageLoad(kUserEmail); StartSamlAndWaitForIdpPageLoad(kUserEmail);
SetSignFormField("Email", "fake_user"); SetSignFormField("Email", "fake_user");
SetSignFormField("Password", "fake_password"); SetSignFormField("Password", "fake_password");
SetSignFormField("Password1", "password1"); SetSignFormField("Password1", "password1");
ExecuteJsInSigninFrame("document.getElementById('IdPForm').submit();"); ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait(); OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
...@@ -389,17 +447,12 @@ IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedMultiple) { ...@@ -389,17 +447,12 @@ IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedMultiple) {
// Tests the no password scraped flow. // Tests the no password scraped flow.
IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedNone) { IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedNone) {
fake_saml_idp()->set_html_template( fake_saml_idp()->SetLoginHTMLTemplate("saml_login_no_passwords.html");
"<form id=IdPForm method=post action=\"$Post\">"
"<input type=hidden name=RelayState value=\"$RelayState\">"
"User: <input type=text id=Email name=Email>"
"<input id=Submit type=submit>"
"</form>");
StartSamlAndWaitForIdpPageLoad(kUserEmail); StartSamlAndWaitForIdpPageLoad(kUserEmail);
SetSignFormField("Email", "fake_user"); SetSignFormField("Email", "fake_user");
ExecuteJsInSigninFrame("document.getElementById('IdPForm').submit();"); ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
OobeScreenWaiter(OobeDisplay::SCREEN_MESSAGE_BOX).Wait(); OobeScreenWaiter(OobeDisplay::SCREEN_MESSAGE_BOX).Wait();
JsExpect( JsExpect(
...@@ -411,6 +464,7 @@ IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedNone) { ...@@ -411,6 +464,7 @@ IN_PROC_BROWSER_TEST_F(SamlTest, ScrapedNone) {
// |bob@example.com| via SAML. Verifies that the logged-in user is correctly // |bob@example.com| via SAML. Verifies that the logged-in user is correctly
// identified as Bob. // identified as Bob.
IN_PROC_BROWSER_TEST_F(SamlTest, UseAutenticatedUserEmailAddress) { IN_PROC_BROWSER_TEST_F(SamlTest, UseAutenticatedUserEmailAddress) {
fake_saml_idp()->SetLoginHTMLTemplate("saml_login.html");
// Type |alice@example.com| into the GAIA login form. // Type |alice@example.com| into the GAIA login form.
StartSamlAndWaitForIdpPageLoad(kAnotherUserEmail); StartSamlAndWaitForIdpPageLoad(kAnotherUserEmail);
...@@ -419,7 +473,7 @@ IN_PROC_BROWSER_TEST_F(SamlTest, UseAutenticatedUserEmailAddress) { ...@@ -419,7 +473,7 @@ IN_PROC_BROWSER_TEST_F(SamlTest, UseAutenticatedUserEmailAddress) {
// reports was set via SetMergeSessionParams()). // reports was set via SetMergeSessionParams()).
SetSignFormField("Email", "fake_user"); SetSignFormField("Email", "fake_user");
SetSignFormField("Password", "fake_password"); SetSignFormField("Password", "fake_password");
ExecuteJsInSigninFrame("document.getElementById('IdPForm').submit();"); ExecuteJsInSigninFrame("document.getElementById('Submit').click();");
OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait(); OobeScreenWaiter(OobeDisplay::SCREEN_CONFIRM_PASSWORD).Wait();
......
...@@ -4,19 +4,29 @@ ...@@ -4,19 +4,29 @@
/** /**
* @fileoverview * @fileoverview
* The background script of auth extension that bridges the communications * A background script of the auth extension that bridges the communication
* between main and injected script. * between the main and injected scripts.
* Here are the communications along a SAML sign-in flow: *
* 1. Main script sends an 'onAuthStarted' signal to indicate the authentication * Here is an overview of the communication flow when SAML is being used:
* flow is started and SAML pages might be loaded from now on; * 1. The main script sends the |startAuth| signal to this background script,
* 2. After the 'onAuthTstarted' signal, injected script starts to scraping * indicating that the authentication flow has started and SAML pages may be
* all password fields on normal page (i.e. http or https) and sends page * loaded from now on.
* load signal as well as the passwords to the background script here; * 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 backgroudn 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.
*/ */
/** /**
* BackgroundBridge holds the main script's state and the scraped passwords * BackgroundBridge allows the main script and the injected script to
* from the injected script to help the two collaborate. * collaborate. It forwards credentials API calls to the main script and
* maintains a list of scraped passwords.
*/ */
function BackgroundBridge() { function BackgroundBridge() {
} }
...@@ -88,6 +98,8 @@ BackgroundBridge.prototype = { ...@@ -88,6 +98,8 @@ BackgroundBridge.prototype = {
setupForInjected_: function(port) { setupForInjected_: function(port) {
this.channelInjected_ = new Channel(); this.channelInjected_ = new Channel();
this.channelInjected_.init(port); this.channelInjected_.init(port);
this.channelInjected_.registerMessage(
'apiCall', this.onAPICall_.bind(this));
this.channelInjected_.registerMessage( this.channelInjected_.registerMessage(
'updatePassword', this.onUpdatePassword_.bind(this)); 'updatePassword', this.onUpdatePassword_.bind(this));
this.channelInjected_.registerMessage( this.channelInjected_.registerMessage(
...@@ -141,6 +153,10 @@ BackgroundBridge.prototype = { ...@@ -141,6 +153,10 @@ BackgroundBridge.prototype = {
return Object.keys(passwords); return Object.keys(passwords);
}, },
onAPICall_: function(msg) {
this.channelMain_.send(msg);
},
onUpdatePassword_: function(msg) { onUpdatePassword_: function(msg) {
if (!this.authStarted_) if (!this.authStarted_)
return; return;
......
...@@ -204,6 +204,8 @@ Authenticator.prototype = { ...@@ -204,6 +204,8 @@ Authenticator.prototype = {
this.samlSupportChannel_.connect('authMain'); this.samlSupportChannel_.connect('authMain');
this.samlSupportChannel_.registerMessage( this.samlSupportChannel_.registerMessage(
'onAuthPageLoaded', this.onAuthPageLoaded_.bind(this)); 'onAuthPageLoaded', this.onAuthPageLoaded_.bind(this));
this.samlSupportChannel_.registerMessage(
'apiCall', this.onAPICall_.bind(this));
this.samlSupportChannel_.send({ this.samlSupportChannel_.send({
name: 'setGaiaUrl', name: 'setGaiaUrl',
gaiaUrl: this.gaiaUrl_ gaiaUrl: this.gaiaUrl_
...@@ -233,6 +235,25 @@ Authenticator.prototype = { ...@@ -233,6 +235,25 @@ Authenticator.prototype = {
}, this.parentPage_); }, this.parentPage_);
}, },
/**
* Invoked when one of the credential passing API methods is called by a SAML
* provider.
* @param {!Object} msg Details of the API call.
*/
onAPICall_: function(msg) {
var call = msg.call;
if (call.method == 'add') {
this.apiToken_ = call.token;
this.email_ = call.user;
this.password_ = call.password;
} else if (call.method == 'confirm') {
if (call.token != this.apiToken_)
console.error('Authenticator.onAPICall_: token mismatch');
} else {
console.error('Authenticator.onAPICall_: unknown message');
}
},
onLoginUILoaded: function() { onLoginUILoaded: function() {
var msg = { var msg = {
'method': 'loginUILoaded' 'method': 'loginUILoaded'
...@@ -261,6 +282,7 @@ Authenticator.prototype = { ...@@ -261,6 +282,7 @@ Authenticator.prototype = {
attemptToken: this.attemptToken_}, attemptToken: this.attemptToken_},
this.parentPage_); this.parentPage_);
if (!this.password_) {
this.samlSupportChannel_.sendWithCallback( this.samlSupportChannel_.sendWithCallback(
{name: 'getScrapedPasswords'}, {name: 'getScrapedPasswords'},
function(passwords) { function(passwords) {
...@@ -274,6 +296,7 @@ Authenticator.prototype = { ...@@ -274,6 +296,7 @@ Authenticator.prototype = {
this.parentPage_); this.parentPage_);
} }
}.bind(this)); }.bind(this));
}
}, },
maybeCompleteSAMLLogin_: function() { maybeCompleteSAMLLogin_: function() {
......
...@@ -4,15 +4,57 @@ ...@@ -4,15 +4,57 @@
/** /**
* @fileoverview * @fileoverview
* Script to be injected into SAML provider pages that do not support the * Script to be injected into SAML provider pages, serving three main purposes:
* auth service provider postMessage API. It serves two main purposes:
* 1. Signal hosting extension that an external page is loaded so that the * 1. Signal hosting extension that an external page is loaded so that the
* UI around it could be changed accordingly; * UI around it should be changed accordingly;
* 2. Scrape password and send it back to be used for encrypt user data and * 2. Provide an API via which the SAML provider can pass user credentials to
* use for offline login; * Chrome OS, allowing the password to be used for encrypting user data and
* offline login.
* 3. Scrape password fields, making the password available to Chrome OS even if
* the SAML provider does not support the credential passing API.
*/ */
(function() { (function() {
function APICallForwarder() {
}
/**
* The credential passing API is used by sending messages to the SAML page's
* |window| object. This class forwards the calls to a background script via a
* |Channel|.
*/
APICallForwarder.prototype = {
// Channel to which API calls are forwarded.
channel_: null,
/**
* Initialize the API call forwarder.
* @param {!Object} channel Channel to which API calls should be forwarded.
*/
init: function(channel) {
this.channel_ = channel;
window.addEventListener('message', this.onMessage_.bind(this));
},
onMessage_: function(event) {
if (event.source != window ||
typeof event.data != 'object' ||
!event.data.hasOwnProperty('type') ||
event.data.type != 'gaia_saml_api') {
return;
}
if (event.data.call.method == 'initialize') {
// Respond to the |initialize| call directly.
event.source.postMessage({
type: 'gaia_saml_api_reply',
response: {result: 'initialized', version: 1}}, '/');
} else {
// Forward all other calls.
this.channel_.send({name: 'apiCall', call: event.data.call});
}
}
};
/** /**
* A class to scrape password from type=password input elements under a given * A class to scrape password from type=password input elements under a given
* docRoot and send them back via a Channel. * docRoot and send them back via a Channel.
...@@ -125,6 +167,9 @@ ...@@ -125,6 +167,9 @@
channel.connect('injected'); channel.connect('injected');
channel.send({name: 'pageLoaded', url: pageURL}); channel.send({name: 'pageLoaded', url: pageURL});
apiCallForwarder = new APICallForwarder();
apiCallForwarder.init(channel);
passwordScraper = new PasswordInputScraper(); passwordScraper = new PasswordInputScraper();
passwordScraper.init(channel, pageURL, document.documentElement); passwordScraper.init(channel, pageURL, document.documentElement);
} }
......
<html>
<head>
<script type="text/javascript">
function initialize() {
window.setTimeout(function() {
window.postMessage({
type: 'gaia_saml_api',
call: {method: 'initialize'}}, '/');
}, 0);
}
function send_and_submit() {
var form = document.forms[0];
var token = form.elements['RelayState'].value;
var user = form.elements['Email'].value;
var password = form.elements['Password'].value;
window.setTimeout(function() {
window.postMessage({
type: 'gaia_saml_api',
call: {method: 'add',
token: token,
user: user,
password: password}}, '/');
form.submit();
}, 0);
}
</script>
</head>
<body onload="initialize();">
<form method=post action="$Post">
<input type=hidden name=RelayState value="$RelayState">
User: <input type=text id=Email name=Email>
Password: <input type=password id=Password name=Password>
<input id=Submit type=button value="Login" onclick="send_and_submit();"/>
</form>
</body>
</html>
<html>
<head>
<script type="text/javascript">
function send_and_submit() {
var form = document.forms[0];
var token = form.elements['RelayState'].value;
window.setTimeout(function() {
window.postMessage({
type: 'gaia_saml_api',
call: {method: 'confirm', token: token}}, '/');
form.submit();
}, 0);
}
</script>
</head>
<body onload="send_and_submit();">
<form method=post action="$Post">
<input type=hidden name=SAMLResponse value="fake_response"/>
<input type=hidden name=RelayState value="$RelayState">
</form>
</body>
</html>
<html>
<head></head>
<body>
<form method=post action="$Post">
<input type=hidden name=RelayState value="$RelayState">
User: <input type=text id=Email name=Email>
Password: <input type=password id=Password name=Password>
<input id=Submit type=submit>
</form>
</body>
</html>
<html>
<head></head>
<body>
<form method=post action="$Post">
<input type=hidden name=RelayState value="$RelayState">
User: <input type=text id=Email name=Email>
<input id=Submit type=submit>
</form>
</body>
</html>
<html>
<head></head>
<body>
<form method=post action="$Post">
<input type=hidden name=RelayState value="$RelayState">
User: <input type=text id=Email name=Email>
Password: <input type=password id=Password name=Password>
Password: <input type=password id=Password1 name=Password1>
<input id=Submit type=submit>
</form>
</body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment