identity_api.h revision 116680a4aac90f2aa7413d9095a592090648e557
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
29class GoogleServiceAuthError;
30class MockGetAuthTokenFunction;
31
32namespace content {
33class BrowserContext;
34}
35
36namespace extensions {
37
38class GetAuthTokenFunctionTest;
39class MockGetAuthTokenFunction;
40
41namespace identity_constants {
42extern const char kInvalidClientId[];
43extern const char kInvalidScopes[];
44extern const char kAuthFailure[];
45extern const char kNoGrant[];
46extern const char kUserRejected[];
47extern const char kUserNotSignedIn[];
48extern const char kInteractionRequired[];
49extern const char kInvalidRedirect[];
50extern const char kOffTheRecord[];
51extern const char kPageLoadFailure[];
52extern const char kCanceled[];
53}  // namespace identity_constants
54
55class 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
85class 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
145template <>
146void BrowserContextKeyedAPIFactory<IdentityAPI>::DeclareFactoryDependencies();
147
148class 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.
179class 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 protected:
193  virtual ~IdentityGetAuthTokenFunction();
194
195  // IdentitySigninFlow::Delegate implementation:
196  virtual void SigninSuccess() OVERRIDE;
197  virtual void SigninFailed() OVERRIDE;
198
199  // GaiaWebAuthFlow::Delegate implementation:
200  virtual void OnGaiaFlowFailure(GaiaWebAuthFlow::Failure failure,
201                                 GoogleServiceAuthError service_error,
202                                 const std::string& oauth_error) OVERRIDE;
203  virtual void OnGaiaFlowCompleted(const std::string& access_token,
204                                   const std::string& expiration) OVERRIDE;
205
206  // OAuth2TokenService::Consumer implementation:
207  virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
208                                 const std::string& access_token,
209                                 const base::Time& expiration_time) OVERRIDE;
210  virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
211                                 const GoogleServiceAuthError& error) OVERRIDE;
212
213  scoped_ptr<OAuth2TokenService::Request> login_token_request_;
214
215 private:
216  FRIEND_TEST_ALL_PREFIXES(GetAuthTokenFunctionTest,
217                           ComponentWithChromeClientId);
218  FRIEND_TEST_ALL_PREFIXES(GetAuthTokenFunctionTest,
219                           ComponentWithNormalClientId);
220  FRIEND_TEST_ALL_PREFIXES(GetAuthTokenFunctionTest, InteractiveQueueShutdown);
221  FRIEND_TEST_ALL_PREFIXES(GetAuthTokenFunctionTest, NoninteractiveShutdown);
222  friend class MockGetAuthTokenFunction;
223
224  // ExtensionFunction:
225  virtual bool RunAsync() OVERRIDE;
226
227  // Helpers to report async function results to the caller.
228  void StartAsyncRun();
229  void CompleteAsyncRun(bool success);
230  void CompleteFunctionWithResult(const std::string& access_token);
231  void CompleteFunctionWithError(const std::string& error);
232
233  // Initiate/complete the sub-flows.
234  void StartSigninFlow();
235  void StartMintTokenFlow(IdentityMintRequestQueue::MintType type);
236  void CompleteMintTokenFlow();
237
238  // IdentityMintRequestQueue::Request implementation:
239  virtual void StartMintToken(IdentityMintRequestQueue::MintType type) OVERRIDE;
240
241  // OAuth2MintTokenFlow::Delegate implementation:
242  virtual void OnMintTokenSuccess(const std::string& access_token,
243                                  int time_to_live) OVERRIDE;
244  virtual void OnMintTokenFailure(
245      const GoogleServiceAuthError& error) OVERRIDE;
246  virtual void OnIssueAdviceSuccess(
247      const IssueAdviceInfo& issue_advice) OVERRIDE;
248
249  // IdentityAPI::ShutdownObserver implementation:
250  virtual void OnShutdown() OVERRIDE;
251
252  // Starts a login access token request.
253  virtual void StartLoginAccessTokenRequest();
254
255#if defined(OS_CHROMEOS)
256  // Starts a login access token request for device robot account. This method
257  // will be called only in enterprise kiosk mode in ChromeOS.
258  virtual void StartDeviceLoginAccessTokenRequest();
259#endif
260
261  // Starts a mint token request to GAIA.
262  void StartGaiaRequest(const std::string& login_access_token);
263
264  // Methods for invoking UI. Overridable for testing.
265  virtual void ShowLoginPopup();
266  virtual void ShowOAuthApprovalDialog(const IssueAdviceInfo& issue_advice);
267  // Caller owns the returned instance.
268  virtual OAuth2MintTokenFlow* CreateMintTokenFlow(
269      const std::string& login_access_token);
270
271  // Checks if there is a master login token to mint tokens for the extension.
272  virtual bool HasLoginToken() const;
273
274  // Maps OAuth2 protocol errors to an error message returned to the
275  // developer in chrome.runtime.lastError.
276  std::string MapOAuth2ErrorToDescription(const std::string& error);
277
278  std::string GetOAuth2ClientId() const;
279
280  bool should_prompt_for_scopes_;
281  IdentityMintRequestQueue::MintType mint_token_flow_type_;
282  scoped_ptr<OAuth2MintTokenFlow> mint_token_flow_;
283  OAuth2MintTokenFlow::Mode gaia_mint_token_mode_;
284  bool should_prompt_for_signin_;
285
286  scoped_ptr<ExtensionTokenKey> token_key_;
287  std::string oauth2_client_id_;
288  // When launched in interactive mode, and if there is no existing grant,
289  // a permissions prompt will be popped up to the user.
290  IssueAdviceInfo issue_advice_;
291  scoped_ptr<GaiaWebAuthFlow> gaia_web_auth_flow_;
292  scoped_ptr<IdentitySigninFlow> signin_flow_;
293};
294
295class IdentityGetProfileUserInfoFunction
296    : public ChromeUIThreadExtensionFunction {
297 public:
298  DECLARE_EXTENSION_FUNCTION("identity.getProfileUserInfo",
299                             IDENTITY_GETPROFILEUSERINFO);
300
301  IdentityGetProfileUserInfoFunction();
302
303 private:
304  virtual ~IdentityGetProfileUserInfoFunction();
305
306  // UIThreadExtensionFunction implementation.
307  virtual ExtensionFunction::ResponseAction Run() OVERRIDE;
308};
309
310class IdentityRemoveCachedAuthTokenFunction
311    : public ChromeSyncExtensionFunction {
312 public:
313  DECLARE_EXTENSION_FUNCTION("identity.removeCachedAuthToken",
314                             EXPERIMENTAL_IDENTITY_REMOVECACHEDAUTHTOKEN)
315  IdentityRemoveCachedAuthTokenFunction();
316
317 protected:
318  virtual ~IdentityRemoveCachedAuthTokenFunction();
319
320  // SyncExtensionFunction implementation:
321  virtual bool RunSync() OVERRIDE;
322};
323
324class IdentityLaunchWebAuthFlowFunction : public ChromeAsyncExtensionFunction,
325                                          public WebAuthFlow::Delegate {
326 public:
327  DECLARE_EXTENSION_FUNCTION("identity.launchWebAuthFlow",
328                             EXPERIMENTAL_IDENTITY_LAUNCHWEBAUTHFLOW);
329
330  IdentityLaunchWebAuthFlowFunction();
331
332  // Tests may override extension_id.
333  void InitFinalRedirectURLPrefixForTest(const std::string& extension_id);
334
335 private:
336  virtual ~IdentityLaunchWebAuthFlowFunction();
337  virtual bool RunAsync() OVERRIDE;
338
339  // WebAuthFlow::Delegate implementation.
340  virtual void OnAuthFlowFailure(WebAuthFlow::Failure failure) OVERRIDE;
341  virtual void OnAuthFlowURLChange(const GURL& redirect_url) OVERRIDE;
342  virtual void OnAuthFlowTitleChange(const std::string& title) OVERRIDE {}
343
344  // Helper to initialize final URL prefix.
345  void InitFinalRedirectURLPrefix(const std::string& extension_id);
346
347  scoped_ptr<WebAuthFlow> auth_flow_;
348  GURL final_url_prefix_;
349};
350
351}  // namespace extensions
352
353#endif  // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_
354