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