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