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