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