• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "google_apis/gaia/fake_gaia.h"
6 
7 #include <vector>
8 
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/url_parse.h"
31 
32 #define REGISTER_RESPONSE_HANDLER(url, method) \
33   request_handlers_.insert(std::make_pair( \
34         url.path(), base::Bind(&FakeGaia::method, base::Unretained(this))))
35 
36 #define REGISTER_PATH_RESPONSE_HANDLER(path, method) \
37   request_handlers_.insert(std::make_pair( \
38         path, base::Bind(&FakeGaia::method, base::Unretained(this))))
39 
40 using namespace net::test_server;
41 
42 namespace {
43 
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";
53 
54 const base::FilePath::CharType kServiceLogin[] =
55     FILE_PATH_LITERAL("google_apis/test/service_login.html");
56 
57 // OAuth2 Authentication header value prefix.
58 const char kAuthHeaderBearer[] = "Bearer ";
59 const char kAuthHeaderOAuth[] = "OAuth ";
60 
61 const char kListAccountsResponseFormat[] =
62     "[\"gaia.l.a.r\",[[\"gaia.l.a\",1,\"\",\"%s\",\"\",1,1,0]]]";
63 
64 typedef std::map<std::string, std::string> CookieMap;
65 
66 // Parses cookie name-value map our of |request|.
GetRequestCookies(const HttpRequest & request)67 CookieMap GetRequestCookies(const HttpRequest& request) {
68   CookieMap result;
69   std::map<std::string, std::string>::const_iterator iter =
70            request.headers.find("Cookie");
71   if (iter != request.headers.end()) {
72     std::vector<std::string> cookie_nv_pairs;
73     base::SplitString(iter->second, ' ', &cookie_nv_pairs);
74     for(std::vector<std::string>::const_iterator cookie_line =
75             cookie_nv_pairs.begin();
76         cookie_line != cookie_nv_pairs.end();
77         ++cookie_line) {
78       std::vector<std::string> name_value;
79       base::SplitString(*cookie_line, '=', &name_value);
80       if (name_value.size() != 2)
81         continue;
82 
83       std::string value = name_value[1];
84       if (value.size() && value[value.size() - 1] == ';')
85         value = value.substr(0, value.size() -1);
86 
87       result.insert(std::make_pair(name_value[0], value));
88     }
89   }
90   return result;
91 }
92 
93 // Extracts the |access_token| from authorization header of |request|.
GetAccessToken(const HttpRequest & request,const char * auth_token_prefix,std::string * access_token)94 bool GetAccessToken(const HttpRequest& request,
95                     const char* auth_token_prefix,
96                     std::string* access_token) {
97   std::map<std::string, std::string>::const_iterator auth_header_entry =
98       request.headers.find("Authorization");
99   if (auth_header_entry != request.headers.end()) {
100     if (StartsWithASCII(auth_header_entry->second, auth_token_prefix, true)) {
101       *access_token = auth_header_entry->second.substr(
102           strlen(auth_token_prefix));
103       return true;
104     }
105   }
106 
107   return false;
108 }
109 
SetCookies(BasicHttpResponse * http_response,const std::string & sid_cookie,const std::string & lsid_cookie)110 void SetCookies(BasicHttpResponse* http_response,
111                 const std::string& sid_cookie,
112                 const std::string& lsid_cookie) {
113   http_response->AddCustomHeader(
114       "Set-Cookie",
115       base::StringPrintf("SID=%s; Path=/; HttpOnly", sid_cookie.c_str()));
116   http_response->AddCustomHeader(
117       "Set-Cookie",
118       base::StringPrintf("LSID=%s; Path=/; HttpOnly", lsid_cookie.c_str()));
119 }
120 
121 }  // namespace
122 
AccessTokenInfo()123 FakeGaia::AccessTokenInfo::AccessTokenInfo()
124   : expires_in(3600) {}
125 
~AccessTokenInfo()126 FakeGaia::AccessTokenInfo::~AccessTokenInfo() {}
127 
MergeSessionParams()128 FakeGaia::MergeSessionParams::MergeSessionParams() {
129 }
130 
~MergeSessionParams()131 FakeGaia::MergeSessionParams::~MergeSessionParams() {
132 }
133 
FakeGaia()134 FakeGaia::FakeGaia() {
135   base::FilePath source_root_dir;
136   PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir);
137   CHECK(base::ReadFileToString(
138       source_root_dir.Append(base::FilePath(kServiceLogin)),
139       &service_login_response_));
140 }
141 
~FakeGaia()142 FakeGaia::~FakeGaia() {}
143 
SetFakeMergeSessionParams(const std::string & email,const std::string & auth_sid_cookie,const std::string & auth_lsid_cookie)144 void FakeGaia::SetFakeMergeSessionParams(
145     const std::string& email,
146     const std::string& auth_sid_cookie,
147     const std::string& auth_lsid_cookie) {
148   FakeGaia::MergeSessionParams params;
149   params.auth_sid_cookie = auth_sid_cookie;
150   params.auth_lsid_cookie = auth_lsid_cookie;
151   params.auth_code = kTestAuthCode;
152   params.refresh_token = kTestRefreshToken;
153   params.access_token = kTestAuthLoginAccessToken;
154   params.gaia_uber_token = kTestGaiaUberToken;
155   params.session_sid_cookie = kTestSessionSIDCookie;
156   params.session_lsid_cookie = kTestSessionLSIDCookie;
157   params.email = email;
158   SetMergeSessionParams(params);
159 }
160 
SetMergeSessionParams(const MergeSessionParams & params)161 void FakeGaia::SetMergeSessionParams(
162     const MergeSessionParams& params) {
163   merge_session_params_ = params;
164 }
165 
Initialize()166 void FakeGaia::Initialize() {
167   GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
168   // Handles /MergeSession GAIA call.
169   REGISTER_RESPONSE_HANDLER(
170       gaia_urls->merge_session_url(), HandleMergeSession);
171 
172   // Handles /o/oauth2/programmatic_auth GAIA call.
173   REGISTER_RESPONSE_HANDLER(
174       gaia_urls->client_login_to_oauth2_url(), HandleProgramaticAuth);
175 
176   // Handles /ServiceLogin GAIA call.
177   REGISTER_RESPONSE_HANDLER(
178       gaia_urls->service_login_url(), HandleServiceLogin);
179 
180   // Handles /OAuthLogin GAIA call.
181   REGISTER_RESPONSE_HANDLER(
182       gaia_urls->oauth1_login_url(), HandleOAuthLogin);
183 
184   // Handles /ServiceLoginAuth GAIA call.
185   REGISTER_RESPONSE_HANDLER(
186       gaia_urls->service_login_auth_url(), HandleServiceLoginAuth);
187 
188   // Handles /SSO GAIA call (not GAIA, made up for SAML tests).
189   REGISTER_PATH_RESPONSE_HANDLER("/SSO", HandleSSO);
190 
191   // Handles /o/oauth2/token GAIA call.
192   REGISTER_RESPONSE_HANDLER(
193       gaia_urls->oauth2_token_url(), HandleAuthToken);
194 
195   // Handles /oauth2/v2/tokeninfo GAIA call.
196   REGISTER_RESPONSE_HANDLER(
197       gaia_urls->oauth2_token_info_url(), HandleTokenInfo);
198 
199   // Handles /oauth2/v2/IssueToken GAIA call.
200   REGISTER_RESPONSE_HANDLER(
201       gaia_urls->oauth2_issue_token_url(), HandleIssueToken);
202 
203   // Handles /ListAccounts GAIA call.
204   REGISTER_RESPONSE_HANDLER(
205       gaia_urls->list_accounts_url(), HandleListAccounts);
206 
207   // Handles /GetUserInfo GAIA call.
208   REGISTER_RESPONSE_HANDLER(
209       gaia_urls->get_user_info_url(), HandleGetUserInfo);
210 }
211 
HandleRequest(const HttpRequest & request)212 scoped_ptr<HttpResponse> FakeGaia::HandleRequest(const HttpRequest& request) {
213   // The scheme and host of the URL is actually not important but required to
214   // get a valid GURL in order to parse |request.relative_url|.
215   GURL request_url = GURL("http://localhost").Resolve(request.relative_url);
216   std::string request_path = request_url.path();
217   scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse());
218   RequestHandlerMap::iterator iter = request_handlers_.find(request_path);
219   if (iter != request_handlers_.end()) {
220     LOG(WARNING) << "Serving request " << request_path;
221     iter->second.Run(request, http_response.get());
222   } else {
223     LOG(ERROR) << "Unhandled request " << request_path;
224     return scoped_ptr<HttpResponse>();      // Request not understood.
225   }
226 
227   return http_response.PassAs<HttpResponse>();
228 }
229 
IssueOAuthToken(const std::string & auth_token,const AccessTokenInfo & token_info)230 void FakeGaia::IssueOAuthToken(const std::string& auth_token,
231                                const AccessTokenInfo& token_info) {
232   access_token_info_map_.insert(std::make_pair(auth_token, token_info));
233 }
234 
RegisterSamlUser(const std::string & account_id,const GURL & saml_idp)235 void FakeGaia::RegisterSamlUser(const std::string& account_id,
236                                 const GURL& saml_idp) {
237   saml_account_idp_map_[account_id] = saml_idp;
238 }
239 
240 // static
GetQueryParameter(const std::string & query,const std::string & key,std::string * value)241 bool FakeGaia::GetQueryParameter(const std::string& query,
242                                  const std::string& key,
243                                  std::string* value) {
244   // Name and scheme actually don't matter, but are required to get a valid URL
245   // for parsing.
246   GURL query_url("http://localhost?" + query);
247   return net::GetValueForKeyInQuery(query_url, key, value);
248 }
249 
HandleMergeSession(const HttpRequest & request,BasicHttpResponse * http_response)250 void FakeGaia::HandleMergeSession(const HttpRequest& request,
251                                   BasicHttpResponse* http_response) {
252   http_response->set_code(net::HTTP_UNAUTHORIZED);
253   if (merge_session_params_.session_sid_cookie.empty() ||
254       merge_session_params_.session_lsid_cookie.empty()) {
255     http_response->set_code(net::HTTP_BAD_REQUEST);
256     return;
257   }
258 
259   std::string uber_token;
260   if (!GetQueryParameter(request.content, "uberauth", &uber_token) ||
261       uber_token != merge_session_params_.gaia_uber_token) {
262     LOG(ERROR) << "Missing or invalid 'uberauth' param in /MergeSession call";
263     return;
264   }
265 
266   std::string continue_url;
267   if (!GetQueryParameter(request.content, "continue", &continue_url)) {
268     LOG(ERROR) << "Missing or invalid 'continue' param in /MergeSession call";
269     return;
270   }
271 
272   std::string source;
273   if (!GetQueryParameter(request.content, "source", &source)) {
274     LOG(ERROR) << "Missing or invalid 'source' param in /MergeSession call";
275     return;
276   }
277 
278   SetCookies(http_response,
279              merge_session_params_.session_sid_cookie,
280              merge_session_params_.session_lsid_cookie);
281   // TODO(zelidrag): Not used now.
282   http_response->set_content("OK");
283   http_response->set_code(net::HTTP_OK);
284 }
285 
HandleProgramaticAuth(const HttpRequest & request,BasicHttpResponse * http_response)286 void FakeGaia::HandleProgramaticAuth(
287     const HttpRequest& request,
288     BasicHttpResponse* http_response) {
289   http_response->set_code(net::HTTP_UNAUTHORIZED);
290   if (merge_session_params_.auth_code.empty()) {
291     http_response->set_code(net::HTTP_BAD_REQUEST);
292     return;
293   }
294 
295   GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
296   std::string scope;
297   if (!GetQueryParameter(request.content, "scope", &scope) ||
298       GaiaConstants::kOAuth1LoginScope != scope) {
299     return;
300   }
301 
302   CookieMap cookies = GetRequestCookies(request);
303   CookieMap::const_iterator sid_iter = cookies.find("SID");
304   if (sid_iter == cookies.end() ||
305       sid_iter->second != merge_session_params_.auth_sid_cookie) {
306     LOG(ERROR) << "/o/oauth2/programmatic_auth missing SID cookie";
307     return;
308   }
309   CookieMap::const_iterator lsid_iter = cookies.find("LSID");
310   if (lsid_iter == cookies.end() ||
311       lsid_iter->second != merge_session_params_.auth_lsid_cookie) {
312     LOG(ERROR) << "/o/oauth2/programmatic_auth missing LSID cookie";
313     return;
314   }
315 
316   std::string client_id;
317   if (!GetQueryParameter(request.content, "client_id", &client_id) ||
318       gaia_urls->oauth2_chrome_client_id() != client_id) {
319     return;
320   }
321 
322   http_response->AddCustomHeader(
323       "Set-Cookie",
324       base::StringPrintf(
325           "oauth_code=%s; Path=/o/GetOAuth2Token; Secure; HttpOnly;",
326           merge_session_params_.auth_code.c_str()));
327   http_response->set_code(net::HTTP_OK);
328   http_response->set_content_type("text/html");
329 }
330 
FormatJSONResponse(const base::DictionaryValue & response_dict,BasicHttpResponse * http_response)331 void FakeGaia::FormatJSONResponse(const base::DictionaryValue& response_dict,
332                                   BasicHttpResponse* http_response) {
333   std::string response_json;
334   base::JSONWriter::Write(&response_dict, &response_json);
335   http_response->set_content(response_json);
336   http_response->set_code(net::HTTP_OK);
337 }
338 
FindAccessTokenInfo(const std::string & auth_token,const std::string & client_id,const std::string & scope_string) const339 const FakeGaia::AccessTokenInfo* FakeGaia::FindAccessTokenInfo(
340     const std::string& auth_token,
341     const std::string& client_id,
342     const std::string& scope_string) const {
343   if (auth_token.empty() || client_id.empty())
344     return NULL;
345 
346   std::vector<std::string> scope_list;
347   base::SplitString(scope_string, ' ', &scope_list);
348   ScopeSet scopes(scope_list.begin(), scope_list.end());
349 
350   for (AccessTokenInfoMap::const_iterator entry(
351            access_token_info_map_.lower_bound(auth_token));
352        entry != access_token_info_map_.upper_bound(auth_token);
353        ++entry) {
354     if (entry->second.audience == client_id &&
355         (scope_string.empty() || entry->second.scopes == scopes)) {
356       return &(entry->second);
357     }
358   }
359 
360   return NULL;
361 }
362 
HandleServiceLogin(const HttpRequest & request,BasicHttpResponse * http_response)363 void FakeGaia::HandleServiceLogin(const HttpRequest& request,
364                                   BasicHttpResponse* http_response) {
365   http_response->set_code(net::HTTP_OK);
366   http_response->set_content(service_login_response_);
367   http_response->set_content_type("text/html");
368 }
369 
HandleOAuthLogin(const HttpRequest & request,BasicHttpResponse * http_response)370 void FakeGaia::HandleOAuthLogin(const HttpRequest& request,
371                                 BasicHttpResponse* http_response) {
372   http_response->set_code(net::HTTP_UNAUTHORIZED);
373   if (merge_session_params_.gaia_uber_token.empty()) {
374     http_response->set_code(net::HTTP_FORBIDDEN);
375     return;
376   }
377 
378   std::string access_token;
379   if (!GetAccessToken(request, kAuthHeaderBearer, &access_token) &&
380       !GetAccessToken(request, kAuthHeaderOAuth, &access_token)) {
381     LOG(ERROR) << "/OAuthLogin missing access token in the header";
382     return;
383   }
384 
385   GURL request_url = GURL("http://localhost").Resolve(request.relative_url);
386   std::string request_query = request_url.query();
387 
388   std::string source;
389   if (!GetQueryParameter(request_query, "source", &source) &&
390       !GetQueryParameter(request.content, "source", &source)) {
391     LOG(ERROR) << "Missing 'source' param in /OAuthLogin call";
392     return;
393   }
394 
395   std::string issue_uberauth;
396   if (GetQueryParameter(request_query, "issueuberauth", &issue_uberauth) &&
397       issue_uberauth == "1") {
398     http_response->set_content(merge_session_params_.gaia_uber_token);
399     http_response->set_code(net::HTTP_OK);
400     // Issue GAIA uber token.
401   } else {
402     http_response->set_content(base::StringPrintf(
403         "SID=%s\nLSID=%s\nAuth=%s",
404         kTestOAuthLoginSID, kTestOAuthLoginLSID, kTestOAuthLoginAuthCode));
405     http_response->set_code(net::HTTP_OK);
406   }
407 }
408 
HandleServiceLoginAuth(const HttpRequest & request,BasicHttpResponse * http_response)409 void FakeGaia::HandleServiceLoginAuth(const HttpRequest& request,
410                                       BasicHttpResponse* http_response) {
411   std::string continue_url =
412       GaiaUrls::GetInstance()->service_login_url().spec();
413   GetQueryParameter(request.content, "continue", &continue_url);
414 
415   std::string redirect_url = continue_url;
416 
417   std::string email;
418   if (GetQueryParameter(request.content, "Email", &email) &&
419       saml_account_idp_map_.find(email) != saml_account_idp_map_.end()) {
420     GURL url(saml_account_idp_map_[email]);
421     url = net::AppendQueryParameter(url, "SAMLRequest", "fake_request");
422     url = net::AppendQueryParameter(url, "RelayState", continue_url);
423     redirect_url = url.spec();
424     http_response->AddCustomHeader("Google-Accounts-SAML", "Start");
425   } else if (!merge_session_params_.auth_sid_cookie.empty() &&
426              !merge_session_params_.auth_lsid_cookie.empty()) {
427     SetCookies(http_response,
428                merge_session_params_.auth_sid_cookie,
429                merge_session_params_.auth_lsid_cookie);
430   }
431 
432   http_response->set_code(net::HTTP_TEMPORARY_REDIRECT);
433   http_response->AddCustomHeader("Location", redirect_url);
434   http_response->AddCustomHeader("google-accounts-signin",
435       base::StringPrintf("email=\"%s\", sessionindex=0", email.c_str()));
436 }
437 
HandleSSO(const HttpRequest & request,BasicHttpResponse * http_response)438 void FakeGaia::HandleSSO(const HttpRequest& request,
439                          BasicHttpResponse* http_response) {
440   if (!merge_session_params_.auth_sid_cookie.empty() &&
441       !merge_session_params_.auth_lsid_cookie.empty()) {
442     SetCookies(http_response,
443                merge_session_params_.auth_sid_cookie,
444                merge_session_params_.auth_lsid_cookie);
445   }
446   std::string relay_state;
447   GetQueryParameter(request.content, "RelayState", &relay_state);
448   std::string redirect_url = relay_state;
449   http_response->set_code(net::HTTP_TEMPORARY_REDIRECT);
450   http_response->AddCustomHeader("Location", redirect_url);
451   http_response->AddCustomHeader("Google-Accounts-SAML", "End");
452 }
453 
HandleAuthToken(const HttpRequest & request,BasicHttpResponse * http_response)454 void FakeGaia::HandleAuthToken(const HttpRequest& request,
455                                BasicHttpResponse* http_response) {
456   std::string scope;
457   GetQueryParameter(request.content, "scope", &scope);
458 
459   std::string grant_type;
460   if (!GetQueryParameter(request.content, "grant_type", &grant_type)) {
461     http_response->set_code(net::HTTP_BAD_REQUEST);
462     LOG(ERROR) << "No 'grant_type' param in /o/oauth2/token";
463     return;
464   }
465 
466   if (grant_type == "authorization_code") {
467     std::string auth_code;
468     if (!GetQueryParameter(request.content, "code", &auth_code) ||
469         auth_code != merge_session_params_.auth_code) {
470       http_response->set_code(net::HTTP_BAD_REQUEST);
471       LOG(ERROR) << "No 'code' param in /o/oauth2/token";
472       return;
473     }
474 
475     if (GaiaConstants::kOAuth1LoginScope != scope) {
476       http_response->set_code(net::HTTP_BAD_REQUEST);
477       LOG(ERROR) << "Invalid scope for /o/oauth2/token - " << scope;
478       return;
479     }
480 
481     base::DictionaryValue response_dict;
482     response_dict.SetString("refresh_token",
483                             merge_session_params_.refresh_token);
484     response_dict.SetString("access_token",
485                             merge_session_params_.access_token);
486     response_dict.SetInteger("expires_in", 3600);
487     FormatJSONResponse(response_dict, http_response);
488     return;
489   }
490 
491   std::string refresh_token;
492   std::string client_id;
493   if (GetQueryParameter(request.content, "refresh_token", &refresh_token) &&
494       GetQueryParameter(request.content, "client_id", &client_id)) {
495     const AccessTokenInfo* token_info =
496         FindAccessTokenInfo(refresh_token, client_id, scope);
497     if (token_info) {
498       base::DictionaryValue response_dict;
499       response_dict.SetString("access_token", token_info->token);
500       response_dict.SetInteger("expires_in", 3600);
501       FormatJSONResponse(response_dict, http_response);
502       return;
503     }
504   }
505 
506   LOG(ERROR) << "Bad request for /o/oauth2/token - "
507               << "refresh_token = " << refresh_token
508               << ", scope = " << scope
509               << ", client_id = " << client_id;
510   http_response->set_code(net::HTTP_BAD_REQUEST);
511 }
512 
HandleTokenInfo(const HttpRequest & request,BasicHttpResponse * http_response)513 void FakeGaia::HandleTokenInfo(const HttpRequest& request,
514                                BasicHttpResponse* http_response) {
515   const AccessTokenInfo* token_info = NULL;
516   std::string access_token;
517   if (GetQueryParameter(request.content, "access_token", &access_token)) {
518     for (AccessTokenInfoMap::const_iterator entry(
519              access_token_info_map_.begin());
520          entry != access_token_info_map_.end();
521          ++entry) {
522       if (entry->second.token == access_token) {
523         token_info = &(entry->second);
524         break;
525       }
526     }
527   }
528 
529   if (token_info) {
530     base::DictionaryValue response_dict;
531     response_dict.SetString("issued_to", token_info->issued_to);
532     response_dict.SetString("audience", token_info->audience);
533     response_dict.SetString("user_id", token_info->user_id);
534     std::vector<std::string> scope_vector(token_info->scopes.begin(),
535                                           token_info->scopes.end());
536     response_dict.SetString("scope", JoinString(scope_vector, " "));
537     response_dict.SetInteger("expires_in", token_info->expires_in);
538     response_dict.SetString("email", token_info->email);
539     FormatJSONResponse(response_dict, http_response);
540   } else {
541     http_response->set_code(net::HTTP_BAD_REQUEST);
542   }
543 }
544 
HandleIssueToken(const HttpRequest & request,BasicHttpResponse * http_response)545 void FakeGaia::HandleIssueToken(const HttpRequest& request,
546                                 BasicHttpResponse* http_response) {
547   std::string access_token;
548   std::string scope;
549   std::string client_id;
550   if (GetAccessToken(request, kAuthHeaderBearer, &access_token) &&
551       GetQueryParameter(request.content, "scope", &scope) &&
552       GetQueryParameter(request.content, "client_id", &client_id)) {
553     const AccessTokenInfo* token_info =
554         FindAccessTokenInfo(access_token, client_id, scope);
555     if (token_info) {
556       base::DictionaryValue response_dict;
557       response_dict.SetString("issueAdvice", "auto");
558       response_dict.SetString("expiresIn",
559                               base::IntToString(token_info->expires_in));
560       response_dict.SetString("token", token_info->token);
561       FormatJSONResponse(response_dict, http_response);
562       return;
563     }
564   }
565   http_response->set_code(net::HTTP_BAD_REQUEST);
566 }
567 
HandleListAccounts(const HttpRequest & request,BasicHttpResponse * http_response)568 void FakeGaia::HandleListAccounts(const HttpRequest& request,
569                                   BasicHttpResponse* http_response) {
570   http_response->set_content(base::StringPrintf(
571       kListAccountsResponseFormat, merge_session_params_.email.c_str()));
572   http_response->set_code(net::HTTP_OK);
573 }
574 
HandleGetUserInfo(const HttpRequest & request,BasicHttpResponse * http_response)575 void FakeGaia::HandleGetUserInfo(const HttpRequest& request,
576                                  BasicHttpResponse* http_response) {
577   http_response->set_content(base::StringPrintf(
578       "email=%s\ndisplayEmail=%s",
579       merge_session_params_.email.c_str(),
580       merge_session_params_.email.c_str()));
581   http_response->set_code(net::HTTP_OK);
582 }
583