identity_api.h revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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
28class GoogleServiceAuthError;
29class MockGetAuthTokenFunction;
30class Profile;
31
32#if defined(OS_CHROMEOS)
33namespace chromeos {
34class DeviceOAuth2TokenService;
35}
36#endif
37
38namespace extensions {
39
40class GetAuthTokenFunctionTest;
41class MockGetAuthTokenFunction;
42
43namespace identity_constants {
44extern const char kInvalidClientId[];
45extern const char kInvalidScopes[];
46extern const char kAuthFailure[];
47extern const char kNoGrant[];
48extern const char kUserRejected[];
49extern const char kUserNotSignedIn[];
50extern const char kInteractionRequired[];
51extern const char kInvalidRedirect[];
52extern const char kOffTheRecord[];
53extern 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.
73class 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
183class 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
197class 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
224class 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
254class 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
302template <>
303void ProfileKeyedAPIFactory<IdentityAPI>::DeclareFactoryDependencies();
304
305}  // namespace extensions
306
307#endif  // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_
308