1// Copyright 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#ifndef GOOGLE_APIS_GAIA_OAUTH2_TOKEN_SERVICE_H_
6#define GOOGLE_APIS_GAIA_OAUTH2_TOKEN_SERVICE_H_
7
8#include <map>
9#include <set>
10#include <string>
11
12#include "base/basictypes.h"
13#include "base/gtest_prod_util.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/memory/weak_ptr.h"
16#include "base/observer_list.h"
17#include "base/threading/non_thread_safe.h"
18#include "base/time/time.h"
19#include "base/timer/timer.h"
20#include "google_apis/gaia/google_service_auth_error.h"
21#include "google_apis/gaia/oauth2_access_token_consumer.h"
22#include "google_apis/gaia/oauth2_access_token_fetcher.h"
23
24namespace net {
25class URLRequestContextGetter;
26}
27
28class GoogleServiceAuthError;
29class OAuth2AccessTokenFetcher;
30
31// Abstract base class for a service that fetches and caches OAuth2 access
32// tokens. Concrete subclasses should implement GetRefreshToken to return
33// the appropriate refresh token. Derived services might maintain refresh tokens
34// for multiple accounts.
35//
36// All calls are expected from the UI thread.
37//
38// To use this service, call StartRequest() with a given set of scopes and a
39// consumer of the request results. The consumer is required to outlive the
40// request. The request can be deleted. The consumer may be called back
41// asynchronously with the fetch results.
42//
43// - If the consumer is not called back before the request is deleted, it will
44//   never be called back.
45//   Note in this case, the actual network requests are not canceled and the
46//   cache will be populated with the fetched results; it is just the consumer
47//   callback that is aborted.
48//
49// - Otherwise the consumer will be called back with the request and the fetch
50//   results.
51//
52// The caller of StartRequest() owns the returned request and is responsible to
53// delete the request even once the callback has been invoked.
54class OAuth2TokenService : public base::NonThreadSafe {
55 public:
56  // A set of scopes in OAuth2 authentication.
57  typedef std::set<std::string> ScopeSet;
58
59  // Class representing a request that fetches an OAuth2 access token.
60  class Request {
61   public:
62    virtual ~Request();
63    virtual std::string GetAccountId() const = 0;
64   protected:
65    Request();
66  };
67
68  // Class representing the consumer of a Request passed to |StartRequest|,
69  // which will be called back when the request completes.
70  class Consumer {
71   public:
72    Consumer(const std::string& id);
73    virtual ~Consumer();
74
75    std::string id() const { return id_; }
76
77    // |request| is a Request that is started by this consumer and has
78    // completed.
79    virtual void OnGetTokenSuccess(const Request* request,
80                                   const std::string& access_token,
81                                   const base::Time& expiration_time) = 0;
82    virtual void OnGetTokenFailure(const Request* request,
83                                   const GoogleServiceAuthError& error) = 0;
84   private:
85    std::string id_;
86  };
87
88  // Classes that want to listen for refresh token availability should
89  // implement this interface and register with the AddObserver() call.
90  class Observer {
91   public:
92    // Called whenever a new login-scoped refresh token is available for
93    // account |account_id|. Once available, access tokens can be retrieved for
94    // this account.  This is called during initial startup for each token
95    // loaded.
96    virtual void OnRefreshTokenAvailable(const std::string& account_id) {}
97    // Called whenever the login-scoped refresh token becomes unavailable for
98    // account |account_id|.
99    virtual void OnRefreshTokenRevoked(const std::string& account_id) {}
100    // Called after all refresh tokens are loaded during OAuth2TokenService
101    // startup.
102    virtual void OnRefreshTokensLoaded() {}
103    // Sent before starting a batch of refresh token changes.
104    virtual void OnStartBatchChanges() {}
105    // Sent after a batch of refresh token changes is done.
106    virtual void OnEndBatchChanges() {}
107
108   protected:
109    virtual ~Observer() {}
110  };
111
112  // Classes that want to monitor status of access token and access token
113  // request should implement this interface and register with the
114  // AddDiagnosticsObserver() call.
115  class DiagnosticsObserver {
116   public:
117    // Called when receiving request for access token.
118    virtual void OnAccessTokenRequested(const std::string& account_id,
119                                        const std::string& consumer_id,
120                                        const ScopeSet& scopes) = 0;
121    // Called when access token fetching finished successfully or
122    // unsuccessfully. |expiration_time| are only valid with
123    // successful completion.
124    virtual void OnFetchAccessTokenComplete(const std::string& account_id,
125                                            const std::string& consumer_id,
126                                            const ScopeSet& scopes,
127                                            GoogleServiceAuthError error,
128                                            base::Time expiration_time) = 0;
129    virtual void OnTokenRemoved(const std::string& account_id,
130                                const ScopeSet& scopes) = 0;
131  };
132
133  OAuth2TokenService();
134  virtual ~OAuth2TokenService();
135
136  // Add or remove observers of this token service.
137  void AddObserver(Observer* observer);
138  void RemoveObserver(Observer* observer);
139
140  // Add or remove observers of this token service.
141  void AddDiagnosticsObserver(DiagnosticsObserver* observer);
142  void RemoveDiagnosticsObserver(DiagnosticsObserver* observer);
143
144  // Checks in the cache for a valid access token for a specified |account_id|
145  // and |scopes|, and if not found starts a request for an OAuth2 access token
146  // using the OAuth2 refresh token maintained by this instance for that
147  // |account_id|. The caller owns the returned Request.
148  // |scopes| is the set of scopes to get an access token for, |consumer| is
149  // the object that will be called back with results if the returned request
150  // is not deleted.
151  scoped_ptr<Request> StartRequest(const std::string& account_id,
152                                   const ScopeSet& scopes,
153                                   Consumer* consumer);
154
155  // This method does the same as |StartRequest| except it uses |client_id| and
156  // |client_secret| to identify OAuth client app instead of using
157  // Chrome's default values.
158  scoped_ptr<Request> StartRequestForClient(
159      const std::string& account_id,
160      const std::string& client_id,
161      const std::string& client_secret,
162      const ScopeSet& scopes,
163      Consumer* consumer);
164
165  // This method does the same as |StartRequest| except it uses the request
166  // context given by |getter| instead of using the one returned by
167  // |GetRequestContext| implemented by derived classes.
168  scoped_ptr<Request> StartRequestWithContext(
169      const std::string& account_id,
170      net::URLRequestContextGetter* getter,
171      const ScopeSet& scopes,
172      Consumer* consumer);
173
174  // Lists account IDs of all accounts with a refresh token maintained by this
175  // instance.
176  virtual std::vector<std::string> GetAccounts();
177
178  // Returns true if a refresh token exists for |account_id|. If false, calls to
179  // |StartRequest| will result in a Consumer::OnGetTokenFailure callback.
180  virtual bool RefreshTokenIsAvailable(const std::string& account_id) const = 0;
181
182  // Mark an OAuth2 |access_token| issued for |account_id| and |scopes| as
183  // invalid. This should be done if the token was received from this class,
184  // but was not accepted by the server (e.g., the server returned
185  // 401 Unauthorized). The token will be removed from the cache for the given
186  // scopes.
187  void InvalidateToken(const std::string& account_id,
188                       const ScopeSet& scopes,
189                       const std::string& access_token);
190
191  // Like |InvalidateToken| except is uses |client_id| to identity OAuth2 client
192  // app that issued the request instead of Chrome's default values.
193  void InvalidateTokenForClient(const std::string& account_id,
194                                const std::string& client_id,
195                                const ScopeSet& scopes,
196                                const std::string& access_token);
197
198
199  // Return the current number of entries in the cache.
200  int cache_size_for_testing() const;
201  void set_max_authorization_token_fetch_retries_for_testing(int max_retries);
202  // Returns the current number of pending fetchers matching given params.
203  size_t GetNumPendingRequestsForTesting(
204      const std::string& client_id,
205      const std::string& account_id,
206      const ScopeSet& scopes) const;
207
208 protected:
209  // Implements a cancelable |OAuth2TokenService::Request|, which should be
210  // operated on the UI thread.
211  // TODO(davidroche): move this out of header file.
212  class RequestImpl : public base::SupportsWeakPtr<RequestImpl>,
213                      public base::NonThreadSafe,
214                      public Request {
215   public:
216    // |consumer| is required to outlive this.
217    explicit RequestImpl(const std::string& account_id, Consumer* consumer);
218    virtual ~RequestImpl();
219
220    // Overridden from Request:
221    virtual std::string GetAccountId() const OVERRIDE;
222
223    std::string GetConsumerId() const;
224
225    // Informs |consumer_| that this request is completed.
226    void InformConsumer(const GoogleServiceAuthError& error,
227                        const std::string& access_token,
228                        const base::Time& expiration_date);
229
230   private:
231    // |consumer_| to call back when this request completes.
232    const std::string account_id_;
233    Consumer* const consumer_;
234  };
235
236  // Helper class to scope batch changes.
237  class ScopedBacthChange {
238   public:
239    ScopedBacthChange(OAuth2TokenService* token_service);
240    ~ScopedBacthChange();
241   private:
242    OAuth2TokenService* token_service_;  // Weak.
243    DISALLOW_COPY_AND_ASSIGN(ScopedBacthChange);
244  };
245
246  // Subclasses can override if they want to report errors to the user.
247  virtual void UpdateAuthError(
248      const std::string& account_id,
249      const GoogleServiceAuthError& error);
250
251  // Add a new entry to the cache.
252  // Subclasses can override if there are implementation-specific reasons
253  // that an access token should ever not be cached.
254  virtual void RegisterCacheEntry(const std::string& client_id,
255                                  const std::string& account_id,
256                                  const ScopeSet& scopes,
257                                  const std::string& access_token,
258                                  const base::Time& expiration_date);
259
260  // Clears the internal token cache.
261  void ClearCache();
262
263  // Clears all of the tokens belonging to |account_id| from the internal token
264  // cache. It does not matter what other parameters, like |client_id| were
265  // used to request the tokens.
266  void ClearCacheForAccount(const std::string& account_id);
267
268  // Cancels all requests that are currently in progress.
269  void CancelAllRequests();
270
271  // Cancels all requests related to a given |account_id|.
272  void CancelRequestsForAccount(const std::string& account_id);
273
274  // Called by subclasses to notify observers.
275  virtual void FireRefreshTokenAvailable(const std::string& account_id);
276  virtual void FireRefreshTokenRevoked(const std::string& account_id);
277  virtual void FireRefreshTokensLoaded();
278
279  virtual void StartBatchChanges();
280  virtual void EndBatchChanges();
281
282  // Fetches an OAuth token for the specified client/scopes. Virtual so it can
283  // be overridden for tests and for platform-specific behavior on Android.
284  virtual void FetchOAuth2Token(RequestImpl* request,
285                                const std::string& account_id,
286                                net::URLRequestContextGetter* getter,
287                                const std::string& client_id,
288                                const std::string& client_secret,
289                                const ScopeSet& scopes);
290
291  // Creates an access token fetcher for the given account id.
292  //
293  // Subclasses should override to create an access token fetcher for the given
294  // |account_id|. This method is only called if subclasses use the default
295  // implementation of |FetchOAuth2Token|.
296  virtual OAuth2AccessTokenFetcher* CreateAccessTokenFetcher(
297      const std::string& account_id,
298      net::URLRequestContextGetter* getter,
299      OAuth2AccessTokenConsumer* consumer) = 0;
300
301  // Invalidates the |access_token| issued for |account_id|, |client_id| and
302  // |scopes|. Virtual so it can be overriden for tests and for platform-
303  // specifc behavior.
304  virtual void InvalidateOAuth2Token(const std::string& account_id,
305                                     const std::string& client_id,
306                                     const ScopeSet& scopes,
307                                     const std::string& access_token);
308
309 private:
310  class Fetcher;
311  friend class Fetcher;
312
313  // The parameters used to fetch an OAuth2 access token.
314  struct RequestParameters {
315    RequestParameters(const std::string& client_id,
316                      const std::string& account_id,
317                      const ScopeSet& scopes);
318    ~RequestParameters();
319    bool operator<(const RequestParameters& params) const;
320
321    // OAuth2 client id.
322    std::string client_id;
323    // Account id for which the request is made.
324    std::string account_id;
325    // URL scopes for the requested access token.
326    ScopeSet scopes;
327  };
328
329  typedef std::map<RequestParameters, Fetcher*> PendingFetcherMap;
330
331  // Derived classes must provide a request context used for fetching access
332  // tokens with the |StartRequest| method.
333  virtual net::URLRequestContextGetter* GetRequestContext() = 0;
334
335  // Struct that contains the information of an OAuth2 access token.
336  struct CacheEntry {
337    std::string access_token;
338    base::Time expiration_date;
339  };
340
341  // This method does the same as |StartRequestWithContext| except it
342  // uses |client_id| and |client_secret| to identify OAuth
343  // client app instead of using Chrome's default values.
344  scoped_ptr<Request> StartRequestForClientWithContext(
345      const std::string& account_id,
346      net::URLRequestContextGetter* getter,
347      const std::string& client_id,
348      const std::string& client_secret,
349      const ScopeSet& scopes,
350      Consumer* consumer);
351
352  // Returns true if GetCacheEntry would return a valid cache entry for the
353  // given scopes.
354  bool HasCacheEntry(const RequestParameters& client_scopes);
355
356  // Posts a task to fire the Consumer callback with the cached token.  Must
357  // Must only be called if HasCacheEntry() returns true.
358  void StartCacheLookupRequest(RequestImpl* request,
359                               const RequestParameters& client_scopes,
360                               Consumer* consumer);
361
362  // Returns a currently valid OAuth2 access token for the given set of scopes,
363  // or NULL if none have been cached. Note the user of this method should
364  // ensure no entry with the same |client_scopes| is added before the usage of
365  // the returned entry is done.
366  const CacheEntry* GetCacheEntry(const RequestParameters& client_scopes);
367
368  // Removes an access token for the given set of scopes from the cache.
369  // Returns true if the entry was removed, otherwise false.
370  bool RemoveCacheEntry(const RequestParameters& client_scopes,
371                        const std::string& token_to_remove);
372
373  // Called when |fetcher| finishes fetching.
374  void OnFetchComplete(Fetcher* fetcher);
375
376  // Called when a number of fetchers need to be canceled.
377  void CancelFetchers(std::vector<Fetcher*> fetchers_to_cancel);
378
379  // The cache of currently valid tokens.
380  typedef std::map<RequestParameters, CacheEntry> TokenCache;
381  TokenCache token_cache_;
382
383  // A map from fetch parameters to a fetcher that is fetching an OAuth2 access
384  // token using these parameters.
385  PendingFetcherMap pending_fetchers_;
386
387  // List of observers to notify when refresh token availability changes.
388  // Makes sure list is empty on destruction.
389  ObserverList<Observer, true> observer_list_;
390
391  // List of observers to notify when access token status changes.
392  ObserverList<DiagnosticsObserver, true> diagnostics_observer_list_;
393
394  // The depth of batch changes.
395  int batch_change_depth_;
396
397  // Maximum number of retries in fetching an OAuth2 access token.
398  static int max_fetch_retry_num_;
399
400  FRIEND_TEST_ALL_PREFIXES(OAuth2TokenServiceTest, RequestParametersOrderTest);
401  FRIEND_TEST_ALL_PREFIXES(OAuth2TokenServiceTest,
402                           SameScopesRequestedForDifferentClients);
403
404  DISALLOW_COPY_AND_ASSIGN(OAuth2TokenService);
405};
406
407#endif  // GOOGLE_APIS_GAIA_OAUTH2_TOKEN_SERVICE_H_
408