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