identity_api.h revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
1b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// Use of this source code is governed by a BSD-style license that can be 3b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// found in the LICENSE file. 4b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar 59ad441ffec97db647fee3725b3424284fb913e14Howard Hinnant#ifndef CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_ 69ad441ffec97db647fee3725b3424284fb913e14Howard Hinnant#define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_ 7b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar 8b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include <map> 9b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include <set> 10b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include <string> 11b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include <utility> 12b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include <vector> 13b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar 14b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "base/memory/ref_counted.h" 15b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "base/memory/weak_ptr.h" 16b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "chrome/browser/extensions/api/identity/gaia_web_auth_flow.h" 17b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "chrome/browser/extensions/api/identity/identity_mint_queue.h" 18b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "chrome/browser/extensions/api/identity/identity_signin_flow.h" 19b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "chrome/browser/extensions/api/identity/web_auth_flow.h" 20b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "chrome/browser/extensions/api/profile_keyed_api_factory.h" 21b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "chrome/browser/extensions/extension_function.h" 22b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "chrome/browser/signin/oauth2_token_service.h" 23b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "chrome/browser/signin/signin_global_error.h" 24b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar#include "google_apis/gaia/oauth2_mint_token_flow.h" 25b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar 26b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarclass GoogleServiceAuthError; 27b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarclass MockGetAuthTokenFunction; 28b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarclass Profile; 29b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarclass SigninManagerBase; 30b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar 31b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarnamespace extensions { 32b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar 33b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarclass GetAuthTokenFunctionTest; 34b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarclass MockGetAuthTokenFunction; 35b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar 36b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarnamespace identity_constants { 37b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarextern const char kInvalidClientId[]; 38b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarextern const char kInvalidScopes[]; 39b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarextern const char kAuthFailure[]; 40b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarextern const char kNoGrant[]; 41b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarextern const char kUserRejected[]; 42b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarextern const char kUserNotSignedIn[]; 43b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarextern const char kInteractionRequired[]; 44b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarextern const char kInvalidRedirect[]; 45b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarextern const char kOffTheRecord[]; 46b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarextern const char kPageLoadFailure[]; 47b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar} // namespace identity_constants 48b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar 49b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// identity.getAuthToken fetches an OAuth 2 function for the 50b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// caller. The request has three sub-flows: non-interactive, 51b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// interactive, and sign-in. 52b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// 53b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// In the non-interactive flow, getAuthToken requests a token from 54b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// GAIA. GAIA may respond with a token, an error, or "consent 55b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// required". In the consent required cases, getAuthToken proceeds to 56b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// the second, interactive phase. 57b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// 58b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// The interactive flow presents a scope approval dialog to the 59b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// user. If the user approves the request, a grant will be recorded on 60b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// the server, and an access token will be returned to the caller. 61b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// 62b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// In some cases we need to display a sign-in dialog. Normally the 63b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// profile will be signed in already, but if it turns out we need a 64b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// new login token, there is a sign-in flow. If that flow completes 65b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar// successfully, getAuthToken proceeds to the non-interactive flow. 66b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbarclass IdentityGetAuthTokenFunction : public AsyncExtensionFunction, 67b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar public GaiaWebAuthFlow::Delegate, 68b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar public IdentityMintRequestQueue::Request, 69b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar public OAuth2MintTokenFlow::Delegate, 70b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar public IdentitySigninFlow::Delegate, 71b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar public OAuth2TokenService::Consumer { 72b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar public: 73b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar DECLARE_EXTENSION_FUNCTION("identity.getAuthToken", 74b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar EXPERIMENTAL_IDENTITY_GETAUTHTOKEN); 75b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar 76b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar IdentityGetAuthTokenFunction(); 77b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar 78b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar protected: 79b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar virtual ~IdentityGetAuthTokenFunction(); 80b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar 81b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar private: 82b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar FRIEND_TEST_ALL_PREFIXES(GetAuthTokenFunctionTest, 83b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar ComponentWithChromeClientId); 84b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar FRIEND_TEST_ALL_PREFIXES(GetAuthTokenFunctionTest, 85b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar ComponentWithNormalClientId); 86b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar friend class MockGetAuthTokenFunction; 87b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar 88b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar // ExtensionFunction: 89b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar virtual bool RunImpl() OVERRIDE; 90b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar 91b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar // Helpers to report async function results to the caller. 92b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar void CompleteFunctionWithResult(const std::string& access_token); 93b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar void CompleteFunctionWithError(const std::string& error); 94b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar 95b3a6901e66f55b35aa9e01bcb24134e6a65ea004Daniel Dunbar // Initiate/complete the sub-flows. 96 void StartSigninFlow(); 97 void StartMintTokenFlow(IdentityMintRequestQueue::MintType type); 98 void CompleteMintTokenFlow(); 99 100 // IdentityMintRequestQueue::Request implementation: 101 virtual void StartMintToken(IdentityMintRequestQueue::MintType type) OVERRIDE; 102 103 // OAuth2MintTokenFlow::Delegate implementation: 104 virtual void OnMintTokenSuccess(const std::string& access_token, 105 int time_to_live) OVERRIDE; 106 virtual void OnMintTokenFailure( 107 const GoogleServiceAuthError& error) OVERRIDE; 108 virtual void OnIssueAdviceSuccess( 109 const IssueAdviceInfo& issue_advice) OVERRIDE; 110 111 // IdentitySigninFlow::Delegate implementation: 112 virtual void SigninSuccess(const std::string& token) OVERRIDE; 113 virtual void SigninFailed() OVERRIDE; 114 115 // GaiaWebAuthFlow::Delegate implementation: 116 virtual void OnGaiaFlowFailure(GaiaWebAuthFlow::Failure failure, 117 GoogleServiceAuthError service_error, 118 const std::string& oauth_error) OVERRIDE; 119 virtual void OnGaiaFlowCompleted(const std::string& access_token, 120 const std::string& expiration) OVERRIDE; 121 122 // OAuth2TokenService::Consumer implementation: 123 virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request, 124 const std::string& access_token, 125 const base::Time& expiration_time) OVERRIDE; 126 virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request, 127 const GoogleServiceAuthError& error) OVERRIDE; 128 129 // Starts a mint token request to GAIA. 130 void StartGaiaRequest(OAuth2MintTokenFlow::Mode mode); 131 132 // Methods for invoking UI. Overridable for testing. 133 virtual void ShowLoginPopup(); 134 virtual void ShowOAuthApprovalDialog(const IssueAdviceInfo& issue_advice); 135 // Caller owns the returned instance. 136 virtual OAuth2MintTokenFlow* CreateMintTokenFlow( 137 OAuth2MintTokenFlow::Mode mode); 138 139 // Checks if there is a master login token to mint tokens for the extension. 140 virtual bool HasLoginToken() const; 141 142 // Maps OAuth2 protocol errors to an error message returned to the 143 // developer in chrome.runtime.lastError. 144 std::string MapOAuth2ErrorToDescription(const std::string& error); 145 146 std::string GetOAuth2ClientId() const; 147 148 bool should_prompt_for_scopes_; 149 IdentityMintRequestQueue::MintType mint_token_flow_type_; 150 scoped_ptr<OAuth2MintTokenFlow> mint_token_flow_; 151 std::string refresh_token_; 152 bool should_prompt_for_signin_; 153 154 std::string oauth2_client_id_; 155 // When launched in interactive mode, and if there is no existing grant, 156 // a permissions prompt will be popped up to the user. 157 IssueAdviceInfo issue_advice_; 158 scoped_ptr<GaiaWebAuthFlow> gaia_web_auth_flow_; 159 scoped_ptr<IdentitySigninFlow> signin_flow_; 160 scoped_ptr<OAuth2TokenService::Request> device_token_request_; 161}; 162 163class IdentityRemoveCachedAuthTokenFunction : public SyncExtensionFunction { 164 public: 165 DECLARE_EXTENSION_FUNCTION("identity.removeCachedAuthToken", 166 EXPERIMENTAL_IDENTITY_REMOVECACHEDAUTHTOKEN) 167 IdentityRemoveCachedAuthTokenFunction(); 168 169 protected: 170 virtual ~IdentityRemoveCachedAuthTokenFunction(); 171 172 // SyncExtensionFunction implementation: 173 virtual bool RunImpl() OVERRIDE; 174}; 175 176class IdentityLaunchWebAuthFlowFunction : public AsyncExtensionFunction, 177 public WebAuthFlow::Delegate { 178 public: 179 DECLARE_EXTENSION_FUNCTION("identity.launchWebAuthFlow", 180 EXPERIMENTAL_IDENTITY_LAUNCHWEBAUTHFLOW); 181 182 IdentityLaunchWebAuthFlowFunction(); 183 184 // Tests may override extension_id. 185 void InitFinalRedirectURLPrefixForTest(const std::string& extension_id); 186 187 private: 188 virtual ~IdentityLaunchWebAuthFlowFunction(); 189 virtual bool RunImpl() OVERRIDE; 190 191 // WebAuthFlow::Delegate implementation. 192 virtual void OnAuthFlowFailure(WebAuthFlow::Failure failure) OVERRIDE; 193 virtual void OnAuthFlowURLChange(const GURL& redirect_url) OVERRIDE; 194 virtual void OnAuthFlowTitleChange(const std::string& title) OVERRIDE {} 195 196 // Helper to initialize final URL prefix. 197 void InitFinalRedirectURLPrefix(const std::string& extension_id); 198 199 scoped_ptr<WebAuthFlow> auth_flow_; 200 GURL final_url_prefix_; 201}; 202 203class IdentityTokenCacheValue { 204 public: 205 IdentityTokenCacheValue(); 206 explicit IdentityTokenCacheValue(const IssueAdviceInfo& issue_advice); 207 IdentityTokenCacheValue(const std::string& token, 208 base::TimeDelta time_to_live); 209 ~IdentityTokenCacheValue(); 210 211 // Order of these entries is used to determine whether or not new 212 // entries supercede older ones in SetCachedToken. 213 enum CacheValueStatus { 214 CACHE_STATUS_NOTFOUND, 215 CACHE_STATUS_ADVICE, 216 CACHE_STATUS_TOKEN 217 }; 218 219 CacheValueStatus status() const; 220 const IssueAdviceInfo& issue_advice() const; 221 const std::string& token() const; 222 const base::Time& expiration_time() const; 223 224 private: 225 bool is_expired() const; 226 227 CacheValueStatus status_; 228 IssueAdviceInfo issue_advice_; 229 std::string token_; 230 base::Time expiration_time_; 231}; 232 233class IdentityAPI : public ProfileKeyedAPI, 234 public SigninGlobalError::AuthStatusProvider, 235 public content::NotificationObserver { 236 public: 237 struct TokenCacheKey { 238 TokenCacheKey(const std::string& extension_id, 239 const std::set<std::string> scopes); 240 ~TokenCacheKey(); 241 bool operator<(const TokenCacheKey& rhs) const; 242 std::string extension_id; 243 std::set<std::string> scopes; 244 }; 245 246 typedef std::map<TokenCacheKey, IdentityTokenCacheValue> CachedTokens; 247 248 explicit IdentityAPI(Profile* profile); 249 virtual ~IdentityAPI(); 250 void Initialize(); 251 252 // Request serialization queue for getAuthToken. 253 IdentityMintRequestQueue* mint_queue(); 254 255 // Token cache 256 void SetCachedToken(const std::string& extension_id, 257 const std::vector<std::string> scopes, 258 const IdentityTokenCacheValue& token_data); 259 void EraseCachedToken(const std::string& extension_id, 260 const std::string& token); 261 void EraseAllCachedTokens(); 262 const IdentityTokenCacheValue& GetCachedToken( 263 const std::string& extension_id, const std::vector<std::string> scopes); 264 265 const CachedTokens& GetAllCachedTokens(); 266 267 void ReportAuthError(const GoogleServiceAuthError& error); 268 269 // ProfileKeyedAPI implementation. 270 virtual void Shutdown() OVERRIDE; 271 static ProfileKeyedAPIFactory<IdentityAPI>* GetFactoryInstance(); 272 273 // AuthStatusProvider implementation. 274 virtual GoogleServiceAuthError GetAuthStatus() const OVERRIDE; 275 276 // content::NotificationObserver implementation. 277 virtual void Observe(int type, 278 const content::NotificationSource& source, 279 const content::NotificationDetails& details) OVERRIDE; 280 281 private: 282 friend class ProfileKeyedAPIFactory<IdentityAPI>; 283 284 // ProfileKeyedAPI implementation. 285 static const char* service_name() { 286 return "IdentityAPI"; 287 } 288 static const bool kServiceIsNULLWhileTesting = true; 289 290 Profile* profile_; 291 SigninManagerBase* signin_manager_; 292 GoogleServiceAuthError error_; 293 // Used to listen to notifications from the TokenService. 294 content::NotificationRegistrar registrar_; 295 IdentityMintRequestQueue mint_queue_; 296 CachedTokens token_cache_; 297}; 298 299template <> 300void ProfileKeyedAPIFactory<IdentityAPI>::DeclareFactoryDependencies(); 301 302} // namespace extensions 303 304#endif // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_ 305