Home | History | Annotate | Download | only in gaia
      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|.
     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|.
     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 
    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 
    123 FakeGaia::AccessTokenInfo::AccessTokenInfo()
    124   : expires_in(3600) {}
    125 
    126 FakeGaia::AccessTokenInfo::~AccessTokenInfo() {}
    127 
    128 FakeGaia::MergeSessionParams::MergeSessionParams() {
    129 }
    130 
    131 FakeGaia::MergeSessionParams::~MergeSessionParams() {
    132 }
    133 
    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 
    142 FakeGaia::~FakeGaia() {}
    143 
    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 
    161 void FakeGaia::SetMergeSessionParams(
    162     const MergeSessionParams& params) {
    163   merge_session_params_ = params;
    164 }
    165 
    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 
    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 
    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 
    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
    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 
    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 
    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 
    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 
    339 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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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