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