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