Index: chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc |
diff --git a/chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc b/chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..df89884eddd4d9f2fa25e270e47129cd25a21b17 |
--- /dev/null |
+++ b/chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc |
@@ -0,0 +1,152 @@ |
+// Copyright (c) 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. |
+ |
+#include "chrome/browser/extensions/api/identity/gaia_web_auth_flow.h" |
+ |
+#include "base/string_util.h" |
+#include "base/stringprintf.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "base/strings/string_split.h" |
+#include "google_apis/gaia/gaia_urls.h" |
+#include "net/base/escape.h" |
+ |
+namespace extensions { |
+ |
+GaiaWebAuthFlow::GaiaWebAuthFlow(Delegate* delegate, |
+ Profile* profile, |
+ chrome::HostDesktopType host_desktop_type, |
+ const std::string& extension_id, |
+ const OAuth2Info& oauth2_info) |
+ : delegate_(delegate), |
+ profile_(profile), |
+ host_desktop_type_(host_desktop_type) { |
+ const char kOAuth2RedirectPathFormat[] = "/%s#"; |
+ const char kOAuth2AuthorizeFormat[] = |
+ "%s?response_type=token&approval_prompt=force&authuser=0&" |
+ "client_id=%s&" |
+ "scope=%s&" |
+ "origin=chrome-extension://%s/&" |
+ "redirect_uri=%s:/%s"; |
+ |
+ std::vector<std::string> client_id_parts; |
+ base::SplitString(oauth2_info.client_id, '.', &client_id_parts); |
+ std::reverse(client_id_parts.begin(), client_id_parts.end()); |
+ redirect_scheme_ = JoinString(client_id_parts, '.'); |
+ |
+ redirect_path_prefix_ = |
+ base::StringPrintf(kOAuth2RedirectPathFormat, extension_id.c_str()); |
+ |
+ auth_url_ = base::StringPrintf( |
+ kOAuth2AuthorizeFormat, |
+ GaiaUrls::GetInstance()->oauth2_auth_url().c_str(), |
+ oauth2_info.client_id.c_str(), |
+ net::EscapeUrlEncodedData(JoinString(oauth2_info.scopes, ' '), true) |
+ .c_str(), |
+ extension_id.c_str(), |
+ redirect_scheme_.c_str(), |
+ extension_id.c_str()); |
+} |
+ |
+GaiaWebAuthFlow::~GaiaWebAuthFlow() {} |
+ |
+void GaiaWebAuthFlow::Start() { |
+ ubertoken_fetcher_.reset(new UbertokenFetcher(profile_, this)); |
+ ubertoken_fetcher_->StartFetchingToken(); |
+} |
+ |
+void GaiaWebAuthFlow::OnUbertokenSuccess(const std::string& token) { |
+ const char kMergeSessionQueryFormat[] = "?uberauth=%s&" |
+ "continue=%s&" |
+ "source=appsv2"; |
+ |
+ std::string merge_query = |
+ base::StringPrintf(kMergeSessionQueryFormat, |
+ net::EscapeUrlEncodedData(token, true).c_str(), |
+ net::EscapeUrlEncodedData(auth_url_, true).c_str()); |
+ GURL merge_url(GaiaUrls::GetInstance()->merge_session_url() + merge_query); |
+ |
+ web_flow_ = CreateWebAuthFlow(merge_url); |
+ web_flow_->Start(); |
+} |
+ |
+void GaiaWebAuthFlow::OnUbertokenFailure(const GoogleServiceAuthError& error) { |
+ delegate_->OnGaiaFlowFailure(GaiaWebAuthFlow::SERVICE_AUTH_ERROR, error); |
+} |
+ |
+void GaiaWebAuthFlow::OnAuthFlowFailure(WebAuthFlow::Failure failure) { |
+ DCHECK(failure == WebAuthFlow::WINDOW_CLOSED); |
+ delegate_->OnGaiaFlowFailure( |
+ GaiaWebAuthFlow::WINDOW_CLOSED, |
+ GoogleServiceAuthError(GoogleServiceAuthError::NONE)); |
+} |
+ |
+void GaiaWebAuthFlow::OnAuthFlowURLChange(const GURL& url) { |
+ const char kOAuth2RedirectAccessTokenKey[] = "access_token"; |
+ const char kOAuth2RedirectErrorKey[] = "error"; |
+ const char kOAuth2ExpiresInKey[] = "expires_in"; |
+ |
+ // The format of the target URL is: |
+ // reversed.oauth.client.id:/extensionid#access_token=TOKEN |
+ // |
+ // Because there is no double slash, everything after the scheme is |
+ // interpreted as a path, including the fragment. |
+ |
+ if (url.scheme() == redirect_scheme_ && !url.has_host() && !url.has_port() && |
+ StartsWithASCII(url.path(), redirect_path_prefix_, true)) { |
+ web_flow_.reset(); |
+ |
+ std::string fragment = |
+ url.path().substr(redirect_path_prefix_.length(), std::string::npos); |
+ std::vector<std::pair<std::string, std::string> > pairs; |
+ base::SplitStringIntoKeyValuePairs(fragment, '=', '&', &pairs); |
+ std::string access_token; |
+ std::string error; |
+ std::string expiration; |
+ |
+ for (std::vector<std::pair<std::string, std::string> >::iterator |
+ it = pairs.begin(); |
+ it != pairs.end(); |
+ ++it) { |
+ if (it->first == kOAuth2RedirectAccessTokenKey) |
+ access_token = it->second; |
+ else if (it->first == kOAuth2RedirectErrorKey) |
+ error = it->second; |
+ else if (it->first == kOAuth2ExpiresInKey) |
+ expiration = it->second; |
+ } |
+ |
+ if (access_token.empty() && error.empty()) { |
+ delegate_->OnGaiaFlowFailure( |
+ GaiaWebAuthFlow::INVALID_REDIRECT, |
+ GoogleServiceAuthError(GoogleServiceAuthError::NONE)); |
+ } else { |
+ delegate_->OnGaiaFlowCompleted(access_token, expiration, error); |
+ } |
+ } |
+} |
+ |
+void GaiaWebAuthFlow::OnAuthFlowTitleChange(const std::string& title) { |
+ // On the final page the title will be "Loading <redirect-url>". |
+ // Treat it as though we'd really been redirected to <redirect-url>. |
+ const char kRedirectPrefix[] = "Loading "; |
+ std::string prefix(kRedirectPrefix); |
+ |
+ if (StartsWithASCII(title, prefix, true)) { |
+ GURL url(title.substr(prefix.length(), std::string::npos)); |
+ if (url.is_valid()) |
+ OnAuthFlowURLChange(url); |
+ } |
+} |
+ |
+scoped_ptr<WebAuthFlow> GaiaWebAuthFlow::CreateWebAuthFlow(GURL url) { |
+ gfx::Rect initial_bounds; |
+ return scoped_ptr<WebAuthFlow>(new WebAuthFlow(this, |
+ profile_, |
+ url, |
+ WebAuthFlow::INTERACTIVE, |
+ initial_bounds, |
+ host_desktop_type_)); |
+} |
+ |
+} // extensions |