OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/api/identity/identity_api.h" | 5 #include "chrome/browser/extensions/api/identity/identity_api.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <memory> | 9 #include <memory> |
10 #include <set> | 10 #include <set> |
(...skipping 29 matching lines...) Expand all Loading... |
40 #include "extensions/browser/event_router.h" | 40 #include "extensions/browser/event_router.h" |
41 #include "extensions/browser/extension_function_dispatcher.h" | 41 #include "extensions/browser/extension_function_dispatcher.h" |
42 #include "extensions/common/extension.h" | 42 #include "extensions/common/extension.h" |
43 #include "extensions/common/extension_l10n_util.h" | 43 #include "extensions/common/extension_l10n_util.h" |
44 #include "extensions/common/manifest_handlers/oauth2_manifest_handler.h" | 44 #include "extensions/common/manifest_handlers/oauth2_manifest_handler.h" |
45 #include "extensions/common/permissions/permission_set.h" | 45 #include "extensions/common/permissions/permission_set.h" |
46 #include "extensions/common/permissions/permissions_data.h" | 46 #include "extensions/common/permissions/permissions_data.h" |
47 #include "google_apis/gaia/gaia_urls.h" | 47 #include "google_apis/gaia/gaia_urls.h" |
48 #include "url/gurl.h" | 48 #include "url/gurl.h" |
49 | 49 |
50 #if defined(OS_CHROMEOS) | |
51 #include "chrome/browser/chromeos/login/session/user_session_manager.h" | |
52 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" | |
53 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" | |
54 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h
" | |
55 #include "components/user_manager/user_manager.h" | |
56 #include "google_apis/gaia/gaia_constants.h" | |
57 #endif | |
58 | |
59 namespace extensions { | 50 namespace extensions { |
60 | 51 |
61 namespace { | |
62 | |
63 #if defined(OS_CHROMEOS) | |
64 // The list of apps that are allowed to use the Identity API to retrieve the | |
65 // token from the device robot account in a public session. | |
66 const char* const kPublicSessionAllowedOrigins[] = { | |
67 // Chrome Remote Desktop - Chromium branding. | |
68 "chrome-extension://ljacajndfccfgnfohlgkdphmbnpkjflk/", | |
69 // Chrome Remote Desktop - Official branding. | |
70 "chrome-extension://gbchcmhmhahfdphkhkmpfmihenigjmpp/"}; | |
71 #endif | |
72 | |
73 std::string GetPrimaryAccountId(content::BrowserContext* context) { | |
74 SigninManagerBase* signin_manager = | |
75 SigninManagerFactory::GetForProfile(Profile::FromBrowserContext(context)); | |
76 return signin_manager->GetAuthenticatedAccountId(); | |
77 } | |
78 | |
79 } // namespace | |
80 | |
81 namespace identity = api::identity; | 52 namespace identity = api::identity; |
82 | 53 |
83 IdentityTokenCacheValue::IdentityTokenCacheValue() | 54 IdentityTokenCacheValue::IdentityTokenCacheValue() |
84 : status_(CACHE_STATUS_NOTFOUND) {} | 55 : status_(CACHE_STATUS_NOTFOUND) {} |
85 | 56 |
86 IdentityTokenCacheValue::IdentityTokenCacheValue( | 57 IdentityTokenCacheValue::IdentityTokenCacheValue( |
87 const IssueAdviceInfo& issue_advice) | 58 const IssueAdviceInfo& issue_advice) |
88 : status_(CACHE_STATUS_ADVICE), issue_advice_(issue_advice) { | 59 : status_(CACHE_STATUS_ADVICE), issue_advice_(issue_advice) { |
89 expiration_time_ = | 60 expiration_time_ = |
90 base::Time::Now() + base::TimeDelta::FromSeconds( | 61 base::Time::Now() + base::TimeDelta::FromSeconds( |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
179 const IdentityTokenCacheValue& IdentityAPI::GetCachedToken( | 150 const IdentityTokenCacheValue& IdentityAPI::GetCachedToken( |
180 const ExtensionTokenKey& key) { | 151 const ExtensionTokenKey& key) { |
181 return token_cache_[key]; | 152 return token_cache_[key]; |
182 } | 153 } |
183 | 154 |
184 const IdentityAPI::CachedTokens& IdentityAPI::GetAllCachedTokens() { | 155 const IdentityAPI::CachedTokens& IdentityAPI::GetAllCachedTokens() { |
185 return token_cache_; | 156 return token_cache_; |
186 } | 157 } |
187 | 158 |
188 std::vector<std::string> IdentityAPI::GetAccounts() const { | 159 std::vector<std::string> IdentityAPI::GetAccounts() const { |
189 const std::string primary_account_id = GetPrimaryAccountId(browser_context_); | |
190 const std::vector<gaia::AccountIds> ids = account_tracker_.GetAccounts(); | 160 const std::vector<gaia::AccountIds> ids = account_tracker_.GetAccounts(); |
191 std::vector<std::string> gaia_ids; | 161 std::vector<std::string> gaia_ids; |
192 | 162 |
193 if (switches::IsExtensionsMultiAccount()) { | 163 if (switches::IsExtensionsMultiAccount()) { |
194 for (std::vector<gaia::AccountIds>::const_iterator it = ids.begin(); | 164 for (std::vector<gaia::AccountIds>::const_iterator it = ids.begin(); |
195 it != ids.end(); | 165 it != ids.end(); |
196 ++it) { | 166 ++it) { |
197 gaia_ids.push_back(it->gaia); | 167 gaia_ids.push_back(it->gaia); |
198 } | 168 } |
199 } else if (ids.size() >= 1) { | 169 } else if (ids.size() >= 1) { |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
275 it != gaia_ids.end(); | 245 it != gaia_ids.end(); |
276 ++it) { | 246 ++it) { |
277 api::identity::AccountInfo account_info; | 247 api::identity::AccountInfo account_info; |
278 account_info.id = *it; | 248 account_info.id = *it; |
279 infos->Append(account_info.ToValue()); | 249 infos->Append(account_info.ToValue()); |
280 } | 250 } |
281 | 251 |
282 return RespondNow(OneArgument(std::move(infos))); | 252 return RespondNow(OneArgument(std::move(infos))); |
283 } | 253 } |
284 | 254 |
285 IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction() | |
286 : OAuth2TokenService::Consumer("extensions_identity_api"), | |
287 interactive_(false), | |
288 should_prompt_for_scopes_(false), | |
289 should_prompt_for_signin_(false) { | |
290 } | |
291 | |
292 IdentityGetAuthTokenFunction::~IdentityGetAuthTokenFunction() { | |
293 TRACE_EVENT_ASYNC_END0("identity", "IdentityGetAuthTokenFunction", this); | |
294 } | |
295 | |
296 bool IdentityGetAuthTokenFunction::RunAsync() { | |
297 TRACE_EVENT_ASYNC_BEGIN1("identity", | |
298 "IdentityGetAuthTokenFunction", | |
299 this, | |
300 "extension", | |
301 extension()->id()); | |
302 | |
303 if (GetProfile()->IsOffTheRecord()) { | |
304 error_ = identity_constants::kOffTheRecord; | |
305 return false; | |
306 } | |
307 | |
308 std::unique_ptr<identity::GetAuthToken::Params> params( | |
309 identity::GetAuthToken::Params::Create(*args_)); | |
310 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
311 interactive_ = params->details.get() && | |
312 params->details->interactive.get() && | |
313 *params->details->interactive; | |
314 | |
315 should_prompt_for_scopes_ = interactive_; | |
316 should_prompt_for_signin_ = interactive_; | |
317 | |
318 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension()); | |
319 | |
320 // Check that the necessary information is present in the manifest. | |
321 oauth2_client_id_ = GetOAuth2ClientId(); | |
322 if (oauth2_client_id_.empty()) { | |
323 error_ = identity_constants::kInvalidClientId; | |
324 return false; | |
325 } | |
326 | |
327 std::set<std::string> scopes(oauth2_info.scopes.begin(), | |
328 oauth2_info.scopes.end()); | |
329 | |
330 std::string account_key = GetPrimaryAccountId(GetProfile()); | |
331 | |
332 if (params->details.get()) { | |
333 if (params->details->account.get()) { | |
334 std::string detail_key = | |
335 extensions::IdentityAPI::GetFactoryInstance() | |
336 ->Get(GetProfile()) | |
337 ->FindAccountKeyByGaiaId(params->details->account->id); | |
338 | |
339 if (detail_key != account_key) { | |
340 if (detail_key.empty() || !switches::IsExtensionsMultiAccount()) { | |
341 // TODO(courage): should this be a different error? | |
342 error_ = identity_constants::kUserNotSignedIn; | |
343 return false; | |
344 } | |
345 | |
346 account_key = detail_key; | |
347 } | |
348 } | |
349 | |
350 if (params->details->scopes.get()) { | |
351 scopes = std::set<std::string>(params->details->scopes->begin(), | |
352 params->details->scopes->end()); | |
353 } | |
354 } | |
355 | |
356 if (scopes.size() == 0) { | |
357 error_ = identity_constants::kInvalidScopes; | |
358 return false; | |
359 } | |
360 | |
361 token_key_.reset( | |
362 new ExtensionTokenKey(extension()->id(), account_key, scopes)); | |
363 | |
364 // From here on out, results must be returned asynchronously. | |
365 StartAsyncRun(); | |
366 | |
367 #if defined(OS_CHROMEOS) | |
368 policy::BrowserPolicyConnectorChromeOS* connector = | |
369 g_browser_process->platform_part()->browser_policy_connector_chromeos(); | |
370 bool is_kiosk = user_manager::UserManager::Get()->IsLoggedInAsKioskApp(); | |
371 bool is_public_session = | |
372 user_manager::UserManager::Get()->IsLoggedInAsPublicAccount(); | |
373 | |
374 if (connector->IsEnterpriseManaged() && (is_kiosk || is_public_session)) { | |
375 if (is_public_session && !IsOriginWhitelistedInPublicSession()) { | |
376 CompleteFunctionWithError(identity_constants::kUserNotSignedIn); | |
377 return true; | |
378 } | |
379 | |
380 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE); | |
381 return true; | |
382 } | |
383 #endif | |
384 | |
385 if (!HasLoginToken()) { | |
386 if (!should_prompt_for_signin_) { | |
387 CompleteFunctionWithError(identity_constants::kUserNotSignedIn); | |
388 return true; | |
389 } | |
390 // Display a login prompt. | |
391 StartSigninFlow(); | |
392 } else { | |
393 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE); | |
394 } | |
395 | |
396 return true; | |
397 } | |
398 | |
399 void IdentityGetAuthTokenFunction::StartAsyncRun() { | |
400 // Balanced in CompleteAsyncRun | |
401 AddRef(); | |
402 extensions::IdentityAPI::GetFactoryInstance() | |
403 ->Get(GetProfile()) | |
404 ->set_get_auth_token_function(this); | |
405 } | |
406 | |
407 void IdentityGetAuthTokenFunction::CompleteAsyncRun(bool success) { | |
408 extensions::IdentityAPI::GetFactoryInstance() | |
409 ->Get(GetProfile()) | |
410 ->set_get_auth_token_function(nullptr); | |
411 | |
412 SendResponse(success); | |
413 Release(); // Balanced in StartAsyncRun | |
414 } | |
415 | |
416 void IdentityGetAuthTokenFunction::CompleteFunctionWithResult( | |
417 const std::string& access_token) { | |
418 SetResult(base::MakeUnique<base::StringValue>(access_token)); | |
419 CompleteAsyncRun(true); | |
420 } | |
421 | |
422 void IdentityGetAuthTokenFunction::CompleteFunctionWithError( | |
423 const std::string& error) { | |
424 TRACE_EVENT_ASYNC_STEP_PAST1("identity", | |
425 "IdentityGetAuthTokenFunction", | |
426 this, | |
427 "CompleteFunctionWithError", | |
428 "error", | |
429 error); | |
430 error_ = error; | |
431 CompleteAsyncRun(false); | |
432 } | |
433 | |
434 void IdentityGetAuthTokenFunction::StartSigninFlow() { | |
435 // All cached tokens are invalid because the user is not signed in. | |
436 IdentityAPI* id_api = | |
437 extensions::IdentityAPI::GetFactoryInstance()->Get(GetProfile()); | |
438 id_api->EraseAllCachedTokens(); | |
439 // Display a login prompt. If the subsequent mint fails, don't display the | |
440 // login prompt again. | |
441 should_prompt_for_signin_ = false; | |
442 ShowLoginPopup(); | |
443 } | |
444 | |
445 void IdentityGetAuthTokenFunction::StartMintTokenFlow( | |
446 IdentityMintRequestQueue::MintType type) { | |
447 mint_token_flow_type_ = type; | |
448 | |
449 // Flows are serialized to prevent excessive traffic to GAIA, and | |
450 // to consolidate UI pop-ups. | |
451 IdentityAPI* id_api = | |
452 extensions::IdentityAPI::GetFactoryInstance()->Get(GetProfile()); | |
453 | |
454 if (!should_prompt_for_scopes_) { | |
455 // Caller requested no interaction. | |
456 | |
457 if (type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE) { | |
458 // GAIA told us to do a consent UI. | |
459 CompleteFunctionWithError(identity_constants::kNoGrant); | |
460 return; | |
461 } | |
462 if (!id_api->mint_queue()->empty( | |
463 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE, *token_key_)) { | |
464 // Another call is going through a consent UI. | |
465 CompleteFunctionWithError(identity_constants::kNoGrant); | |
466 return; | |
467 } | |
468 } | |
469 id_api->mint_queue()->RequestStart(type, *token_key_, this); | |
470 } | |
471 | |
472 void IdentityGetAuthTokenFunction::CompleteMintTokenFlow() { | |
473 IdentityMintRequestQueue::MintType type = mint_token_flow_type_; | |
474 | |
475 extensions::IdentityAPI::GetFactoryInstance() | |
476 ->Get(GetProfile()) | |
477 ->mint_queue() | |
478 ->RequestComplete(type, *token_key_, this); | |
479 } | |
480 | |
481 void IdentityGetAuthTokenFunction::StartMintToken( | |
482 IdentityMintRequestQueue::MintType type) { | |
483 TRACE_EVENT_ASYNC_STEP_PAST1("identity", | |
484 "IdentityGetAuthTokenFunction", | |
485 this, | |
486 "StartMintToken", | |
487 "type", | |
488 type); | |
489 | |
490 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension()); | |
491 IdentityAPI* id_api = IdentityAPI::GetFactoryInstance()->Get(GetProfile()); | |
492 IdentityTokenCacheValue cache_entry = id_api->GetCachedToken(*token_key_); | |
493 IdentityTokenCacheValue::CacheValueStatus cache_status = | |
494 cache_entry.status(); | |
495 | |
496 if (type == IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE) { | |
497 switch (cache_status) { | |
498 case IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND: | |
499 #if defined(OS_CHROMEOS) | |
500 // Always force minting token for ChromeOS kiosk app and public session. | |
501 if (user_manager::UserManager::Get()->IsLoggedInAsPublicAccount() && | |
502 !IsOriginWhitelistedInPublicSession()) { | |
503 CompleteFunctionWithError(identity_constants::kUserNotSignedIn); | |
504 return; | |
505 } | |
506 | |
507 if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp() || | |
508 user_manager::UserManager::Get()->IsLoggedInAsPublicAccount()) { | |
509 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE; | |
510 policy::BrowserPolicyConnectorChromeOS* connector = | |
511 g_browser_process->platform_part() | |
512 ->browser_policy_connector_chromeos(); | |
513 if (connector->IsEnterpriseManaged()) { | |
514 StartDeviceLoginAccessTokenRequest(); | |
515 } else { | |
516 StartLoginAccessTokenRequest(); | |
517 } | |
518 return; | |
519 } | |
520 #endif | |
521 | |
522 if (oauth2_info.auto_approve) | |
523 // oauth2_info.auto_approve is protected by a whitelist in | |
524 // _manifest_features.json hence only selected extensions take | |
525 // advantage of forcefully minting the token. | |
526 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE; | |
527 else | |
528 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE; | |
529 StartLoginAccessTokenRequest(); | |
530 break; | |
531 | |
532 case IdentityTokenCacheValue::CACHE_STATUS_TOKEN: | |
533 CompleteMintTokenFlow(); | |
534 CompleteFunctionWithResult(cache_entry.token()); | |
535 break; | |
536 | |
537 case IdentityTokenCacheValue::CACHE_STATUS_ADVICE: | |
538 CompleteMintTokenFlow(); | |
539 should_prompt_for_signin_ = false; | |
540 issue_advice_ = cache_entry.issue_advice(); | |
541 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE); | |
542 break; | |
543 } | |
544 } else { | |
545 DCHECK(type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE); | |
546 | |
547 if (cache_status == IdentityTokenCacheValue::CACHE_STATUS_TOKEN) { | |
548 CompleteMintTokenFlow(); | |
549 CompleteFunctionWithResult(cache_entry.token()); | |
550 } else { | |
551 ShowOAuthApprovalDialog(issue_advice_); | |
552 } | |
553 } | |
554 } | |
555 | |
556 void IdentityGetAuthTokenFunction::OnMintTokenSuccess( | |
557 const std::string& access_token, int time_to_live) { | |
558 TRACE_EVENT_ASYNC_STEP_PAST0("identity", | |
559 "IdentityGetAuthTokenFunction", | |
560 this, | |
561 "OnMintTokenSuccess"); | |
562 | |
563 IdentityTokenCacheValue token(access_token, | |
564 base::TimeDelta::FromSeconds(time_to_live)); | |
565 IdentityAPI::GetFactoryInstance()->Get(GetProfile())->SetCachedToken( | |
566 *token_key_, token); | |
567 | |
568 CompleteMintTokenFlow(); | |
569 CompleteFunctionWithResult(access_token); | |
570 } | |
571 | |
572 void IdentityGetAuthTokenFunction::OnMintTokenFailure( | |
573 const GoogleServiceAuthError& error) { | |
574 TRACE_EVENT_ASYNC_STEP_PAST1("identity", | |
575 "IdentityGetAuthTokenFunction", | |
576 this, | |
577 "OnMintTokenFailure", | |
578 "error", | |
579 error.ToString()); | |
580 CompleteMintTokenFlow(); | |
581 switch (error.state()) { | |
582 case GoogleServiceAuthError::SERVICE_ERROR: | |
583 if (interactive_) { | |
584 StartSigninFlow(); | |
585 return; | |
586 } | |
587 break; | |
588 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: | |
589 case GoogleServiceAuthError::ACCOUNT_DELETED: | |
590 case GoogleServiceAuthError::ACCOUNT_DISABLED: | |
591 // TODO(courage): flush ticket and retry once | |
592 if (should_prompt_for_signin_) { | |
593 // Display a login prompt and try again (once). | |
594 StartSigninFlow(); | |
595 return; | |
596 } | |
597 break; | |
598 default: | |
599 // Return error to caller. | |
600 break; | |
601 } | |
602 | |
603 CompleteFunctionWithError( | |
604 std::string(identity_constants::kAuthFailure) + error.ToString()); | |
605 } | |
606 | |
607 void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess( | |
608 const IssueAdviceInfo& issue_advice) { | |
609 TRACE_EVENT_ASYNC_STEP_PAST0("identity", | |
610 "IdentityGetAuthTokenFunction", | |
611 this, | |
612 "OnIssueAdviceSuccess"); | |
613 | |
614 IdentityAPI::GetFactoryInstance()->Get(GetProfile())->SetCachedToken( | |
615 *token_key_, IdentityTokenCacheValue(issue_advice)); | |
616 CompleteMintTokenFlow(); | |
617 | |
618 should_prompt_for_signin_ = false; | |
619 // Existing grant was revoked and we used NO_FORCE, so we got info back | |
620 // instead. Start a consent UI if we can. | |
621 issue_advice_ = issue_advice; | |
622 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE); | |
623 } | |
624 | |
625 void IdentityGetAuthTokenFunction::SigninSuccess() { | |
626 TRACE_EVENT_ASYNC_STEP_PAST0("identity", | |
627 "IdentityGetAuthTokenFunction", | |
628 this, | |
629 "SigninSuccess"); | |
630 | |
631 // If there was no account associated this profile before the | |
632 // sign-in, we may not have an account_id in the token_key yet. | |
633 if (token_key_->account_id.empty()) { | |
634 token_key_->account_id = GetPrimaryAccountId(GetProfile()); | |
635 } | |
636 | |
637 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE); | |
638 } | |
639 | |
640 void IdentityGetAuthTokenFunction::SigninFailed() { | |
641 TRACE_EVENT_ASYNC_STEP_PAST0("identity", | |
642 "IdentityGetAuthTokenFunction", | |
643 this, | |
644 "SigninFailed"); | |
645 CompleteFunctionWithError(identity_constants::kUserNotSignedIn); | |
646 } | |
647 | |
648 void IdentityGetAuthTokenFunction::OnGaiaFlowFailure( | |
649 GaiaWebAuthFlow::Failure failure, | |
650 GoogleServiceAuthError service_error, | |
651 const std::string& oauth_error) { | |
652 CompleteMintTokenFlow(); | |
653 std::string error; | |
654 | |
655 switch (failure) { | |
656 case GaiaWebAuthFlow::WINDOW_CLOSED: | |
657 error = identity_constants::kUserRejected; | |
658 break; | |
659 | |
660 case GaiaWebAuthFlow::INVALID_REDIRECT: | |
661 error = identity_constants::kInvalidRedirect; | |
662 break; | |
663 | |
664 case GaiaWebAuthFlow::SERVICE_AUTH_ERROR: | |
665 // If this is really an authentication error and not just a transient | |
666 // network error, and this is an interactive request for a signed-in | |
667 // user, then we show signin UI instead of failing. | |
668 if (service_error.state() != GoogleServiceAuthError::CONNECTION_FAILED && | |
669 service_error.state() != | |
670 GoogleServiceAuthError::SERVICE_UNAVAILABLE && | |
671 interactive_ && HasLoginToken()) { | |
672 StartSigninFlow(); | |
673 return; | |
674 } | |
675 error = std::string(identity_constants::kAuthFailure) + | |
676 service_error.ToString(); | |
677 break; | |
678 | |
679 case GaiaWebAuthFlow::OAUTH_ERROR: | |
680 error = MapOAuth2ErrorToDescription(oauth_error); | |
681 break; | |
682 | |
683 case GaiaWebAuthFlow::LOAD_FAILED: | |
684 error = identity_constants::kPageLoadFailure; | |
685 break; | |
686 | |
687 default: | |
688 NOTREACHED() << "Unexpected error from gaia web auth flow: " << failure; | |
689 error = identity_constants::kInvalidRedirect; | |
690 break; | |
691 } | |
692 | |
693 CompleteFunctionWithError(error); | |
694 } | |
695 | |
696 void IdentityGetAuthTokenFunction::OnGaiaFlowCompleted( | |
697 const std::string& access_token, | |
698 const std::string& expiration) { | |
699 TRACE_EVENT_ASYNC_STEP_PAST0("identity", | |
700 "IdentityGetAuthTokenFunction", | |
701 this, | |
702 "OnGaiaFlowCompleted"); | |
703 int time_to_live; | |
704 if (!expiration.empty() && base::StringToInt(expiration, &time_to_live)) { | |
705 IdentityTokenCacheValue token_value( | |
706 access_token, base::TimeDelta::FromSeconds(time_to_live)); | |
707 IdentityAPI::GetFactoryInstance()->Get(GetProfile())->SetCachedToken( | |
708 *token_key_, token_value); | |
709 } | |
710 | |
711 CompleteMintTokenFlow(); | |
712 CompleteFunctionWithResult(access_token); | |
713 } | |
714 | |
715 void IdentityGetAuthTokenFunction::OnGetTokenSuccess( | |
716 const OAuth2TokenService::Request* request, | |
717 const std::string& access_token, | |
718 const base::Time& expiration_time) { | |
719 TRACE_EVENT_ASYNC_STEP_PAST1("identity", | |
720 "IdentityGetAuthTokenFunction", | |
721 this, | |
722 "OnGetTokenSuccess", | |
723 "account", | |
724 request->GetAccountId()); | |
725 login_token_request_.reset(); | |
726 StartGaiaRequest(access_token); | |
727 } | |
728 | |
729 void IdentityGetAuthTokenFunction::OnGetTokenFailure( | |
730 const OAuth2TokenService::Request* request, | |
731 const GoogleServiceAuthError& error) { | |
732 TRACE_EVENT_ASYNC_STEP_PAST1("identity", | |
733 "IdentityGetAuthTokenFunction", | |
734 this, | |
735 "OnGetTokenFailure", | |
736 "error", | |
737 error.ToString()); | |
738 login_token_request_.reset(); | |
739 OnGaiaFlowFailure(GaiaWebAuthFlow::SERVICE_AUTH_ERROR, error, std::string()); | |
740 } | |
741 | |
742 void IdentityGetAuthTokenFunction::Shutdown() { | |
743 gaia_web_auth_flow_.reset(); | |
744 signin_flow_.reset(); | |
745 login_token_request_.reset(); | |
746 extensions::IdentityAPI::GetFactoryInstance() | |
747 ->Get(GetProfile()) | |
748 ->mint_queue() | |
749 ->RequestCancel(*token_key_, this); | |
750 CompleteFunctionWithError(identity_constants::kCanceled); | |
751 } | |
752 | |
753 #if defined(OS_CHROMEOS) | |
754 void IdentityGetAuthTokenFunction::StartDeviceLoginAccessTokenRequest() { | |
755 chromeos::DeviceOAuth2TokenService* service = | |
756 chromeos::DeviceOAuth2TokenServiceFactory::Get(); | |
757 // Since robot account refresh tokens are scoped down to [any-api] only, | |
758 // request access token for [any-api] instead of login. | |
759 OAuth2TokenService::ScopeSet scopes; | |
760 scopes.insert(GaiaConstants::kAnyApiOAuth2Scope); | |
761 login_token_request_ = | |
762 service->StartRequest(service->GetRobotAccountId(), | |
763 scopes, | |
764 this); | |
765 } | |
766 | |
767 bool IdentityGetAuthTokenFunction::IsOriginWhitelistedInPublicSession() { | |
768 DCHECK(extension()); | |
769 GURL extension_url = extension()->url(); | |
770 for (size_t i = 0; i < arraysize(kPublicSessionAllowedOrigins); i++) { | |
771 URLPattern allowed_origin(URLPattern::SCHEME_ALL, | |
772 kPublicSessionAllowedOrigins[i]); | |
773 if (allowed_origin.MatchesSecurityOrigin(extension_url)) { | |
774 return true; | |
775 } | |
776 } | |
777 return false; | |
778 } | |
779 #endif | |
780 | |
781 void IdentityGetAuthTokenFunction::StartLoginAccessTokenRequest() { | |
782 ProfileOAuth2TokenService* service = | |
783 ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile()); | |
784 #if defined(OS_CHROMEOS) | |
785 if (chrome::IsRunningInForcedAppMode()) { | |
786 std::string app_client_id; | |
787 std::string app_client_secret; | |
788 if (chromeos::UserSessionManager::GetInstance()-> | |
789 GetAppModeChromeClientOAuthInfo(&app_client_id, | |
790 &app_client_secret)) { | |
791 login_token_request_ = | |
792 service->StartRequestForClient(token_key_->account_id, | |
793 app_client_id, | |
794 app_client_secret, | |
795 OAuth2TokenService::ScopeSet(), | |
796 this); | |
797 return; | |
798 } | |
799 } | |
800 #endif | |
801 login_token_request_ = service->StartRequest( | |
802 token_key_->account_id, OAuth2TokenService::ScopeSet(), this); | |
803 } | |
804 | |
805 void IdentityGetAuthTokenFunction::StartGaiaRequest( | |
806 const std::string& login_access_token) { | |
807 DCHECK(!login_access_token.empty()); | |
808 mint_token_flow_.reset(CreateMintTokenFlow()); | |
809 mint_token_flow_->Start(GetProfile()->GetRequestContext(), | |
810 login_access_token); | |
811 } | |
812 | |
813 void IdentityGetAuthTokenFunction::ShowLoginPopup() { | |
814 signin_flow_.reset(new IdentitySigninFlow(this, GetProfile())); | |
815 signin_flow_->Start(); | |
816 } | |
817 | |
818 void IdentityGetAuthTokenFunction::ShowOAuthApprovalDialog( | |
819 const IssueAdviceInfo& issue_advice) { | |
820 const std::string locale = extension_l10n_util::CurrentLocaleOrDefault(); | |
821 | |
822 gaia_web_auth_flow_.reset(new GaiaWebAuthFlow( | |
823 this, GetProfile(), token_key_.get(), oauth2_client_id_, locale)); | |
824 gaia_web_auth_flow_->Start(); | |
825 } | |
826 | |
827 OAuth2MintTokenFlow* IdentityGetAuthTokenFunction::CreateMintTokenFlow() { | |
828 SigninClient* signin_client = | |
829 ChromeSigninClientFactory::GetForProfile(GetProfile()); | |
830 std::string signin_scoped_device_id = | |
831 signin_client->GetSigninScopedDeviceId(); | |
832 OAuth2MintTokenFlow* mint_token_flow = new OAuth2MintTokenFlow( | |
833 this, | |
834 OAuth2MintTokenFlow::Parameters( | |
835 extension()->id(), | |
836 oauth2_client_id_, | |
837 std::vector<std::string>(token_key_->scopes.begin(), | |
838 token_key_->scopes.end()), | |
839 signin_scoped_device_id, | |
840 gaia_mint_token_mode_)); | |
841 return mint_token_flow; | |
842 } | |
843 | |
844 bool IdentityGetAuthTokenFunction::HasLoginToken() const { | |
845 ProfileOAuth2TokenService* token_service = | |
846 ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile()); | |
847 return token_service->RefreshTokenIsAvailable(token_key_->account_id); | |
848 } | |
849 | |
850 std::string IdentityGetAuthTokenFunction::MapOAuth2ErrorToDescription( | |
851 const std::string& error) { | |
852 const char kOAuth2ErrorAccessDenied[] = "access_denied"; | |
853 const char kOAuth2ErrorInvalidScope[] = "invalid_scope"; | |
854 | |
855 if (error == kOAuth2ErrorAccessDenied) | |
856 return std::string(identity_constants::kUserRejected); | |
857 else if (error == kOAuth2ErrorInvalidScope) | |
858 return std::string(identity_constants::kInvalidScopes); | |
859 else | |
860 return std::string(identity_constants::kAuthFailure) + error; | |
861 } | |
862 | |
863 std::string IdentityGetAuthTokenFunction::GetOAuth2ClientId() const { | |
864 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension()); | |
865 std::string client_id = oauth2_info.client_id; | |
866 | |
867 // Component apps using auto_approve may use Chrome's client ID by | |
868 // omitting the field. | |
869 if (client_id.empty() && extension()->location() == Manifest::COMPONENT && | |
870 oauth2_info.auto_approve) { | |
871 client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id(); | |
872 } | |
873 return client_id; | |
874 } | |
875 | |
876 } // namespace extensions | 255 } // namespace extensions |
OLD | NEW |