gaia_auth_fetcher.h revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 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#ifndef GOOGLE_APIS_GAIA_GAIA_AUTH_FETCHER_H_
6#define GOOGLE_APIS_GAIA_GAIA_AUTH_FETCHER_H_
7
8#include <string>
9#include <vector>
10
11#include "base/gtest_prod_util.h"
12#include "base/memory/scoped_ptr.h"
13#include "google_apis/gaia/gaia_auth_consumer.h"
14#include "google_apis/gaia/google_service_auth_error.h"
15#include "googleurl/src/gurl.h"
16#include "net/url_request/url_fetcher_delegate.h"
17
18// Authenticate a user against the Google Accounts ClientLogin API
19// with various capabilities and return results to a GaiaAuthConsumer.
20//
21// In the future, we will also issue auth tokens from this class.
22// This class should be used on a single thread, but it can be whichever thread
23// that you like.
24//
25// This class can handle one request at a time on any thread. To parallelize
26// requests, create multiple GaiaAuthFetcher's.
27
28class GaiaAuthFetcherTest;
29
30namespace net {
31class URLFetcher;
32class URLRequestContextGetter;
33class URLRequestStatus;
34}
35
36class GaiaAuthFetcher : public net::URLFetcherDelegate {
37 public:
38  enum HostedAccountsSetting {
39    HostedAccountsAllowed,
40    HostedAccountsNotAllowed
41  };
42
43  // Magic string indicating that, while a second factor is still
44  // needed to complete authentication, the user provided the right password.
45  static const char kSecondFactor[];
46
47  // This will later be hidden behind an auth service which caches
48  // tokens.
49  GaiaAuthFetcher(GaiaAuthConsumer* consumer,
50                  const std::string& source,
51                  net::URLRequestContextGetter* getter);
52  virtual ~GaiaAuthFetcher();
53
54  // Start a request to obtain the SID and LSID cookies for the the account
55  // identified by |username| and |password|.  If |service| is not null or
56  // empty, then also obtains a service token for specified service.
57  //
58  // If this is a second call because of captcha challenge, then the
59  // |login_token| and |login_captcha| arugment should correspond to the
60  // solution of the challenge.
61  //
62  // Either OnClientLoginSuccess or OnClientLoginFailure will be
63  // called on the consumer on the original thread.
64  void StartClientLogin(const std::string& username,
65                        const std::string& password,
66                        const char* const service,
67                        const std::string& login_token,
68                        const std::string& login_captcha,
69                        HostedAccountsSetting allow_hosted_accounts);
70
71  // Start a request to obtain service token for the the account identified by
72  // |sid| and |lsid| and the service|service|.
73  //
74  // Either OnIssueAuthTokenSuccess or OnIssueAuthTokenFailure will be
75  // called on the consumer on the original thread.
76  void StartIssueAuthToken(const std::string& sid,
77                           const std::string& lsid,
78                           const char* const service);
79
80  // Start a request to exchange an "lso" service token given by |auth_token|
81  // for an OAuthLogin-scoped oauth2 token.
82  //
83  // Either OnClientOAuthSuccess or OnClientOAuthFailure will be
84  // called on the consumer on the original thread.
85  void StartLsoForOAuthLoginTokenExchange(const std::string& auth_token);
86
87  // Start a request to exchange the cookies of a signed-in user session
88  // for an OAuthLogin-scoped oauth2 token.  In the case of a session with
89  // multiple accounts signed in, |session_index| indicate the which of accounts
90  // within the session.
91  //
92  // Either OnClientOAuthSuccess or OnClientOAuthFailure will be
93  // called on the consumer on the original thread.
94  void StartCookieForOAuthLoginTokenExchange(const std::string& session_index);
95
96  // Start a request to get user info for the account identified by |lsid|.
97  //
98  // Either OnGetUserInfoSuccess or OnGetUserInfoFailure will be
99  // called on the consumer on the original thread.
100  void StartGetUserInfo(const std::string& lsid);
101
102  // Start a MergeSession request to pre-login the user with the given
103  // credentials.
104  //
105  // Start a MergeSession request to fill the browsing cookie jar with
106  // credentials represented by the account whose uber-auth token is
107  // |uber_token|.  This method will modify the cookies of the current profile.
108  //
109  // Either OnMergeSessionSuccess or OnMergeSessionFailure will be
110  // called on the consumer on the original thread.
111  void StartMergeSession(const std::string& uber_token);
112
113  // Start a request to exchange an OAuthLogin-scoped oauth2 access token for an
114  // uber-auth token.  The returned token can be used with the method
115  // StartMergeSession().
116  //
117  // Either OnUberAuthTokenSuccess or OnUberAuthTokenFailure will be
118  // called on the consumer on the original thread.
119  void StartTokenFetchForUberAuthExchange(const std::string& access_token);
120
121  // Start a request to obtain an OAuth2 token for the account identified by
122  // |username| and |password|.  |scopes| is a list of oauth scopes that
123  // indicate the access permerssions to assign to the returned token.
124  // |persistent_id| is an optional client identifier used to identify this
125  // particular chrome instances, which may reduce the chance of a challenge.
126  // |locale| will be used to format messages to be presented to the user in
127  // challenges, if needed.
128  //
129  // If the request cannot complete due to a challenge, the
130  // GoogleServiceAuthError will indicate the type of challenge required:
131  // either CAPTCHA_REQUIRED or TWO_FACTOR.
132  //
133  // Either OnClientOAuthSuccess or OnClientOAuthFailure will be
134  // called on the consumer on the original thread.
135  void StartClientOAuth(const std::string& username,
136                        const std::string& password,
137                        const std::vector<std::string>& scopes,
138                        const std::string& persistent_id,
139                        const std::string& locale);
140
141  // Start a challenge response to obtain an OAuth2 token.  This method is
142  // called after a challenge response is issued from a previous call to
143  // StartClientOAuth().  The |type| and |token| arguments come from the
144  // error response to StartClientOAuth(), while the |solution| argument
145  // represents the answer from the user for the partocular challenge.
146  //
147  // Either OnClientOAuthSuccess or OnClientOAuthFailure will be
148  // called on the consumer on the original thread.
149  void StartClientOAuthChallengeResponse(GoogleServiceAuthError::State type,
150                                         const std::string& token,
151                                         const std::string& solution);
152
153  // Start a request to exchange an OAuthLogin-scoped oauth2 access token for a
154  // ClientLogin-style service tokens.  The response to this request is the
155  // same as the response to a ClientLogin request, except that captcha
156  // challenges are never issued.
157  //
158  // Either OnClientLoginSuccess or OnClientLoginFailure will be
159  // called on the consumer on the original thread.
160  void StartOAuthLogin(const std::string& access_token,
161                       const std::string& service);
162
163  // Implementation of net::URLFetcherDelegate
164  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
165
166  // StartClientLogin been called && results not back yet?
167  bool HasPendingFetch();
168
169  // Stop any URL fetches in progress.
170  void CancelRequest();
171
172  // From a URLFetcher result, generate an appropriate error.
173  // From the API documentation, both IssueAuthToken and ClientLogin have
174  // the same error returns.
175  static GoogleServiceAuthError GenerateOAuthLoginError(
176      const std::string& data,
177      const net::URLRequestStatus& status);
178
179 private:
180  // ClientLogin body constants that don't change
181  static const char kCookiePersistence[];
182  static const char kAccountTypeHostedOrGoogle[];
183  static const char kAccountTypeGoogle[];
184
185  // The format of the POST body for ClientLogin.
186  static const char kClientLoginFormat[];
187  // The format of said POST body when CAPTCHA token & answer are specified.
188  static const char kClientLoginCaptchaFormat[];
189  // The format of the POST body for IssueAuthToken.
190  static const char kIssueAuthTokenFormat[];
191  // The format of the POST body to get OAuth2 auth code from auth token.
192  static const char kClientLoginToOAuth2BodyFormat[];
193  // The format of the POST body to get OAuth2 token pair from auth code.
194  static const char kOAuth2CodeToTokenPairBodyFormat[];
195  // The format of the POST body for GetUserInfo.
196  static const char kGetUserInfoFormat[];
197  // The format of the POST body for MergeSession.
198  static const char kMergeSessionFormat[];
199  // The format of the URL for UberAuthToken.
200  static const char kUberAuthTokenURLFormat[];
201  // The format of the body for OAuthLogin.
202  static const char kOAuthLoginFormat[];
203
204  // Constants for parsing ClientLogin errors.
205  static const char kAccountDeletedError[];
206  static const char kAccountDeletedErrorCode[];
207  static const char kAccountDisabledError[];
208  static const char kAccountDisabledErrorCode[];
209  static const char kBadAuthenticationError[];
210  static const char kBadAuthenticationErrorCode[];
211  static const char kCaptchaError[];
212  static const char kCaptchaErrorCode[];
213  static const char kServiceUnavailableError[];
214  static const char kServiceUnavailableErrorCode[];
215  static const char kErrorParam[];
216  static const char kErrorUrlParam[];
217  static const char kCaptchaUrlParam[];
218  static const char kCaptchaTokenParam[];
219
220  // Constants for parsing ClientOAuth errors.
221  static const char kNeedsAdditional[];
222  static const char kCaptcha[];
223  static const char kTwoFactor[];
224
225  // Constants for request/response for OAuth2 requests.
226  static const char kAuthHeaderFormat[];
227  static const char kOAuthHeaderFormat[];
228  static const char kClientLoginToOAuth2CookiePartSecure[];
229  static const char kClientLoginToOAuth2CookiePartHttpOnly[];
230  static const char kClientLoginToOAuth2CookiePartCodePrefix[];
231  static const int kClientLoginToOAuth2CookiePartCodePrefixLength;
232
233  // Process the results of a ClientLogin fetch.
234  void OnClientLoginFetched(const std::string& data,
235                            const net::URLRequestStatus& status,
236                            int response_code);
237
238  void OnIssueAuthTokenFetched(const std::string& data,
239                               const net::URLRequestStatus& status,
240                               int response_code);
241
242  void OnClientLoginToOAuth2Fetched(const std::string& data,
243                                    const net::ResponseCookies& cookies,
244                                    const net::URLRequestStatus& status,
245                                    int response_code);
246
247  void OnOAuth2TokenPairFetched(const std::string& data,
248                                const net::URLRequestStatus& status,
249                                int response_code);
250
251  void OnGetUserInfoFetched(const std::string& data,
252                            const net::URLRequestStatus& status,
253                            int response_code);
254
255  void OnMergeSessionFetched(const std::string& data,
256                             const net::URLRequestStatus& status,
257                             int response_code);
258
259  void OnUberAuthTokenFetch(const std::string& data,
260                            const net::URLRequestStatus& status,
261                            int response_code);
262
263  void OnClientOAuthFetched(const std::string& data,
264                            const net::URLRequestStatus& status,
265                            int response_code);
266
267  void OnOAuthLoginFetched(const std::string& data,
268                           const net::URLRequestStatus& status,
269                           int response_code);
270
271  // Tokenize the results of a ClientLogin fetch.
272  static void ParseClientLoginResponse(const std::string& data,
273                                       std::string* sid,
274                                       std::string* lsid,
275                                       std::string* token);
276
277  static void ParseClientLoginFailure(const std::string& data,
278                                      std::string* error,
279                                      std::string* error_url,
280                                      std::string* captcha_url,
281                                      std::string* captcha_token);
282
283  // Parse ClientLogin to OAuth2 response.
284  static bool ParseClientLoginToOAuth2Response(
285      const net::ResponseCookies& cookies,
286      std::string* auth_code);
287
288  static bool ParseClientLoginToOAuth2Cookie(const std::string& cookie,
289                                             std::string* auth_code);
290
291  static GoogleServiceAuthError GenerateClientOAuthError(
292      const std::string& data,
293      const net::URLRequestStatus& status);
294
295  // Is this a special case Gaia error for TwoFactor auth?
296  static bool IsSecondFactorSuccess(const std::string& alleged_error);
297
298  // Given parameters, create a ClientLogin request body.
299  static std::string MakeClientLoginBody(
300      const std::string& username,
301      const std::string& password,
302      const std::string& source,
303      const char* const service,
304      const std::string& login_token,
305      const std::string& login_captcha,
306      HostedAccountsSetting allow_hosted_accounts);
307  // Supply the sid / lsid returned from ClientLogin in order to
308  // request a long lived auth token for a service.
309  static std::string MakeIssueAuthTokenBody(const std::string& sid,
310                                            const std::string& lsid,
311                                            const char* const service);
312  // Create body to get OAuth2 auth code.
313  static std::string MakeGetAuthCodeBody();
314  // Given auth code, create body to get OAuth2 token pair.
315  static std::string MakeGetTokenPairBody(const std::string& auth_code);
316  // Supply the lsid returned from ClientLogin in order to fetch
317  // user information.
318  static std::string MakeGetUserInfoBody(const std::string& lsid);
319
320  // Supply the authentication token returned from StartIssueAuthToken.
321  static std::string MakeMergeSessionBody(const std::string& auth_token,
322                                       const std::string& continue_url,
323                                       const std::string& source);
324
325  static std::string MakeGetAuthCodeHeader(const std::string& auth_token);
326
327  static std::string MakeClientOAuthBody(const std::string& username,
328                                         const std::string& password,
329                                         const std::vector<std::string>& scopes,
330                                         const std::string& persistent_id,
331                                         const std::string& friendly_name,
332                                         const std::string& locale);
333
334  static std::string MakeClientOAuthChallengeResponseBody(
335      const std::string& name,
336      const std::string& token,
337      const std::string& solution);
338
339  static std::string MakeOAuthLoginBody(const std::string& service,
340                                        const std::string& source);
341
342  void StartOAuth2TokenPairFetch(const std::string& auth_code);
343
344  // Create a fetcher usable for making any Gaia request.  |body| is used
345  // as the body of the POST request sent to GAIA.  Any strings listed in
346  // |headers| are added as extra HTTP headers in the request.
347  //
348  // |load_flags| are passed to directly to net::URLFetcher::Create() when
349  // creating the URL fetcher.
350  static net::URLFetcher* CreateGaiaFetcher(
351      net::URLRequestContextGetter* getter,
352      const std::string& body,
353      const std::string& headers,
354      const GURL& gaia_gurl,
355      int load_flags,
356      net::URLFetcherDelegate* delegate);
357
358  // From a URLFetcher result, generate an appropriate error.
359  // From the API documentation, both IssueAuthToken and ClientLogin have
360  // the same error returns.
361  static GoogleServiceAuthError GenerateAuthError(
362      const std::string& data,
363      const net::URLRequestStatus& status);
364
365  // These fields are common to GaiaAuthFetcher, same every request
366  GaiaAuthConsumer* const consumer_;
367  net::URLRequestContextGetter* const getter_;
368  std::string source_;
369  const GURL client_login_gurl_;
370  const GURL issue_auth_token_gurl_;
371  const GURL oauth2_token_gurl_;
372  const GURL get_user_info_gurl_;
373  const GURL merge_session_gurl_;
374  const GURL uberauth_token_gurl_;
375  const GURL client_oauth_gurl_;
376  const GURL oauth_login_gurl_;
377
378  // While a fetch is going on:
379  scoped_ptr<net::URLFetcher> fetcher_;
380  GURL client_login_to_oauth2_gurl_;
381  std::string request_body_;
382  std::string requested_service_; // Currently tracked for IssueAuthToken only.
383  bool fetch_pending_;
384
385  friend class GaiaAuthFetcherTest;
386  FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, CaptchaParse);
387  FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, AccountDeletedError);
388  FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, AccountDisabledError);
389  FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, BadAuthenticationError);
390  FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, IncomprehensibleError);
391  FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, ServiceUnavailableError);
392  FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, CheckNormalErrorCode);
393  FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, CheckTwoFactorResponse);
394  FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, LoginNetFailure);
395  FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest,
396      ParseClientLoginToOAuth2Response);
397  FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, ParseOAuth2TokenPairResponse);
398  FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, ClientOAuthSuccess);
399  FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, ClientOAuthWithQuote);
400  FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, ClientOAuthChallengeSuccess);
401  FRIEND_TEST_ALL_PREFIXES(GaiaAuthFetcherTest, ClientOAuthChallengeQuote);
402
403  DISALLOW_COPY_AND_ASSIGN(GaiaAuthFetcher);
404};
405
406#endif  // GOOGLE_APIS_GAIA_GAIA_AUTH_FETCHER_H_
407