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 "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