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.
5 #ifndef CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_
6 #define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/observer_list.h"
17 #include "chrome/browser/extensions/api/identity/extension_token_key.h"
18 #include "chrome/browser/extensions/api/identity/gaia_web_auth_flow.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/chrome_extension_function.h"
23 #include "components/signin/core/browser/profile_identity_provider.h"
24 #include "extensions/browser/browser_context_keyed_api_factory.h"
25 #include "google_apis/gaia/account_tracker.h"
26 #include "google_apis/gaia/oauth2_mint_token_flow.h"
27 #include "google_apis/gaia/oauth2_token_service.h"
29 class GoogleServiceAuthError
;
30 class MockGetAuthTokenFunction
;
36 namespace extensions
{
38 class GetAuthTokenFunctionTest
;
39 class MockGetAuthTokenFunction
;
41 namespace identity_constants
{
42 extern const char kInvalidClientId
[];
43 extern const char kInvalidScopes
[];
44 extern const char kAuthFailure
[];
45 extern const char kNoGrant
[];
46 extern const char kUserRejected
[];
47 extern const char kUserNotSignedIn
[];
48 extern const char kInteractionRequired
[];
49 extern const char kInvalidRedirect
[];
50 extern const char kOffTheRecord
[];
51 extern const char kPageLoadFailure
[];
52 extern const char kCanceled
[];
53 } // namespace identity_constants
55 class IdentityTokenCacheValue
{
57 IdentityTokenCacheValue();
58 explicit IdentityTokenCacheValue(const IssueAdviceInfo
& issue_advice
);
59 IdentityTokenCacheValue(const std::string
& token
,
60 base::TimeDelta time_to_live
);
61 ~IdentityTokenCacheValue();
63 // Order of these entries is used to determine whether or not new
64 // entries supercede older ones in SetCachedToken.
65 enum CacheValueStatus
{
66 CACHE_STATUS_NOTFOUND
,
71 CacheValueStatus
status() const;
72 const IssueAdviceInfo
& issue_advice() const;
73 const std::string
& token() const;
74 const base::Time
& expiration_time() const;
77 bool is_expired() const;
79 CacheValueStatus status_
;
80 IssueAdviceInfo issue_advice_
;
82 base::Time expiration_time_
;
85 class IdentityAPI
: public BrowserContextKeyedAPI
,
86 public gaia::AccountTracker::Observer
{
88 typedef std::map
<ExtensionTokenKey
, IdentityTokenCacheValue
> CachedTokens
;
90 class ShutdownObserver
{
92 virtual void OnShutdown() = 0;
95 explicit IdentityAPI(content::BrowserContext
* context
);
96 ~IdentityAPI() override
;
98 // Request serialization queue for getAuthToken.
99 IdentityMintRequestQueue
* mint_queue();
102 void SetCachedToken(const ExtensionTokenKey
& key
,
103 const IdentityTokenCacheValue
& token_data
);
104 void EraseCachedToken(const std::string
& extension_id
,
105 const std::string
& token
);
106 void EraseAllCachedTokens();
107 const IdentityTokenCacheValue
& GetCachedToken(const ExtensionTokenKey
& key
);
109 const CachedTokens
& GetAllCachedTokens();
112 std::vector
<std::string
> GetAccounts() const;
113 std::string
FindAccountKeyByGaiaId(const std::string
& gaia_id
);
115 // BrowserContextKeyedAPI implementation.
116 void Shutdown() override
;
117 static BrowserContextKeyedAPIFactory
<IdentityAPI
>* GetFactoryInstance();
119 // gaia::AccountTracker::Observer implementation:
120 void OnAccountAdded(const gaia::AccountIds
& ids
) override
;
121 void OnAccountRemoved(const gaia::AccountIds
& ids
) override
;
122 void OnAccountSignInChanged(const gaia::AccountIds
& ids
,
123 bool is_signed_in
) override
;
125 void AddShutdownObserver(ShutdownObserver
* observer
);
126 void RemoveShutdownObserver(ShutdownObserver
* observer
);
128 void SetAccountStateForTest(gaia::AccountIds ids
, bool is_signed_in
);
131 friend class BrowserContextKeyedAPIFactory
<IdentityAPI
>;
133 // BrowserContextKeyedAPI implementation.
134 static const char* service_name() { return "IdentityAPI"; }
135 static const bool kServiceIsNULLWhileTesting
= true;
137 content::BrowserContext
* browser_context_
;
138 IdentityMintRequestQueue mint_queue_
;
139 CachedTokens token_cache_
;
140 ProfileIdentityProvider profile_identity_provider_
;
141 gaia::AccountTracker account_tracker_
;
142 base::ObserverList
<ShutdownObserver
> shutdown_observer_list_
;
146 void BrowserContextKeyedAPIFactory
<IdentityAPI
>::DeclareFactoryDependencies();
148 class IdentityGetAccountsFunction
: public ChromeUIThreadExtensionFunction
{
150 DECLARE_EXTENSION_FUNCTION("identity.getAccounts",
151 IDENTITY_GETACCOUNTS
);
153 IdentityGetAccountsFunction();
156 ~IdentityGetAccountsFunction() override
;
158 // UIThreadExtensionFunction implementation.
159 ExtensionFunction::ResponseAction
Run() override
;
162 // identity.getAuthToken fetches an OAuth 2 function for the
163 // caller. The request has three sub-flows: non-interactive,
164 // interactive, and sign-in.
166 // In the non-interactive flow, getAuthToken requests a token from
167 // GAIA. GAIA may respond with a token, an error, or "consent
168 // required". In the consent required cases, getAuthToken proceeds to
169 // the second, interactive phase.
171 // The interactive flow presents a scope approval dialog to the
172 // user. If the user approves the request, a grant will be recorded on
173 // the server, and an access token will be returned to the caller.
175 // In some cases we need to display a sign-in dialog. Normally the
176 // profile will be signed in already, but if it turns out we need a
177 // new login token, there is a sign-in flow. If that flow completes
178 // successfully, getAuthToken proceeds to the non-interactive flow.
179 class IdentityGetAuthTokenFunction
: public ChromeAsyncExtensionFunction
,
180 public GaiaWebAuthFlow::Delegate
,
181 public IdentityMintRequestQueue::Request
,
182 public OAuth2MintTokenFlow::Delegate
,
183 public IdentitySigninFlow::Delegate
,
184 public OAuth2TokenService::Consumer
,
185 public IdentityAPI::ShutdownObserver
{
187 DECLARE_EXTENSION_FUNCTION("identity.getAuthToken",
188 EXPERIMENTAL_IDENTITY_GETAUTHTOKEN
);
190 IdentityGetAuthTokenFunction();
192 const ExtensionTokenKey
* GetExtensionTokenKeyForTest() {
193 return token_key_
.get();
197 ~IdentityGetAuthTokenFunction() override
;
199 // IdentitySigninFlow::Delegate implementation:
200 void SigninSuccess() override
;
201 void SigninFailed() override
;
203 // GaiaWebAuthFlow::Delegate implementation:
204 void OnGaiaFlowFailure(GaiaWebAuthFlow::Failure failure
,
205 GoogleServiceAuthError service_error
,
206 const std::string
& oauth_error
) override
;
207 void OnGaiaFlowCompleted(const std::string
& access_token
,
208 const std::string
& expiration
) override
;
210 // Starts a login access token request.
211 virtual void StartLoginAccessTokenRequest();
213 // OAuth2TokenService::Consumer implementation:
214 void OnGetTokenSuccess(const OAuth2TokenService::Request
* request
,
215 const std::string
& access_token
,
216 const base::Time
& expiration_time
) override
;
217 void OnGetTokenFailure(const OAuth2TokenService::Request
* request
,
218 const GoogleServiceAuthError
& error
) override
;
220 // Starts a mint token request to GAIA.
221 // Exposed for testing.
222 virtual void StartGaiaRequest(const std::string
& login_access_token
);
224 // Caller owns the returned instance.
225 // Exposed for testing.
226 virtual OAuth2MintTokenFlow
* CreateMintTokenFlow();
228 scoped_ptr
<OAuth2TokenService::Request
> login_token_request_
;
231 FRIEND_TEST_ALL_PREFIXES(GetAuthTokenFunctionTest
,
232 ComponentWithChromeClientId
);
233 FRIEND_TEST_ALL_PREFIXES(GetAuthTokenFunctionTest
,
234 ComponentWithNormalClientId
);
235 FRIEND_TEST_ALL_PREFIXES(GetAuthTokenFunctionTest
, InteractiveQueueShutdown
);
236 FRIEND_TEST_ALL_PREFIXES(GetAuthTokenFunctionTest
, NoninteractiveShutdown
);
238 // ExtensionFunction:
239 bool RunAsync() override
;
241 // Helpers to report async function results to the caller.
242 void StartAsyncRun();
243 void CompleteAsyncRun(bool success
);
244 void CompleteFunctionWithResult(const std::string
& access_token
);
245 void CompleteFunctionWithError(const std::string
& error
);
247 // Initiate/complete the sub-flows.
248 void StartSigninFlow();
249 void StartMintTokenFlow(IdentityMintRequestQueue::MintType type
);
250 void CompleteMintTokenFlow();
252 // IdentityMintRequestQueue::Request implementation:
253 void StartMintToken(IdentityMintRequestQueue::MintType type
) override
;
255 // OAuth2MintTokenFlow::Delegate implementation:
256 void OnMintTokenSuccess(const std::string
& access_token
,
257 int time_to_live
) override
;
258 void OnMintTokenFailure(const GoogleServiceAuthError
& error
) override
;
259 void OnIssueAdviceSuccess(const IssueAdviceInfo
& issue_advice
) override
;
261 // IdentityAPI::ShutdownObserver implementation:
262 void OnShutdown() override
;
264 #if defined(OS_CHROMEOS)
265 // Starts a login access token request for device robot account. This method
266 // will be called only in enterprise kiosk mode in ChromeOS.
267 virtual void StartDeviceLoginAccessTokenRequest();
270 // Methods for invoking UI. Overridable for testing.
271 virtual void ShowLoginPopup();
272 virtual void ShowOAuthApprovalDialog(const IssueAdviceInfo
& issue_advice
);
274 // Checks if there is a master login token to mint tokens for the extension.
275 bool HasLoginToken() const;
277 // Maps OAuth2 protocol errors to an error message returned to the
278 // developer in chrome.runtime.lastError.
279 std::string
MapOAuth2ErrorToDescription(const std::string
& error
);
281 std::string
GetOAuth2ClientId() const;
284 bool should_prompt_for_scopes_
;
285 IdentityMintRequestQueue::MintType mint_token_flow_type_
;
286 scoped_ptr
<OAuth2MintTokenFlow
> mint_token_flow_
;
287 OAuth2MintTokenFlow::Mode gaia_mint_token_mode_
;
288 bool should_prompt_for_signin_
;
290 scoped_ptr
<ExtensionTokenKey
> token_key_
;
291 std::string oauth2_client_id_
;
292 // When launched in interactive mode, and if there is no existing grant,
293 // a permissions prompt will be popped up to the user.
294 IssueAdviceInfo issue_advice_
;
295 scoped_ptr
<GaiaWebAuthFlow
> gaia_web_auth_flow_
;
296 scoped_ptr
<IdentitySigninFlow
> signin_flow_
;
299 class IdentityGetProfileUserInfoFunction
300 : public ChromeUIThreadExtensionFunction
{
302 DECLARE_EXTENSION_FUNCTION("identity.getProfileUserInfo",
303 IDENTITY_GETPROFILEUSERINFO
);
305 IdentityGetProfileUserInfoFunction();
308 ~IdentityGetProfileUserInfoFunction() override
;
310 // UIThreadExtensionFunction implementation.
311 ExtensionFunction::ResponseAction
Run() override
;
314 class IdentityRemoveCachedAuthTokenFunction
315 : public ChromeSyncExtensionFunction
{
317 DECLARE_EXTENSION_FUNCTION("identity.removeCachedAuthToken",
318 EXPERIMENTAL_IDENTITY_REMOVECACHEDAUTHTOKEN
)
319 IdentityRemoveCachedAuthTokenFunction();
322 ~IdentityRemoveCachedAuthTokenFunction() override
;
324 // SyncExtensionFunction implementation:
325 bool RunSync() override
;
328 class IdentityLaunchWebAuthFlowFunction
: public ChromeAsyncExtensionFunction
,
329 public WebAuthFlow::Delegate
{
331 DECLARE_EXTENSION_FUNCTION("identity.launchWebAuthFlow",
332 EXPERIMENTAL_IDENTITY_LAUNCHWEBAUTHFLOW
);
334 IdentityLaunchWebAuthFlowFunction();
336 // Tests may override extension_id.
337 void InitFinalRedirectURLPrefixForTest(const std::string
& extension_id
);
340 ~IdentityLaunchWebAuthFlowFunction() override
;
341 bool RunAsync() override
;
343 // WebAuthFlow::Delegate implementation.
344 void OnAuthFlowFailure(WebAuthFlow::Failure failure
) override
;
345 void OnAuthFlowURLChange(const GURL
& redirect_url
) override
;
346 void OnAuthFlowTitleChange(const std::string
& title
) override
{}
348 // Helper to initialize final URL prefix.
349 void InitFinalRedirectURLPrefix(const std::string
& extension_id
);
351 scoped_ptr
<WebAuthFlow
> auth_flow_
;
352 GURL final_url_prefix_
;
355 } // namespace extensions
357 #endif // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_