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