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