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