identity_api.h revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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  // URL checking helpers. Public for testing.
157  // Checks to see if the current URL ends the flow.
158  bool IsFinalRedirectURL(const GURL& url) const;
159
160  // Unit tests may override extension_id.
161  void InitFinalRedirectURLPrefixesForTest(const std::string& extension_id);
162
163 private:
164  virtual ~IdentityLaunchWebAuthFlowFunction();
165  virtual bool RunImpl() OVERRIDE;
166
167  // WebAuthFlow::Delegate implementation.
168  virtual void OnAuthFlowFailure(WebAuthFlow::Failure failure) OVERRIDE;
169  virtual void OnAuthFlowURLChange(const GURL& redirect_url) OVERRIDE;
170
171  // Helper to initialize final URLs vector.
172  void InitFinalRedirectURLPrefixes(const std::string& extension_id);
173
174  scoped_ptr<WebAuthFlow> auth_flow_;
175  std::vector<GURL> final_prefixes_;
176};
177
178class IdentityTokenCacheValue {
179 public:
180  IdentityTokenCacheValue();
181  explicit IdentityTokenCacheValue(const IssueAdviceInfo& issue_advice);
182  IdentityTokenCacheValue(const std::string& token,
183                          base::TimeDelta time_to_live);
184  ~IdentityTokenCacheValue();
185
186  // Order of these entries is used to determine whether or not new
187  // entries supercede older ones in SetCachedToken.
188  enum CacheValueStatus {
189    CACHE_STATUS_NOTFOUND,
190    CACHE_STATUS_ADVICE,
191    CACHE_STATUS_TOKEN
192  };
193
194  CacheValueStatus status() const;
195  const IssueAdviceInfo& issue_advice() const;
196  const std::string& token() const;
197
198 private:
199  bool is_expired() const;
200
201  CacheValueStatus status_;
202  IssueAdviceInfo issue_advice_;
203  std::string token_;
204  base::Time expiration_time_;
205};
206
207class IdentityAPI : public ProfileKeyedAPI,
208                    public SigninGlobalError::AuthStatusProvider,
209                    public content::NotificationObserver {
210 public:
211  explicit IdentityAPI(Profile* profile);
212  virtual ~IdentityAPI();
213  void Initialize();
214
215  // Request serialization queue for getAuthToken.
216  IdentityMintRequestQueue* mint_queue();
217
218  // Token cache
219  void SetCachedToken(const std::string& extension_id,
220                      const std::vector<std::string> scopes,
221                      const IdentityTokenCacheValue& token_data);
222  void EraseCachedToken(const std::string& extension_id,
223                        const std::string& token);
224  void EraseAllCachedTokens();
225  const IdentityTokenCacheValue& GetCachedToken(
226      const std::string& extension_id, const std::vector<std::string> scopes);
227
228  void ReportAuthError(const GoogleServiceAuthError& error);
229
230  // ProfileKeyedAPI implementation.
231  virtual void Shutdown() OVERRIDE;
232  static ProfileKeyedAPIFactory<IdentityAPI>* GetFactoryInstance();
233
234  // AuthStatusProvider implementation.
235  virtual GoogleServiceAuthError GetAuthStatus() const OVERRIDE;
236
237  // content::NotificationObserver implementation.
238  virtual void Observe(int type,
239                       const content::NotificationSource& source,
240                       const content::NotificationDetails& details) OVERRIDE;
241
242 private:
243  friend class ProfileKeyedAPIFactory<IdentityAPI>;
244
245  struct TokenCacheKey {
246    TokenCacheKey(const std::string& extension_id,
247                  const std::set<std::string> scopes);
248    ~TokenCacheKey();
249    bool operator<(const TokenCacheKey& rhs) const;
250    std::string extension_id;
251    std::set<std::string> scopes;
252  };
253
254  // ProfileKeyedAPI implementation.
255  static const char* service_name() {
256    return "IdentityAPI";
257  }
258  static const bool kServiceIsNULLWhileTesting = true;
259
260  Profile* profile_;
261  SigninManagerBase* signin_manager_;
262  GoogleServiceAuthError error_;
263  // Used to listen to notifications from the TokenService.
264  content::NotificationRegistrar registrar_;
265  IdentityMintRequestQueue mint_queue_;
266  std::map<TokenCacheKey, IdentityTokenCacheValue> token_cache_;
267};
268
269template <>
270void ProfileKeyedAPIFactory<IdentityAPI>::DeclareFactoryDependencies();
271
272}  // namespace extensions
273
274#endif  // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_
275