Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / api / identity / gaia_web_auth_flow.cc
blob825c87c28f3250946d880044865813820fe3d535
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.
5 #include "chrome/browser/extensions/api/identity/gaia_web_auth_flow.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "base/strings/string_split.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/trace_event/trace_event.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/signin/chrome_signin_client_factory.h"
14 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
15 #include "chrome/browser/signin/signin_manager_factory.h"
16 #include "components/signin/core/browser/profile_oauth2_token_service.h"
17 #include "components/signin/core/browser/signin_manager.h"
18 #include "google_apis/gaia/gaia_constants.h"
19 #include "google_apis/gaia/gaia_urls.h"
20 #include "net/base/escape.h"
22 namespace extensions {
24 GaiaWebAuthFlow::GaiaWebAuthFlow(Delegate* delegate,
25 Profile* profile,
26 const ExtensionTokenKey* token_key,
27 const std::string& oauth2_client_id,
28 const std::string& locale)
29 : delegate_(delegate),
30 profile_(profile),
31 account_id_(token_key->account_id) {
32 TRACE_EVENT_ASYNC_BEGIN2("identity",
33 "GaiaWebAuthFlow",
34 this,
35 "extension_id",
36 token_key->extension_id,
37 "account_id",
38 token_key->account_id);
40 const char kOAuth2RedirectPathFormat[] = "/%s#";
41 const char kOAuth2AuthorizeFormat[] =
42 "?response_type=token&approval_prompt=force&authuser=0&"
43 "client_id=%s&"
44 "scope=%s&"
45 "origin=chrome-extension://%s/&"
46 "redirect_uri=%s:/%s&"
47 "hl=%s";
48 // Additional parameters to pass if device_id is enabled.
49 const char kOAuth2AuthorizeFormatDeviceIdAddendum[] =
50 "&device_id=%s&"
51 "device_type=chrome";
53 std::vector<std::string> scopes(token_key->scopes.begin(),
54 token_key->scopes.end());
55 std::vector<std::string> client_id_parts = base::SplitString(
56 oauth2_client_id, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
57 std::reverse(client_id_parts.begin(), client_id_parts.end());
58 redirect_scheme_ = base::JoinString(client_id_parts, ".");
59 std::string signin_scoped_device_id;
60 // profile_ can be nullptr in unittests.
61 SigninClient* signin_client =
62 profile_ ? ChromeSigninClientFactory::GetForProfile(profile_) : nullptr;
63 if (signin_client)
64 signin_scoped_device_id = signin_client->GetSigninScopedDeviceId();
66 redirect_path_prefix_ = base::StringPrintf(kOAuth2RedirectPathFormat,
67 token_key->extension_id.c_str());
69 std::string oauth2_authorize_params = base::StringPrintf(
70 kOAuth2AuthorizeFormat,
71 oauth2_client_id.c_str(),
72 net::EscapeUrlEncodedData(base::JoinString(scopes, " "), true).c_str(),
73 token_key->extension_id.c_str(),
74 redirect_scheme_.c_str(),
75 token_key->extension_id.c_str(),
76 locale.c_str());
77 if (!signin_scoped_device_id.empty()) {
78 oauth2_authorize_params += base::StringPrintf(
79 kOAuth2AuthorizeFormatDeviceIdAddendum,
80 net::EscapeUrlEncodedData(signin_scoped_device_id, true).c_str());
82 auth_url_ = GaiaUrls::GetInstance()->oauth2_auth_url().Resolve(
83 oauth2_authorize_params);
86 GaiaWebAuthFlow::~GaiaWebAuthFlow() {
87 TRACE_EVENT_ASYNC_END0("identity", "GaiaWebAuthFlow", this);
89 if (web_flow_)
90 web_flow_.release()->DetachDelegateAndDelete();
93 void GaiaWebAuthFlow::Start() {
94 ProfileOAuth2TokenService* token_service =
95 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
96 ubertoken_fetcher_.reset(new UbertokenFetcher(token_service,
97 this,
98 GaiaConstants::kChromeSource,
99 profile_->GetRequestContext()));
100 ubertoken_fetcher_->StartFetchingToken(account_id_);
103 void GaiaWebAuthFlow::OnUbertokenSuccess(const std::string& token) {
104 TRACE_EVENT_ASYNC_STEP_PAST0(
105 "identity", "GaiaWebAuthFlow", this, "OnUbertokenSuccess");
107 const char kMergeSessionQueryFormat[] = "?uberauth=%s&"
108 "continue=%s&"
109 "source=appsv2";
111 std::string merge_query = base::StringPrintf(
112 kMergeSessionQueryFormat,
113 net::EscapeUrlEncodedData(token, true).c_str(),
114 net::EscapeUrlEncodedData(auth_url_.spec(), true).c_str());
115 GURL merge_url(
116 GaiaUrls::GetInstance()->merge_session_url().Resolve(merge_query));
118 web_flow_ = CreateWebAuthFlow(merge_url);
119 web_flow_->Start();
122 void GaiaWebAuthFlow::OnUbertokenFailure(const GoogleServiceAuthError& error) {
123 TRACE_EVENT_ASYNC_STEP_PAST1("identity",
124 "GaiaWebAuthFlow",
125 this,
126 "OnUbertokenSuccess",
127 "error",
128 error.ToString());
130 DVLOG(1) << "OnUbertokenFailure: " << error.error_message();
131 delegate_->OnGaiaFlowFailure(
132 GaiaWebAuthFlow::SERVICE_AUTH_ERROR, error, std::string());
135 void GaiaWebAuthFlow::OnAuthFlowFailure(WebAuthFlow::Failure failure) {
136 GaiaWebAuthFlow::Failure gaia_failure;
138 switch (failure) {
139 case WebAuthFlow::WINDOW_CLOSED:
140 gaia_failure = GaiaWebAuthFlow::WINDOW_CLOSED;
141 break;
142 case WebAuthFlow::LOAD_FAILED:
143 DVLOG(1) << "OnAuthFlowFailure LOAD_FAILED";
144 gaia_failure = GaiaWebAuthFlow::LOAD_FAILED;
145 break;
146 default:
147 NOTREACHED() << "Unexpected error from web auth flow: " << failure;
148 gaia_failure = GaiaWebAuthFlow::LOAD_FAILED;
149 break;
152 TRACE_EVENT_ASYNC_STEP_PAST1("identity",
153 "GaiaWebAuthFlow",
154 this,
155 "OnAuthFlowFailure",
156 "error",
157 gaia_failure);
159 delegate_->OnGaiaFlowFailure(
160 gaia_failure,
161 GoogleServiceAuthError(GoogleServiceAuthError::NONE),
162 std::string());
165 void GaiaWebAuthFlow::OnAuthFlowURLChange(const GURL& url) {
166 TRACE_EVENT_ASYNC_STEP_PAST0("identity",
167 "GaiaWebAuthFlow",
168 this,
169 "OnAuthFlowURLChange");
171 const char kOAuth2RedirectAccessTokenKey[] = "access_token";
172 const char kOAuth2RedirectErrorKey[] = "error";
173 const char kOAuth2ExpiresInKey[] = "expires_in";
175 // The format of the target URL is:
176 // reversed.oauth.client.id:/extensionid#access_token=TOKEN
178 // Because there is no double slash, everything after the scheme is
179 // interpreted as a path, including the fragment.
181 if (url.scheme() == redirect_scheme_ && !url.has_host() && !url.has_port() &&
182 base::StartsWith(url.GetContent(), redirect_path_prefix_,
183 base::CompareCase::SENSITIVE)) {
184 web_flow_.release()->DetachDelegateAndDelete();
186 std::string fragment = url.GetContent().substr(
187 redirect_path_prefix_.length(), std::string::npos);
188 base::StringPairs pairs;
189 base::SplitStringIntoKeyValuePairs(fragment, '=', '&', &pairs);
190 std::string access_token;
191 std::string error;
192 std::string expiration;
194 for (base::StringPairs::iterator it = pairs.begin();
195 it != pairs.end();
196 ++it) {
197 if (it->first == kOAuth2RedirectAccessTokenKey)
198 access_token = it->second;
199 else if (it->first == kOAuth2RedirectErrorKey)
200 error = it->second;
201 else if (it->first == kOAuth2ExpiresInKey)
202 expiration = it->second;
205 if (access_token.empty() && error.empty()) {
206 delegate_->OnGaiaFlowFailure(
207 GaiaWebAuthFlow::INVALID_REDIRECT,
208 GoogleServiceAuthError(GoogleServiceAuthError::NONE),
209 std::string());
210 } else if (!error.empty()) {
211 delegate_->OnGaiaFlowFailure(
212 GaiaWebAuthFlow::OAUTH_ERROR,
213 GoogleServiceAuthError(GoogleServiceAuthError::NONE),
214 error);
215 } else {
216 delegate_->OnGaiaFlowCompleted(access_token, expiration);
221 void GaiaWebAuthFlow::OnAuthFlowTitleChange(const std::string& title) {
222 // On the final page the title will be "Loading <redirect-url>".
223 // Treat it as though we'd really been redirected to <redirect-url>.
224 const char kRedirectPrefix[] = "Loading ";
225 std::string prefix(kRedirectPrefix);
227 if (base::StartsWith(title, prefix, base::CompareCase::SENSITIVE)) {
228 GURL url(title.substr(prefix.length(), std::string::npos));
229 if (url.is_valid())
230 OnAuthFlowURLChange(url);
234 scoped_ptr<WebAuthFlow> GaiaWebAuthFlow::CreateWebAuthFlow(GURL url) {
235 return scoped_ptr<WebAuthFlow>(new WebAuthFlow(this,
236 profile_,
237 url,
238 WebAuthFlow::INTERACTIVE));
241 } // namespace extensions