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 "google_apis/gaia/fake_gaia.h"
9 #include "base/base_paths.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/json/json_writer.h"
15 #include "base/logging.h"
16 #include "base/memory/linked_ptr.h"
17 #include "base/path_service.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_split.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/values.h"
23 #include "google_apis/gaia/gaia_constants.h"
24 #include "google_apis/gaia/gaia_urls.h"
25 #include "net/base/url_util.h"
26 #include "net/cookies/parsed_cookie.h"
27 #include "net/http/http_status_code.h"
28 #include "net/test/embedded_test_server/http_request.h"
29 #include "net/test/embedded_test_server/http_response.h"
30 #include "url/third_party/mozilla/url_parse.h"
32 #define REGISTER_RESPONSE_HANDLER(url, method) \
33 request_handlers_.insert(std::make_pair( \
34 url.path(), base::Bind(&FakeGaia::method, base::Unretained(this))))
36 #define REGISTER_PATH_RESPONSE_HANDLER(path, method) \
37 request_handlers_.insert(std::make_pair( \
38 path, base::Bind(&FakeGaia::method, base::Unretained(this))))
40 using namespace net::test_server
;
44 const char kTestAuthCode
[] = "fake-auth-code";
45 const char kTestGaiaUberToken
[] = "fake-uber-token";
46 const char kTestAuthLoginAccessToken
[] = "fake-access-token";
47 const char kTestRefreshToken
[] = "fake-refresh-token";
48 const char kTestSessionSIDCookie
[] = "fake-session-SID-cookie";
49 const char kTestSessionLSIDCookie
[] = "fake-session-LSID-cookie";
50 const char kTestOAuthLoginSID
[] = "fake-oauth-SID-cookie";
51 const char kTestOAuthLoginLSID
[] = "fake-oauth-LSID-cookie";
52 const char kTestOAuthLoginAuthCode
[] = "fake-oauth-auth-code";
54 const char kDefaultGaiaId
[] = "12345";
56 const base::FilePath::CharType kServiceLogin
[] =
57 FILE_PATH_LITERAL("google_apis/test/service_login.html");
59 const base::FilePath::CharType kEmbeddedSetupChromeos
[] =
60 FILE_PATH_LITERAL("google_apis/test/embedded_setup_chromeos.html");
62 // OAuth2 Authentication header value prefix.
63 const char kAuthHeaderBearer
[] = "Bearer ";
64 const char kAuthHeaderOAuth
[] = "OAuth ";
66 const char kListAccountsResponseFormat
[] =
67 "[\"gaia.l.a.r\",[[\"gaia.l.a\",1,\"\",\"%s\",\"\",1,1,0,0,1,\"12345\"]]]";
69 typedef std::map
<std::string
, std::string
> CookieMap
;
71 // Parses cookie name-value map our of |request|.
72 CookieMap
GetRequestCookies(const HttpRequest
& request
) {
74 auto iter
= request
.headers
.find("Cookie");
75 if (iter
!= request
.headers
.end()) {
76 for (const std::string
& cookie_line
:
77 base::SplitString(iter
->second
, " ", base::TRIM_WHITESPACE
,
78 base::SPLIT_WANT_ALL
)) {
79 std::vector
<std::string
> name_value
= base::SplitString(
80 cookie_line
, "=", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
81 if (name_value
.size() != 2)
84 std::string value
= name_value
[1];
85 if (value
.size() && value
[value
.size() - 1] == ';')
86 value
= value
.substr(0, value
.size() -1);
88 result
.insert(std::make_pair(name_value
[0], value
));
94 // Extracts the |access_token| from authorization header of |request|.
95 bool GetAccessToken(const HttpRequest
& request
,
96 const char* auth_token_prefix
,
97 std::string
* access_token
) {
98 std::map
<std::string
, std::string
>::const_iterator auth_header_entry
=
99 request
.headers
.find("Authorization");
100 if (auth_header_entry
!= request
.headers
.end()) {
101 if (base::StartsWith(auth_header_entry
->second
, auth_token_prefix
,
102 base::CompareCase::SENSITIVE
)) {
103 *access_token
= auth_header_entry
->second
.substr(
104 strlen(auth_token_prefix
));
112 void SetCookies(BasicHttpResponse
* http_response
,
113 const std::string
& sid_cookie
,
114 const std::string
& lsid_cookie
) {
115 http_response
->AddCustomHeader(
117 base::StringPrintf("SID=%s; Path=/; HttpOnly", sid_cookie
.c_str()));
118 http_response
->AddCustomHeader(
120 base::StringPrintf("LSID=%s; Path=/; HttpOnly", lsid_cookie
.c_str()));
125 FakeGaia::AccessTokenInfo::AccessTokenInfo()
126 : expires_in(3600) {}
128 FakeGaia::AccessTokenInfo::~AccessTokenInfo() {}
130 FakeGaia::MergeSessionParams::MergeSessionParams() {
133 FakeGaia::MergeSessionParams::~MergeSessionParams() {
136 void FakeGaia::MergeSessionParams::Update(const MergeSessionParams
& update
) {
137 // This lambda uses a pointer to data member to merge attributes.
138 auto maybe_update_field
=
139 [this, &update
](std::string
MergeSessionParams::* field_ptr
) {
140 if (!(update
.*field_ptr
).empty())
141 this->*field_ptr
= update
.*field_ptr
;
144 maybe_update_field(&MergeSessionParams::auth_sid_cookie
);
145 maybe_update_field(&MergeSessionParams::auth_lsid_cookie
);
146 maybe_update_field(&MergeSessionParams::auth_code
);
147 maybe_update_field(&MergeSessionParams::refresh_token
);
148 maybe_update_field(&MergeSessionParams::access_token
);
149 maybe_update_field(&MergeSessionParams::gaia_uber_token
);
150 maybe_update_field(&MergeSessionParams::session_sid_cookie
);
151 maybe_update_field(&MergeSessionParams::session_lsid_cookie
);
152 maybe_update_field(&MergeSessionParams::email
);
155 FakeGaia::FakeGaia() : issue_oauth_code_cookie_(false) {
156 base::FilePath source_root_dir
;
157 PathService::Get(base::DIR_SOURCE_ROOT
, &source_root_dir
);
158 CHECK(base::ReadFileToString(
159 source_root_dir
.Append(base::FilePath(kServiceLogin
)),
160 &service_login_response_
));
161 CHECK(base::ReadFileToString(
162 source_root_dir
.Append(base::FilePath(kEmbeddedSetupChromeos
)),
163 &embedded_setup_chromeos_response_
));
166 FakeGaia::~FakeGaia() {}
168 void FakeGaia::SetFakeMergeSessionParams(
169 const std::string
& email
,
170 const std::string
& auth_sid_cookie
,
171 const std::string
& auth_lsid_cookie
) {
172 FakeGaia::MergeSessionParams params
;
173 params
.auth_sid_cookie
= auth_sid_cookie
;
174 params
.auth_lsid_cookie
= auth_lsid_cookie
;
175 params
.auth_code
= kTestAuthCode
;
176 params
.refresh_token
= kTestRefreshToken
;
177 params
.access_token
= kTestAuthLoginAccessToken
;
178 params
.gaia_uber_token
= kTestGaiaUberToken
;
179 params
.session_sid_cookie
= kTestSessionSIDCookie
;
180 params
.session_lsid_cookie
= kTestSessionLSIDCookie
;
181 params
.email
= email
;
182 SetMergeSessionParams(params
);
185 void FakeGaia::SetMergeSessionParams(
186 const MergeSessionParams
& params
) {
187 merge_session_params_
= params
;
190 void FakeGaia::UpdateMergeSessionParams(const MergeSessionParams
& params
) {
191 merge_session_params_
.Update(params
);
194 void FakeGaia::MapEmailToGaiaId(const std::string
& email
,
195 const std::string
& gaia_id
) {
196 DCHECK(!email
.empty());
197 DCHECK(!gaia_id
.empty());
198 email_to_gaia_id_map_
[email
] = gaia_id
;
201 std::string
FakeGaia::GetGaiaIdOfEmail(const std::string
& email
) const {
202 DCHECK(!email
.empty());
203 const auto it
= email_to_gaia_id_map_
.find(email
);
204 return it
== email_to_gaia_id_map_
.end() ? std::string(kDefaultGaiaId
) :
208 void FakeGaia::AddGoogleAccountsSigninHeader(
209 net::test_server::BasicHttpResponse
* http_response
,
210 const std::string
& email
) const {
211 DCHECK(!email
.empty());
212 http_response
->AddCustomHeader("google-accounts-signin",
214 "email=\"%s\", obfuscatedid=\"%s\", sessionindex=0",
215 email
.c_str(), GetGaiaIdOfEmail(email
).c_str()));
218 void FakeGaia::SetOAuthCodeCookie(
219 net::test_server::BasicHttpResponse
* http_response
) const {
220 http_response
->AddCustomHeader(
223 "oauth_code=%s; Path=/o/GetOAuth2Token; Secure; HttpOnly;",
224 merge_session_params_
.auth_code
.c_str()));
227 void FakeGaia::Initialize() {
228 GaiaUrls
* gaia_urls
= GaiaUrls::GetInstance();
229 // Handles /MergeSession GAIA call.
230 REGISTER_RESPONSE_HANDLER(
231 gaia_urls
->merge_session_url(), HandleMergeSession
);
233 // Handles /o/oauth2/programmatic_auth GAIA call.
234 REGISTER_RESPONSE_HANDLER(
235 gaia_urls
->client_login_to_oauth2_url(), HandleProgramaticAuth
);
237 // Handles /ServiceLogin GAIA call.
238 REGISTER_RESPONSE_HANDLER(
239 gaia_urls
->service_login_url(), HandleServiceLogin
);
241 // Handles /embedded/setup/chromeos GAIA call.
242 REGISTER_RESPONSE_HANDLER(gaia_urls
->embedded_setup_chromeos_url(),
243 HandleEmbeddedSetupChromeos
);
245 // Handles /OAuthLogin GAIA call.
246 REGISTER_RESPONSE_HANDLER(
247 gaia_urls
->oauth1_login_url(), HandleOAuthLogin
);
249 // Handles /ServiceLoginAuth GAIA call.
250 REGISTER_RESPONSE_HANDLER(
251 gaia_urls
->service_login_auth_url(), HandleServiceLoginAuth
);
253 // Handles /_/embedded/lookup/accountlookup for /embedded/setup/chromeos
254 // authentication request.
255 REGISTER_PATH_RESPONSE_HANDLER("/_/embedded/lookup/accountlookup",
256 HandleEmbeddedLookupAccountLookup
);
258 // Handles /_/embedded/signin/challenge for /embedded/setup/chromeos
259 // authentication request.
260 REGISTER_PATH_RESPONSE_HANDLER("/_/embedded/signin/challenge",
261 HandleEmbeddedSigninChallenge
);
263 // Handles /SSO GAIA call (not GAIA, made up for SAML tests).
264 REGISTER_PATH_RESPONSE_HANDLER("/SSO", HandleSSO
);
266 // Handles /oauth2/v4/token GAIA call.
267 REGISTER_RESPONSE_HANDLER(
268 gaia_urls
->oauth2_token_url(), HandleAuthToken
);
270 // Handles /oauth2/v2/tokeninfo GAIA call.
271 REGISTER_RESPONSE_HANDLER(
272 gaia_urls
->oauth2_token_info_url(), HandleTokenInfo
);
274 // Handles /oauth2/v2/IssueToken GAIA call.
275 REGISTER_RESPONSE_HANDLER(
276 gaia_urls
->oauth2_issue_token_url(), HandleIssueToken
);
278 // Handles /ListAccounts GAIA call.
279 REGISTER_RESPONSE_HANDLER(
280 gaia_urls
->ListAccountsURLWithSource(std::string()), HandleListAccounts
);
282 // Handles /GetUserInfo GAIA call.
283 REGISTER_RESPONSE_HANDLER(
284 gaia_urls
->get_user_info_url(), HandleGetUserInfo
);
286 // Handles /oauth2/v1/userinfo call.
287 REGISTER_RESPONSE_HANDLER(
288 gaia_urls
->oauth_user_info_url(), HandleOAuthUserInfo
);
291 scoped_ptr
<HttpResponse
> FakeGaia::HandleRequest(const HttpRequest
& request
) {
292 // The scheme and host of the URL is actually not important but required to
293 // get a valid GURL in order to parse |request.relative_url|.
294 GURL request_url
= GURL("http://localhost").Resolve(request
.relative_url
);
295 std::string request_path
= request_url
.path();
296 scoped_ptr
<BasicHttpResponse
> http_response(new BasicHttpResponse());
297 RequestHandlerMap::iterator iter
= request_handlers_
.find(request_path
);
298 if (iter
!= request_handlers_
.end()) {
299 LOG(WARNING
) << "Serving request " << request_path
;
300 iter
->second
.Run(request
, http_response
.get());
302 LOG(ERROR
) << "Unhandled request " << request_path
;
303 return scoped_ptr
<HttpResponse
>(); // Request not understood.
306 return http_response
.Pass();
309 void FakeGaia::IssueOAuthToken(const std::string
& auth_token
,
310 const AccessTokenInfo
& token_info
) {
311 access_token_info_map_
.insert(std::make_pair(auth_token
, token_info
));
314 void FakeGaia::RegisterSamlUser(const std::string
& account_id
,
315 const GURL
& saml_idp
) {
316 saml_account_idp_map_
[account_id
] = saml_idp
;
320 bool FakeGaia::GetQueryParameter(const std::string
& query
,
321 const std::string
& key
,
322 std::string
* value
) {
323 // Name and scheme actually don't matter, but are required to get a valid URL
325 GURL
query_url("http://localhost?" + query
);
326 return net::GetValueForKeyInQuery(query_url
, key
, value
);
329 std::string
FakeGaia::GetDeviceIdByRefreshToken(
330 const std::string
& refresh_token
) const {
331 auto it
= refresh_token_to_device_id_map_
.find(refresh_token
);
332 return it
!= refresh_token_to_device_id_map_
.end() ? it
->second
336 void FakeGaia::SetRefreshTokenToDeviceIdMap(
337 const RefreshTokenToDeviceIdMap
& refresh_token_to_device_id_map
) {
338 refresh_token_to_device_id_map_
= refresh_token_to_device_id_map
;
341 void FakeGaia::HandleMergeSession(const HttpRequest
& request
,
342 BasicHttpResponse
* http_response
) {
343 http_response
->set_code(net::HTTP_UNAUTHORIZED
);
344 if (merge_session_params_
.session_sid_cookie
.empty() ||
345 merge_session_params_
.session_lsid_cookie
.empty()) {
346 http_response
->set_code(net::HTTP_BAD_REQUEST
);
350 std::string uber_token
;
351 if (!GetQueryParameter(request
.content
, "uberauth", &uber_token
) ||
352 uber_token
!= merge_session_params_
.gaia_uber_token
) {
353 LOG(ERROR
) << "Missing or invalid 'uberauth' param in /MergeSession call";
357 std::string continue_url
;
358 if (!GetQueryParameter(request
.content
, "continue", &continue_url
)) {
359 LOG(ERROR
) << "Missing or invalid 'continue' param in /MergeSession call";
364 if (!GetQueryParameter(request
.content
, "source", &source
)) {
365 LOG(ERROR
) << "Missing or invalid 'source' param in /MergeSession call";
369 SetCookies(http_response
,
370 merge_session_params_
.session_sid_cookie
,
371 merge_session_params_
.session_lsid_cookie
);
372 // TODO(zelidrag): Not used now.
373 http_response
->set_content("OK");
374 http_response
->set_code(net::HTTP_OK
);
377 void FakeGaia::HandleProgramaticAuth(
378 const HttpRequest
& request
,
379 BasicHttpResponse
* http_response
) {
380 http_response
->set_code(net::HTTP_UNAUTHORIZED
);
381 if (merge_session_params_
.auth_code
.empty()) {
382 http_response
->set_code(net::HTTP_BAD_REQUEST
);
386 GURL request_url
= GURL("http://localhost").Resolve(request
.relative_url
);
387 std::string request_query
= request_url
.query();
389 GaiaUrls
* gaia_urls
= GaiaUrls::GetInstance();
391 if (!GetQueryParameter(request_query
, "scope", &scope
) ||
392 GaiaConstants::kOAuth1LoginScope
!= scope
) {
396 CookieMap cookies
= GetRequestCookies(request
);
397 CookieMap::const_iterator sid_iter
= cookies
.find("SID");
398 if (sid_iter
== cookies
.end() ||
399 sid_iter
->second
!= merge_session_params_
.auth_sid_cookie
) {
400 LOG(ERROR
) << "/o/oauth2/programmatic_auth missing SID cookie";
403 CookieMap::const_iterator lsid_iter
= cookies
.find("LSID");
404 if (lsid_iter
== cookies
.end() ||
405 lsid_iter
->second
!= merge_session_params_
.auth_lsid_cookie
) {
406 LOG(ERROR
) << "/o/oauth2/programmatic_auth missing LSID cookie";
410 std::string client_id
;
411 if (!GetQueryParameter(request_query
, "client_id", &client_id
) ||
412 gaia_urls
->oauth2_chrome_client_id() != client_id
) {
416 http_response
->AddCustomHeader(
419 "oauth_code=%s; Path=/o/GetOAuth2Token; Secure; HttpOnly;",
420 merge_session_params_
.auth_code
.c_str()));
421 http_response
->set_code(net::HTTP_OK
);
422 http_response
->set_content_type("text/html");
425 void FakeGaia::FormatJSONResponse(const base::DictionaryValue
& response_dict
,
426 BasicHttpResponse
* http_response
) {
427 std::string response_json
;
428 base::JSONWriter::Write(response_dict
, &response_json
);
429 http_response
->set_content(response_json
);
430 http_response
->set_code(net::HTTP_OK
);
433 const FakeGaia::AccessTokenInfo
* FakeGaia::FindAccessTokenInfo(
434 const std::string
& auth_token
,
435 const std::string
& client_id
,
436 const std::string
& scope_string
) const {
437 if (auth_token
.empty() || client_id
.empty())
440 std::vector
<std::string
> scope_list
= base::SplitString(
441 scope_string
, " ", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
442 ScopeSet
scopes(scope_list
.begin(), scope_list
.end());
444 for (AccessTokenInfoMap::const_iterator
entry(
445 access_token_info_map_
.lower_bound(auth_token
));
446 entry
!= access_token_info_map_
.upper_bound(auth_token
);
448 if (entry
->second
.audience
== client_id
&&
449 (scope_string
.empty() || entry
->second
.scopes
== scopes
)) {
450 return &(entry
->second
);
457 const FakeGaia::AccessTokenInfo
* FakeGaia::GetAccessTokenInfo(
458 const std::string
& access_token
) const {
459 for (AccessTokenInfoMap::const_iterator
entry(
460 access_token_info_map_
.begin());
461 entry
!= access_token_info_map_
.end();
463 if (entry
->second
.token
== access_token
)
464 return &(entry
->second
);
470 void FakeGaia::HandleServiceLogin(const HttpRequest
& request
,
471 BasicHttpResponse
* http_response
) {
472 http_response
->set_code(net::HTTP_OK
);
473 http_response
->set_content(service_login_response_
);
474 http_response
->set_content_type("text/html");
477 void FakeGaia::HandleEmbeddedSetupChromeos(const HttpRequest
& request
,
478 BasicHttpResponse
* http_response
) {
479 GURL request_url
= GURL("http://localhost").Resolve(request
.relative_url
);
480 std::string client_id
;
481 if (!GetQueryParameter(request_url
.query(), "client_id", &client_id
) ||
482 GaiaUrls::GetInstance()->oauth2_chrome_client_id() != client_id
) {
483 LOG(ERROR
) << "Missing or invalid param 'client_id' in "
484 "/embedded/setup/chromeos call";
488 http_response
->set_code(net::HTTP_OK
);
489 http_response
->set_content(embedded_setup_chromeos_response_
);
490 http_response
->set_content_type("text/html");
493 void FakeGaia::HandleOAuthLogin(const HttpRequest
& request
,
494 BasicHttpResponse
* http_response
) {
495 http_response
->set_code(net::HTTP_UNAUTHORIZED
);
496 if (merge_session_params_
.gaia_uber_token
.empty()) {
497 http_response
->set_code(net::HTTP_FORBIDDEN
);
501 std::string access_token
;
502 if (!GetAccessToken(request
, kAuthHeaderBearer
, &access_token
) &&
503 !GetAccessToken(request
, kAuthHeaderOAuth
, &access_token
)) {
504 LOG(ERROR
) << "/OAuthLogin missing access token in the header";
508 GURL request_url
= GURL("http://localhost").Resolve(request
.relative_url
);
509 std::string request_query
= request_url
.query();
512 if (!GetQueryParameter(request_query
, "source", &source
) &&
513 !GetQueryParameter(request
.content
, "source", &source
)) {
514 LOG(ERROR
) << "Missing 'source' param in /OAuthLogin call";
518 std::string issue_uberauth
;
519 if (GetQueryParameter(request_query
, "issueuberauth", &issue_uberauth
) &&
520 issue_uberauth
== "1") {
521 http_response
->set_content(merge_session_params_
.gaia_uber_token
);
522 http_response
->set_code(net::HTTP_OK
);
523 // Issue GAIA uber token.
525 http_response
->set_content(base::StringPrintf(
526 "SID=%s\nLSID=%s\nAuth=%s",
527 kTestOAuthLoginSID
, kTestOAuthLoginLSID
, kTestOAuthLoginAuthCode
));
528 http_response
->set_code(net::HTTP_OK
);
532 void FakeGaia::HandleServiceLoginAuth(const HttpRequest
& request
,
533 BasicHttpResponse
* http_response
) {
534 std::string continue_url
=
535 GaiaUrls::GetInstance()->service_login_url().spec();
536 GetQueryParameter(request
.content
, "continue", &continue_url
);
538 std::string redirect_url
= continue_url
;
542 GetQueryParameter(request
.content
, "Email", &email
) &&
543 saml_account_idp_map_
.find(email
) != saml_account_idp_map_
.end();
546 GURL
url(saml_account_idp_map_
[email
]);
547 url
= net::AppendQueryParameter(url
, "SAMLRequest", "fake_request");
548 url
= net::AppendQueryParameter(url
, "RelayState", continue_url
);
549 redirect_url
= url
.spec();
550 http_response
->AddCustomHeader("Google-Accounts-SAML", "Start");
551 } else if (!merge_session_params_
.auth_sid_cookie
.empty() &&
552 !merge_session_params_
.auth_lsid_cookie
.empty()) {
553 SetCookies(http_response
,
554 merge_session_params_
.auth_sid_cookie
,
555 merge_session_params_
.auth_lsid_cookie
);
558 http_response
->set_code(net::HTTP_TEMPORARY_REDIRECT
);
559 http_response
->AddCustomHeader("Location", redirect_url
);
561 // SAML sign-ins complete in HandleSSO().
565 AddGoogleAccountsSigninHeader(http_response
, email
);
566 if (issue_oauth_code_cookie_
)
567 SetOAuthCodeCookie(http_response
);
570 void FakeGaia::HandleEmbeddedLookupAccountLookup(
571 const net::test_server::HttpRequest
& request
,
572 net::test_server::BasicHttpResponse
* http_response
) {
575 GetQueryParameter(request
.content
, "identifier", &email
) &&
576 saml_account_idp_map_
.find(email
) != saml_account_idp_map_
.end();
581 GURL
url(saml_account_idp_map_
[email
]);
582 url
= net::AppendQueryParameter(url
, "SAMLRequest", "fake_request");
583 url
= net::AppendQueryParameter(
585 GaiaUrls::GetInstance()->signin_completed_continue_url().spec());
586 std::string redirect_url
= url
.spec();
587 http_response
->AddCustomHeader("Google-Accounts-SAML", "Start");
589 http_response
->AddCustomHeader("continue", redirect_url
);
592 void FakeGaia::HandleEmbeddedSigninChallenge(const HttpRequest
& request
,
593 BasicHttpResponse
* http_response
) {
595 GetQueryParameter(request
.content
, "identifier", &email
);
597 if (!merge_session_params_
.auth_sid_cookie
.empty() &&
598 !merge_session_params_
.auth_lsid_cookie
.empty()) {
599 SetCookies(http_response
, merge_session_params_
.auth_sid_cookie
,
600 merge_session_params_
.auth_lsid_cookie
);
603 AddGoogleAccountsSigninHeader(http_response
, email
);
605 if (issue_oauth_code_cookie_
)
606 SetOAuthCodeCookie(http_response
);
609 void FakeGaia::HandleSSO(const HttpRequest
& request
,
610 BasicHttpResponse
* http_response
) {
611 if (!merge_session_params_
.auth_sid_cookie
.empty() &&
612 !merge_session_params_
.auth_lsid_cookie
.empty()) {
613 SetCookies(http_response
,
614 merge_session_params_
.auth_sid_cookie
,
615 merge_session_params_
.auth_lsid_cookie
);
617 std::string relay_state
;
618 GetQueryParameter(request
.content
, "RelayState", &relay_state
);
619 std::string redirect_url
= relay_state
;
620 http_response
->set_code(net::HTTP_TEMPORARY_REDIRECT
);
621 http_response
->AddCustomHeader("Location", redirect_url
);
622 http_response
->AddCustomHeader("Google-Accounts-SAML", "End");
624 if (!merge_session_params_
.email
.empty())
625 AddGoogleAccountsSigninHeader(http_response
, merge_session_params_
.email
);
627 if (issue_oauth_code_cookie_
)
628 SetOAuthCodeCookie(http_response
);
631 void FakeGaia::HandleAuthToken(const HttpRequest
& request
,
632 BasicHttpResponse
* http_response
) {
633 std::string grant_type
;
634 if (!GetQueryParameter(request
.content
, "grant_type", &grant_type
)) {
635 http_response
->set_code(net::HTTP_BAD_REQUEST
);
636 LOG(ERROR
) << "No 'grant_type' param in /oauth2/v4/token";
640 if (grant_type
== "authorization_code") {
641 std::string auth_code
;
642 if (!GetQueryParameter(request
.content
, "code", &auth_code
) ||
643 auth_code
!= merge_session_params_
.auth_code
) {
644 http_response
->set_code(net::HTTP_BAD_REQUEST
);
645 LOG(ERROR
) << "No 'code' param in /oauth2/v4/token";
649 std::string device_id
;
650 if (GetQueryParameter(request
.content
, "device_id", &device_id
)) {
651 std::string device_type
;
652 if (!GetQueryParameter(request
.content
, "device_type", &device_type
)) {
653 http_response
->set_code(net::HTTP_BAD_REQUEST
);
654 LOG(ERROR
) << "'device_type' should be set if 'device_id' is set.";
657 if (device_type
!= "chrome") {
658 http_response
->set_code(net::HTTP_BAD_REQUEST
);
659 LOG(ERROR
) << "'device_type' is not 'chrome'.";
664 base::DictionaryValue response_dict
;
665 response_dict
.SetString("refresh_token",
666 merge_session_params_
.refresh_token
);
667 if (!device_id
.empty())
668 refresh_token_to_device_id_map_
[merge_session_params_
.refresh_token
] =
670 response_dict
.SetString("access_token",
671 merge_session_params_
.access_token
);
672 response_dict
.SetInteger("expires_in", 3600);
673 FormatJSONResponse(response_dict
, http_response
);
678 GetQueryParameter(request
.content
, "scope", &scope
);
680 std::string refresh_token
;
681 std::string client_id
;
682 if (GetQueryParameter(request
.content
, "refresh_token", &refresh_token
) &&
683 GetQueryParameter(request
.content
, "client_id", &client_id
)) {
684 const AccessTokenInfo
* token_info
=
685 FindAccessTokenInfo(refresh_token
, client_id
, scope
);
687 base::DictionaryValue response_dict
;
688 response_dict
.SetString("access_token", token_info
->token
);
689 response_dict
.SetInteger("expires_in", 3600);
690 FormatJSONResponse(response_dict
, http_response
);
695 LOG(ERROR
) << "Bad request for /oauth2/v4/token - "
696 << "refresh_token = " << refresh_token
697 << ", scope = " << scope
698 << ", client_id = " << client_id
;
699 http_response
->set_code(net::HTTP_BAD_REQUEST
);
702 void FakeGaia::HandleTokenInfo(const HttpRequest
& request
,
703 BasicHttpResponse
* http_response
) {
704 const AccessTokenInfo
* token_info
= NULL
;
705 std::string access_token
;
706 if (GetQueryParameter(request
.content
, "access_token", &access_token
))
707 token_info
= GetAccessTokenInfo(access_token
);
710 base::DictionaryValue response_dict
;
711 response_dict
.SetString("issued_to", token_info
->issued_to
);
712 response_dict
.SetString("audience", token_info
->audience
);
713 response_dict
.SetString("user_id", token_info
->user_id
);
714 std::vector
<std::string
> scope_vector(token_info
->scopes
.begin(),
715 token_info
->scopes
.end());
716 response_dict
.SetString("scope", base::JoinString(scope_vector
, " "));
717 response_dict
.SetInteger("expires_in", token_info
->expires_in
);
718 response_dict
.SetString("email", token_info
->email
);
719 FormatJSONResponse(response_dict
, http_response
);
721 http_response
->set_code(net::HTTP_BAD_REQUEST
);
725 void FakeGaia::HandleIssueToken(const HttpRequest
& request
,
726 BasicHttpResponse
* http_response
) {
727 std::string access_token
;
729 std::string client_id
;
730 if (GetAccessToken(request
, kAuthHeaderBearer
, &access_token
) &&
731 GetQueryParameter(request
.content
, "scope", &scope
) &&
732 GetQueryParameter(request
.content
, "client_id", &client_id
)) {
733 const AccessTokenInfo
* token_info
=
734 FindAccessTokenInfo(access_token
, client_id
, scope
);
736 base::DictionaryValue response_dict
;
737 response_dict
.SetString("issueAdvice", "auto");
738 response_dict
.SetString("expiresIn",
739 base::IntToString(token_info
->expires_in
));
740 response_dict
.SetString("token", token_info
->token
);
741 FormatJSONResponse(response_dict
, http_response
);
745 http_response
->set_code(net::HTTP_BAD_REQUEST
);
748 void FakeGaia::HandleListAccounts(const HttpRequest
& request
,
749 BasicHttpResponse
* http_response
) {
750 http_response
->set_content(base::StringPrintf(
751 kListAccountsResponseFormat
, merge_session_params_
.email
.c_str()));
752 http_response
->set_code(net::HTTP_OK
);
755 void FakeGaia::HandleGetUserInfo(const HttpRequest
& request
,
756 BasicHttpResponse
* http_response
) {
757 http_response
->set_content(base::StringPrintf(
758 "email=%s\ndisplayEmail=%s",
759 merge_session_params_
.email
.c_str(),
760 merge_session_params_
.email
.c_str()));
761 http_response
->set_code(net::HTTP_OK
);
764 void FakeGaia::HandleOAuthUserInfo(
765 const net::test_server::HttpRequest
& request
,
766 net::test_server::BasicHttpResponse
* http_response
) {
767 const AccessTokenInfo
* token_info
= NULL
;
768 std::string access_token
;
769 if (GetAccessToken(request
, kAuthHeaderBearer
, &access_token
) ||
770 GetAccessToken(request
, kAuthHeaderOAuth
, &access_token
)) {
771 token_info
= GetAccessTokenInfo(access_token
);
775 base::DictionaryValue response_dict
;
776 response_dict
.SetString("id", GetGaiaIdOfEmail(token_info
->email
));
777 response_dict
.SetString("email", token_info
->email
);
778 response_dict
.SetString("verified_email", token_info
->email
);
779 FormatJSONResponse(response_dict
, http_response
);
781 http_response
->set_code(net::HTTP_BAD_REQUEST
);