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