OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/extensions/api/identity/gaia_web_auth_flow.h" |
| 6 |
| 7 #include "base/string_util.h" |
| 8 #include "base/stringprintf.h" |
| 9 #include "base/strings/string_number_conversions.h" |
| 10 #include "base/strings/string_split.h" |
| 11 #include "google_apis/gaia/gaia_urls.h" |
| 12 #include "net/base/escape.h" |
| 13 |
| 14 namespace extensions { |
| 15 |
| 16 GaiaWebAuthFlow::GaiaWebAuthFlow(Delegate* delegate, |
| 17 Profile* profile, |
| 18 chrome::HostDesktopType host_desktop_type, |
| 19 const std::string& extension_id, |
| 20 const OAuth2Info& oauth2_info) |
| 21 : delegate_(delegate), |
| 22 profile_(profile), |
| 23 host_desktop_type_(host_desktop_type) { |
| 24 const char kOAuth2RedirectPathFormat[] = "/%s#"; |
| 25 const char kOAuth2AuthorizeFormat[] = |
| 26 "%s?response_type=token&approval_prompt=force&authuser=0&" |
| 27 "client_id=%s&" |
| 28 "scope=%s&" |
| 29 "origin=chrome-extension://%s/&" |
| 30 "redirect_uri=%s:/%s"; |
| 31 |
| 32 std::vector<std::string> client_id_parts; |
| 33 base::SplitString(oauth2_info.client_id, '.', &client_id_parts); |
| 34 std::reverse(client_id_parts.begin(), client_id_parts.end()); |
| 35 redirect_scheme_ = JoinString(client_id_parts, '.'); |
| 36 |
| 37 redirect_path_prefix_ = |
| 38 base::StringPrintf(kOAuth2RedirectPathFormat, extension_id.c_str()); |
| 39 |
| 40 auth_url_ = GURL(base::StringPrintf( |
| 41 kOAuth2AuthorizeFormat, |
| 42 GaiaUrls::GetInstance()->oauth2_auth_url().c_str(), |
| 43 oauth2_info.client_id.c_str(), |
| 44 net::EscapeUrlEncodedData(JoinString(oauth2_info.scopes, ' '), true) |
| 45 .c_str(), |
| 46 extension_id.c_str(), |
| 47 redirect_scheme_.c_str(), |
| 48 extension_id.c_str())); |
| 49 } |
| 50 |
| 51 GaiaWebAuthFlow::~GaiaWebAuthFlow() {} |
| 52 |
| 53 void GaiaWebAuthFlow::Start() { |
| 54 ubertoken_fetcher_.reset(new UbertokenFetcher(profile_, this)); |
| 55 ubertoken_fetcher_->StartFetchingToken(); |
| 56 } |
| 57 |
| 58 void GaiaWebAuthFlow::OnUbertokenSuccess(const std::string& token) { |
| 59 const char kMergeSessionQueryFormat[] = "?uberauth=%s&" |
| 60 "continue=%s&" |
| 61 "source=appsv2"; |
| 62 |
| 63 std::string merge_query = base::StringPrintf( |
| 64 kMergeSessionQueryFormat, |
| 65 net::EscapeUrlEncodedData(token, true).c_str(), |
| 66 net::EscapeUrlEncodedData(auth_url_.spec(), true).c_str()); |
| 67 GURL merge_url(GaiaUrls::GetInstance()->merge_session_url() + merge_query); |
| 68 |
| 69 web_flow_ = CreateWebAuthFlow(merge_url); |
| 70 web_flow_->Start(); |
| 71 } |
| 72 |
| 73 void GaiaWebAuthFlow::OnUbertokenFailure(const GoogleServiceAuthError& error) { |
| 74 delegate_->OnGaiaFlowFailure( |
| 75 GaiaWebAuthFlow::SERVICE_AUTH_ERROR, error, std::string()); |
| 76 } |
| 77 |
| 78 void GaiaWebAuthFlow::OnAuthFlowFailure(WebAuthFlow::Failure failure) { |
| 79 DCHECK(failure == WebAuthFlow::WINDOW_CLOSED); |
| 80 delegate_->OnGaiaFlowFailure( |
| 81 GaiaWebAuthFlow::WINDOW_CLOSED, |
| 82 GoogleServiceAuthError(GoogleServiceAuthError::NONE), |
| 83 std::string()); |
| 84 } |
| 85 |
| 86 void GaiaWebAuthFlow::OnAuthFlowURLChange(const GURL& url) { |
| 87 const char kOAuth2RedirectAccessTokenKey[] = "access_token"; |
| 88 const char kOAuth2RedirectErrorKey[] = "error"; |
| 89 const char kOAuth2ExpiresInKey[] = "expires_in"; |
| 90 |
| 91 // The format of the target URL is: |
| 92 // reversed.oauth.client.id:/extensionid#access_token=TOKEN |
| 93 // |
| 94 // Because there is no double slash, everything after the scheme is |
| 95 // interpreted as a path, including the fragment. |
| 96 |
| 97 if (url.scheme() == redirect_scheme_ && !url.has_host() && !url.has_port() && |
| 98 StartsWithASCII(url.path(), redirect_path_prefix_, true)) { |
| 99 web_flow_.reset(); |
| 100 |
| 101 std::string fragment = |
| 102 url.path().substr(redirect_path_prefix_.length(), std::string::npos); |
| 103 std::vector<std::pair<std::string, std::string> > pairs; |
| 104 base::SplitStringIntoKeyValuePairs(fragment, '=', '&', &pairs); |
| 105 std::string access_token; |
| 106 std::string error; |
| 107 std::string expiration; |
| 108 |
| 109 for (std::vector<std::pair<std::string, std::string> >::iterator |
| 110 it = pairs.begin(); |
| 111 it != pairs.end(); |
| 112 ++it) { |
| 113 if (it->first == kOAuth2RedirectAccessTokenKey) |
| 114 access_token = it->second; |
| 115 else if (it->first == kOAuth2RedirectErrorKey) |
| 116 error = it->second; |
| 117 else if (it->first == kOAuth2ExpiresInKey) |
| 118 expiration = it->second; |
| 119 } |
| 120 |
| 121 if (access_token.empty() && error.empty()) { |
| 122 delegate_->OnGaiaFlowFailure( |
| 123 GaiaWebAuthFlow::INVALID_REDIRECT, |
| 124 GoogleServiceAuthError(GoogleServiceAuthError::NONE), |
| 125 std::string()); |
| 126 } else if (!error.empty()) { |
| 127 delegate_->OnGaiaFlowFailure( |
| 128 GaiaWebAuthFlow::OAUTH_ERROR, |
| 129 GoogleServiceAuthError(GoogleServiceAuthError::NONE), |
| 130 error); |
| 131 } else { |
| 132 delegate_->OnGaiaFlowCompleted(access_token, expiration); |
| 133 } |
| 134 } |
| 135 } |
| 136 |
| 137 void GaiaWebAuthFlow::OnAuthFlowTitleChange(const std::string& title) { |
| 138 // On the final page the title will be "Loading <redirect-url>". |
| 139 // Treat it as though we'd really been redirected to <redirect-url>. |
| 140 const char kRedirectPrefix[] = "Loading "; |
| 141 std::string prefix(kRedirectPrefix); |
| 142 |
| 143 if (StartsWithASCII(title, prefix, true)) { |
| 144 GURL url(title.substr(prefix.length(), std::string::npos)); |
| 145 if (url.is_valid()) |
| 146 OnAuthFlowURLChange(url); |
| 147 } |
| 148 } |
| 149 |
| 150 scoped_ptr<WebAuthFlow> GaiaWebAuthFlow::CreateWebAuthFlow(GURL url) { |
| 151 gfx::Rect initial_bounds; |
| 152 return scoped_ptr<WebAuthFlow>(new WebAuthFlow(this, |
| 153 profile_, |
| 154 url, |
| 155 WebAuthFlow::INTERACTIVE, |
| 156 initial_bounds, |
| 157 host_desktop_type_)); |
| 158 } |
| 159 |
| 160 } // extensions |
OLD | NEW |