• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
24 namespace net {
25 class URLRequestContextGetter;
26 }
27 
28 class GoogleServiceAuthError;
29 class 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.
54 class 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 
id()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.
OnRefreshTokenAvailable(const std::string & account_id)96     virtual void OnRefreshTokenAvailable(const std::string& account_id) {}
97     // Called whenever the login-scoped refresh token becomes unavailable for
98     // account |account_id|.
OnRefreshTokenRevoked(const std::string & account_id)99     virtual void OnRefreshTokenRevoked(const std::string& account_id) {}
100     // Called after all refresh tokens are loaded during OAuth2TokenService
101     // startup.
OnRefreshTokensLoaded()102     virtual void OnRefreshTokensLoaded() {}
103     // Sent before starting a batch of refresh token changes.
OnStartBatchChanges()104     virtual void OnStartBatchChanges() {}
105     // Sent after a batch of refresh token changes is done.
OnEndBatchChanges()106     virtual void OnEndBatchChanges() {}
107 
108    protected:
~Observer()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