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