identity_api.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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#include "chrome/browser/extensions/api/identity/identity_api.h" 6 7#include <set> 8#include <string> 9#include <utility> 10#include <vector> 11 12#include "base/lazy_instance.h" 13#include "base/prefs/pref_service.h" 14#include "base/strings/string_number_conversions.h" 15#include "base/strings/stringprintf.h" 16#include "base/values.h" 17#include "chrome/browser/app_mode/app_mode_utils.h" 18#include "chrome/browser/browser_process.h" 19#include "chrome/browser/chrome_notification_types.h" 20#include "chrome/browser/extensions/extension_service.h" 21#include "chrome/browser/profiles/profile.h" 22#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 23#include "chrome/browser/signin/signin_global_error.h" 24#include "chrome/browser/signin/signin_manager_factory.h" 25#include "chrome/common/extensions/api/identity.h" 26#include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h" 27#include "chrome/common/pref_names.h" 28#include "chrome/common/url_constants.h" 29#include "components/signin/core/browser/profile_oauth2_token_service.h" 30#include "components/signin/core/browser/signin_manager.h" 31#include "extensions/browser/event_router.h" 32#include "extensions/browser/extension_function_dispatcher.h" 33#include "extensions/common/extension.h" 34#include "google_apis/gaia/gaia_urls.h" 35#include "url/gurl.h" 36 37#if defined(OS_CHROMEOS) 38#include "chrome/browser/chromeos/login/user_manager.h" 39#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" 40#include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" 41#include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h" 42#include "google_apis/gaia/gaia_constants.h" 43#endif 44 45namespace extensions { 46 47namespace identity_constants { 48const char kInvalidClientId[] = "Invalid OAuth2 Client ID."; 49const char kInvalidScopes[] = "Invalid OAuth2 scopes."; 50const char kAuthFailure[] = "OAuth2 request failed: "; 51const char kNoGrant[] = "OAuth2 not granted or revoked."; 52const char kUserRejected[] = "The user did not approve access."; 53const char kUserNotSignedIn[] = "The user is not signed in."; 54const char kInteractionRequired[] = "User interaction required."; 55const char kInvalidRedirect[] = "Did not redirect to the right URL."; 56const char kOffTheRecord[] = "Identity API is disabled in incognito windows."; 57const char kPageLoadFailure[] = "Authorization page could not be loaded."; 58const char kCanceled[] = "canceled"; 59 60const int kCachedIssueAdviceTTLSeconds = 1; 61} // namespace identity_constants 62 63namespace { 64 65static const char kChromiumDomainRedirectUrlPattern[] = 66 "https://%s.chromiumapp.org/"; 67 68std::string GetPrimaryAccountId(content::BrowserContext* context) { 69 SigninManagerBase* signin_manager = 70 SigninManagerFactory::GetForProfile(Profile::FromBrowserContext(context)); 71 return signin_manager->GetAuthenticatedAccountId(); 72} 73 74} // namespace 75 76namespace identity = api::identity; 77 78IdentityTokenCacheValue::IdentityTokenCacheValue() 79 : status_(CACHE_STATUS_NOTFOUND) {} 80 81IdentityTokenCacheValue::IdentityTokenCacheValue( 82 const IssueAdviceInfo& issue_advice) 83 : status_(CACHE_STATUS_ADVICE), issue_advice_(issue_advice) { 84 expiration_time_ = 85 base::Time::Now() + base::TimeDelta::FromSeconds( 86 identity_constants::kCachedIssueAdviceTTLSeconds); 87} 88 89IdentityTokenCacheValue::IdentityTokenCacheValue(const std::string& token, 90 base::TimeDelta time_to_live) 91 : status_(CACHE_STATUS_TOKEN), token_(token) { 92 // Remove 20 minutes from the ttl so cached tokens will have some time 93 // to live any time they are returned. 94 time_to_live -= base::TimeDelta::FromMinutes(20); 95 96 base::TimeDelta zero_delta; 97 if (time_to_live < zero_delta) 98 time_to_live = zero_delta; 99 100 expiration_time_ = base::Time::Now() + time_to_live; 101} 102 103IdentityTokenCacheValue::~IdentityTokenCacheValue() {} 104 105IdentityTokenCacheValue::CacheValueStatus IdentityTokenCacheValue::status() 106 const { 107 if (is_expired()) 108 return IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND; 109 else 110 return status_; 111} 112 113const IssueAdviceInfo& IdentityTokenCacheValue::issue_advice() const { 114 return issue_advice_; 115} 116 117const std::string& IdentityTokenCacheValue::token() const { return token_; } 118 119bool IdentityTokenCacheValue::is_expired() const { 120 return status_ == CACHE_STATUS_NOTFOUND || 121 expiration_time_ < base::Time::Now(); 122} 123 124const base::Time& IdentityTokenCacheValue::expiration_time() const { 125 return expiration_time_; 126} 127 128IdentityAPI::IdentityAPI(content::BrowserContext* context) 129 : browser_context_(context), 130 account_tracker_(Profile::FromBrowserContext(context)) { 131 account_tracker_.AddObserver(this); 132} 133 134IdentityAPI::~IdentityAPI() {} 135 136IdentityMintRequestQueue* IdentityAPI::mint_queue() { return &mint_queue_; } 137 138void IdentityAPI::SetCachedToken(const ExtensionTokenKey& key, 139 const IdentityTokenCacheValue& token_data) { 140 CachedTokens::iterator it = token_cache_.find(key); 141 if (it != token_cache_.end() && it->second.status() <= token_data.status()) 142 token_cache_.erase(it); 143 144 token_cache_.insert(std::make_pair(key, token_data)); 145} 146 147void IdentityAPI::EraseCachedToken(const std::string& extension_id, 148 const std::string& token) { 149 CachedTokens::iterator it; 150 for (it = token_cache_.begin(); it != token_cache_.end(); ++it) { 151 if (it->first.extension_id == extension_id && 152 it->second.status() == IdentityTokenCacheValue::CACHE_STATUS_TOKEN && 153 it->second.token() == token) { 154 token_cache_.erase(it); 155 break; 156 } 157 } 158} 159 160void IdentityAPI::EraseAllCachedTokens() { token_cache_.clear(); } 161 162const IdentityTokenCacheValue& IdentityAPI::GetCachedToken( 163 const ExtensionTokenKey& key) { 164 return token_cache_[key]; 165} 166 167const IdentityAPI::CachedTokens& IdentityAPI::GetAllCachedTokens() { 168 return token_cache_; 169} 170 171void IdentityAPI::ReportAuthError(const GoogleServiceAuthError& error) { 172 account_tracker_.ReportAuthError(GetPrimaryAccountId(browser_context_), 173 error); 174} 175 176GoogleServiceAuthError IdentityAPI::GetAuthStatusForTest() const { 177 return account_tracker_.GetAuthStatus(); 178} 179 180void IdentityAPI::Shutdown() { 181 FOR_EACH_OBSERVER(ShutdownObserver, shutdown_observer_list_, OnShutdown()); 182 account_tracker_.RemoveObserver(this); 183 account_tracker_.Shutdown(); 184} 185 186static base::LazyInstance<BrowserContextKeyedAPIFactory<IdentityAPI> > 187 g_factory = LAZY_INSTANCE_INITIALIZER; 188 189// static 190BrowserContextKeyedAPIFactory<IdentityAPI>* IdentityAPI::GetFactoryInstance() { 191 return g_factory.Pointer(); 192} 193 194void IdentityAPI::OnAccountAdded(const AccountIds& ids) {} 195 196void IdentityAPI::OnAccountRemoved(const AccountIds& ids) {} 197 198void IdentityAPI::OnAccountSignInChanged(const AccountIds& ids, 199 bool is_signed_in) { 200 api::identity::AccountInfo account_info; 201 account_info.id = ids.gaia; 202 203 scoped_ptr<base::ListValue> args = 204 api::identity::OnSignInChanged::Create(account_info, is_signed_in); 205 scoped_ptr<Event> event(new Event(api::identity::OnSignInChanged::kEventName, 206 args.Pass(), 207 browser_context_)); 208 209 EventRouter::Get(browser_context_)->BroadcastEvent(event.Pass()); 210} 211 212void IdentityAPI::AddShutdownObserver(ShutdownObserver* observer) { 213 shutdown_observer_list_.AddObserver(observer); 214} 215 216void IdentityAPI::RemoveShutdownObserver(ShutdownObserver* observer) { 217 shutdown_observer_list_.RemoveObserver(observer); 218} 219 220template <> 221void BrowserContextKeyedAPIFactory<IdentityAPI>::DeclareFactoryDependencies() { 222 DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory()); 223 DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance()); 224} 225 226IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction() 227 : OAuth2TokenService::Consumer("extensions_identity_api"), 228 should_prompt_for_scopes_(false), 229 should_prompt_for_signin_(false) {} 230 231IdentityGetAuthTokenFunction::~IdentityGetAuthTokenFunction() {} 232 233bool IdentityGetAuthTokenFunction::RunImpl() { 234 if (GetProfile()->IsOffTheRecord()) { 235 error_ = identity_constants::kOffTheRecord; 236 return false; 237 } 238 239 scoped_ptr<identity::GetAuthToken::Params> params( 240 identity::GetAuthToken::Params::Create(*args_)); 241 EXTENSION_FUNCTION_VALIDATE(params.get()); 242 bool interactive = params->details.get() && 243 params->details->interactive.get() && 244 *params->details->interactive; 245 246 should_prompt_for_scopes_ = interactive; 247 should_prompt_for_signin_ = interactive; 248 249 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 250 251 // Check that the necessary information is present in the manifest. 252 oauth2_client_id_ = GetOAuth2ClientId(); 253 if (oauth2_client_id_.empty()) { 254 error_ = identity_constants::kInvalidClientId; 255 return false; 256 } 257 258 if (oauth2_info.scopes.size() == 0) { 259 error_ = identity_constants::kInvalidScopes; 260 return false; 261 } 262 263 std::set<std::string> scopes(oauth2_info.scopes.begin(), 264 oauth2_info.scopes.end()); 265 token_key_.reset(new ExtensionTokenKey( 266 GetExtension()->id(), GetPrimaryAccountId(GetProfile()), scopes)); 267 268 // From here on out, results must be returned asynchronously. 269 StartAsyncRun(); 270 271#if defined(OS_CHROMEOS) 272 policy::BrowserPolicyConnectorChromeOS* connector = 273 g_browser_process->platform_part()->browser_policy_connector_chromeos(); 274 if (chromeos::UserManager::Get()->IsLoggedInAsKioskApp() && 275 connector->IsEnterpriseManaged()) { 276 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE); 277 return true; 278 } 279#endif 280 281 if (!HasLoginToken()) { 282 if (!should_prompt_for_signin_) { 283 CompleteFunctionWithError(identity_constants::kUserNotSignedIn); 284 return true; 285 } 286 // Display a login prompt. 287 StartSigninFlow(); 288 } else { 289 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE); 290 } 291 292 return true; 293} 294 295void IdentityGetAuthTokenFunction::StartAsyncRun() { 296 // Balanced in CompleteAsyncRun 297 AddRef(); 298 extensions::IdentityAPI::GetFactoryInstance() 299 ->Get(GetProfile()) 300 ->AddShutdownObserver(this); 301} 302 303void IdentityGetAuthTokenFunction::CompleteAsyncRun(bool success) { 304 extensions::IdentityAPI::GetFactoryInstance() 305 ->Get(GetProfile()) 306 ->RemoveShutdownObserver(this); 307 308 SendResponse(success); 309 Release(); // Balanced in StartAsyncRun 310} 311 312void IdentityGetAuthTokenFunction::CompleteFunctionWithResult( 313 const std::string& access_token) { 314 315 SetResult(new base::StringValue(access_token)); 316 CompleteAsyncRun(true); 317} 318 319void IdentityGetAuthTokenFunction::CompleteFunctionWithError( 320 const std::string& error) { 321 error_ = error; 322 CompleteAsyncRun(false); 323} 324 325void IdentityGetAuthTokenFunction::StartSigninFlow() { 326 // All cached tokens are invalid because the user is not signed in. 327 IdentityAPI* id_api = 328 extensions::IdentityAPI::GetFactoryInstance()->Get(GetProfile()); 329 id_api->EraseAllCachedTokens(); 330 // Display a login prompt. If the subsequent mint fails, don't display the 331 // login prompt again. 332 should_prompt_for_signin_ = false; 333 ShowLoginPopup(); 334} 335 336void IdentityGetAuthTokenFunction::StartMintTokenFlow( 337 IdentityMintRequestQueue::MintType type) { 338 mint_token_flow_type_ = type; 339 340 // Flows are serialized to prevent excessive traffic to GAIA, and 341 // to consolidate UI pop-ups. 342 IdentityAPI* id_api = 343 extensions::IdentityAPI::GetFactoryInstance()->Get(GetProfile()); 344 345 if (!should_prompt_for_scopes_) { 346 // Caller requested no interaction. 347 348 if (type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE) { 349 // GAIA told us to do a consent UI. 350 CompleteFunctionWithError(identity_constants::kNoGrant); 351 return; 352 } 353 if (!id_api->mint_queue()->empty( 354 IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE, *token_key_)) { 355 // Another call is going through a consent UI. 356 CompleteFunctionWithError(identity_constants::kNoGrant); 357 return; 358 } 359 } 360 id_api->mint_queue()->RequestStart(type, *token_key_, this); 361} 362 363void IdentityGetAuthTokenFunction::CompleteMintTokenFlow() { 364 IdentityMintRequestQueue::MintType type = mint_token_flow_type_; 365 366 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 367 std::set<std::string> scopes(oauth2_info.scopes.begin(), 368 oauth2_info.scopes.end()); 369 370 extensions::IdentityAPI::GetFactoryInstance() 371 ->Get(GetProfile()) 372 ->mint_queue() 373 ->RequestComplete(type, *token_key_, this); 374} 375 376void IdentityGetAuthTokenFunction::StartMintToken( 377 IdentityMintRequestQueue::MintType type) { 378 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 379 IdentityAPI* id_api = IdentityAPI::GetFactoryInstance()->Get(GetProfile()); 380 IdentityTokenCacheValue cache_entry = id_api->GetCachedToken(*token_key_); 381 IdentityTokenCacheValue::CacheValueStatus cache_status = 382 cache_entry.status(); 383 384 if (type == IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE) { 385 switch (cache_status) { 386 case IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND: 387#if defined(OS_CHROMEOS) 388 // Always force minting token for ChromeOS kiosk app. 389 if (chromeos::UserManager::Get()->IsLoggedInAsKioskApp()) { 390 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE; 391 policy::BrowserPolicyConnectorChromeOS* connector = 392 g_browser_process->platform_part() 393 ->browser_policy_connector_chromeos(); 394 if (connector->IsEnterpriseManaged()) { 395 StartDeviceLoginAccessTokenRequest(); 396 } else { 397 StartLoginAccessTokenRequest(); 398 } 399 return; 400 } 401#endif 402 403 if (oauth2_info.auto_approve) 404 // oauth2_info.auto_approve is protected by a whitelist in 405 // _manifest_features.json hence only selected extensions take 406 // advantage of forcefully minting the token. 407 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE; 408 else 409 gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE; 410 StartLoginAccessTokenRequest(); 411 break; 412 413 case IdentityTokenCacheValue::CACHE_STATUS_TOKEN: 414 CompleteMintTokenFlow(); 415 CompleteFunctionWithResult(cache_entry.token()); 416 break; 417 418 case IdentityTokenCacheValue::CACHE_STATUS_ADVICE: 419 CompleteMintTokenFlow(); 420 should_prompt_for_signin_ = false; 421 issue_advice_ = cache_entry.issue_advice(); 422 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE); 423 break; 424 } 425 } else { 426 DCHECK(type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE); 427 428 if (cache_status == IdentityTokenCacheValue::CACHE_STATUS_TOKEN) { 429 CompleteMintTokenFlow(); 430 CompleteFunctionWithResult(cache_entry.token()); 431 } else { 432 ShowOAuthApprovalDialog(issue_advice_); 433 } 434 } 435} 436 437void IdentityGetAuthTokenFunction::OnMintTokenSuccess( 438 const std::string& access_token, int time_to_live) { 439 IdentityTokenCacheValue token(access_token, 440 base::TimeDelta::FromSeconds(time_to_live)); 441 IdentityAPI::GetFactoryInstance()->Get(GetProfile())->SetCachedToken( 442 *token_key_, token); 443 444 CompleteMintTokenFlow(); 445 CompleteFunctionWithResult(access_token); 446} 447 448void IdentityGetAuthTokenFunction::OnMintTokenFailure( 449 const GoogleServiceAuthError& error) { 450 CompleteMintTokenFlow(); 451 452 switch (error.state()) { 453 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: 454 case GoogleServiceAuthError::ACCOUNT_DELETED: 455 case GoogleServiceAuthError::ACCOUNT_DISABLED: 456 extensions::IdentityAPI::GetFactoryInstance() 457 ->Get(GetProfile()) 458 ->ReportAuthError(error); 459 if (should_prompt_for_signin_) { 460 // Display a login prompt and try again (once). 461 StartSigninFlow(); 462 return; 463 } 464 break; 465 default: 466 // Return error to caller. 467 break; 468 } 469 470 CompleteFunctionWithError( 471 std::string(identity_constants::kAuthFailure) + error.ToString()); 472} 473 474void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess( 475 const IssueAdviceInfo& issue_advice) { 476 IdentityAPI::GetFactoryInstance()->Get(GetProfile())->SetCachedToken( 477 *token_key_, IdentityTokenCacheValue(issue_advice)); 478 CompleteMintTokenFlow(); 479 480 should_prompt_for_signin_ = false; 481 // Existing grant was revoked and we used NO_FORCE, so we got info back 482 // instead. Start a consent UI if we can. 483 issue_advice_ = issue_advice; 484 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE); 485} 486 487void IdentityGetAuthTokenFunction::SigninSuccess() { 488 StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE); 489} 490 491void IdentityGetAuthTokenFunction::SigninFailed() { 492 CompleteFunctionWithError(identity_constants::kUserNotSignedIn); 493} 494 495void IdentityGetAuthTokenFunction::OnGaiaFlowFailure( 496 GaiaWebAuthFlow::Failure failure, 497 GoogleServiceAuthError service_error, 498 const std::string& oauth_error) { 499 CompleteMintTokenFlow(); 500 std::string error; 501 502 switch (failure) { 503 case GaiaWebAuthFlow::WINDOW_CLOSED: 504 error = identity_constants::kUserRejected; 505 break; 506 507 case GaiaWebAuthFlow::INVALID_REDIRECT: 508 error = identity_constants::kInvalidRedirect; 509 break; 510 511 case GaiaWebAuthFlow::SERVICE_AUTH_ERROR: 512 error = std::string(identity_constants::kAuthFailure) + 513 service_error.ToString(); 514 break; 515 516 case GaiaWebAuthFlow::OAUTH_ERROR: 517 error = MapOAuth2ErrorToDescription(oauth_error); 518 break; 519 520 case GaiaWebAuthFlow::LOAD_FAILED: 521 error = identity_constants::kPageLoadFailure; 522 break; 523 524 default: 525 NOTREACHED() << "Unexpected error from gaia web auth flow: " << failure; 526 error = identity_constants::kInvalidRedirect; 527 break; 528 } 529 530 CompleteFunctionWithError(error); 531} 532 533void IdentityGetAuthTokenFunction::OnGaiaFlowCompleted( 534 const std::string& access_token, 535 const std::string& expiration) { 536 537 int time_to_live; 538 if (!expiration.empty() && base::StringToInt(expiration, &time_to_live)) { 539 IdentityTokenCacheValue token_value( 540 access_token, base::TimeDelta::FromSeconds(time_to_live)); 541 IdentityAPI::GetFactoryInstance()->Get(GetProfile())->SetCachedToken( 542 *token_key_, token_value); 543 } 544 545 CompleteMintTokenFlow(); 546 CompleteFunctionWithResult(access_token); 547} 548 549void IdentityGetAuthTokenFunction::OnGetTokenSuccess( 550 const OAuth2TokenService::Request* request, 551 const std::string& access_token, 552 const base::Time& expiration_time) { 553 login_token_request_.reset(); 554 StartGaiaRequest(access_token); 555} 556 557void IdentityGetAuthTokenFunction::OnGetTokenFailure( 558 const OAuth2TokenService::Request* request, 559 const GoogleServiceAuthError& error) { 560 login_token_request_.reset(); 561 OnGaiaFlowFailure(GaiaWebAuthFlow::SERVICE_AUTH_ERROR, error, std::string()); 562} 563 564void IdentityGetAuthTokenFunction::OnShutdown() { 565 gaia_web_auth_flow_.reset(); 566 signin_flow_.reset(); 567 login_token_request_.reset(); 568 extensions::IdentityAPI::GetFactoryInstance() 569 ->Get(GetProfile()) 570 ->mint_queue() 571 ->RequestCancel(*token_key_, this); 572 CompleteFunctionWithError(identity_constants::kCanceled); 573} 574 575#if defined(OS_CHROMEOS) 576void IdentityGetAuthTokenFunction::StartDeviceLoginAccessTokenRequest() { 577 chromeos::DeviceOAuth2TokenService* service = 578 chromeos::DeviceOAuth2TokenServiceFactory::Get(); 579 // Since robot account refresh tokens are scoped down to [any-api] only, 580 // request access token for [any-api] instead of login. 581 OAuth2TokenService::ScopeSet scopes; 582 scopes.insert(GaiaConstants::kAnyApiOAuth2Scope); 583 login_token_request_ = 584 service->StartRequest(service->GetRobotAccountId(), 585 scopes, 586 this); 587} 588#endif 589 590void IdentityGetAuthTokenFunction::StartLoginAccessTokenRequest() { 591 ProfileOAuth2TokenService* service = 592 ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile()); 593 const std::string primary_account_id = GetPrimaryAccountId(GetProfile()); 594#if defined(OS_CHROMEOS) 595 if (chrome::IsRunningInForcedAppMode()) { 596 std::string app_client_id; 597 std::string app_client_secret; 598 if (chromeos::UserManager::Get()->GetAppModeChromeClientOAuthInfo( 599 &app_client_id, &app_client_secret)) { 600 login_token_request_ = 601 service->StartRequestForClient(primary_account_id, 602 app_client_id, 603 app_client_secret, 604 OAuth2TokenService::ScopeSet(), 605 this); 606 return; 607 } 608 } 609#endif 610 login_token_request_ = service->StartRequest( 611 primary_account_id, OAuth2TokenService::ScopeSet(), this); 612} 613 614void IdentityGetAuthTokenFunction::StartGaiaRequest( 615 const std::string& login_access_token) { 616 DCHECK(!login_access_token.empty()); 617 mint_token_flow_.reset(CreateMintTokenFlow(login_access_token)); 618 mint_token_flow_->Start(); 619} 620 621void IdentityGetAuthTokenFunction::ShowLoginPopup() { 622 signin_flow_.reset(new IdentitySigninFlow(this, GetProfile())); 623 signin_flow_->Start(); 624} 625 626void IdentityGetAuthTokenFunction::ShowOAuthApprovalDialog( 627 const IssueAdviceInfo& issue_advice) { 628 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 629 const std::string locale = g_browser_process->local_state()->GetString( 630 prefs::kApplicationLocale); 631 632 gaia_web_auth_flow_.reset(new GaiaWebAuthFlow( 633 this, GetProfile(), GetExtension()->id(), oauth2_info, locale)); 634 gaia_web_auth_flow_->Start(); 635} 636 637OAuth2MintTokenFlow* IdentityGetAuthTokenFunction::CreateMintTokenFlow( 638 const std::string& login_access_token) { 639 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 640 641 OAuth2MintTokenFlow* mint_token_flow = new OAuth2MintTokenFlow( 642 GetProfile()->GetRequestContext(), 643 this, 644 OAuth2MintTokenFlow::Parameters(login_access_token, 645 GetExtension()->id(), 646 oauth2_client_id_, 647 oauth2_info.scopes, 648 gaia_mint_token_mode_)); 649 return mint_token_flow; 650} 651 652bool IdentityGetAuthTokenFunction::HasLoginToken() const { 653 ProfileOAuth2TokenService* token_service = 654 ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile()); 655 return token_service->RefreshTokenIsAvailable( 656 GetPrimaryAccountId(GetProfile())); 657} 658 659std::string IdentityGetAuthTokenFunction::MapOAuth2ErrorToDescription( 660 const std::string& error) { 661 const char kOAuth2ErrorAccessDenied[] = "access_denied"; 662 const char kOAuth2ErrorInvalidScope[] = "invalid_scope"; 663 664 if (error == kOAuth2ErrorAccessDenied) 665 return std::string(identity_constants::kUserRejected); 666 else if (error == kOAuth2ErrorInvalidScope) 667 return std::string(identity_constants::kInvalidScopes); 668 else 669 return std::string(identity_constants::kAuthFailure) + error; 670} 671 672std::string IdentityGetAuthTokenFunction::GetOAuth2ClientId() const { 673 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 674 std::string client_id = oauth2_info.client_id; 675 676 // Component apps using auto_approve may use Chrome's client ID by 677 // omitting the field. 678 if (client_id.empty() && GetExtension()->location() == Manifest::COMPONENT && 679 oauth2_info.auto_approve) { 680 client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id(); 681 } 682 return client_id; 683} 684 685IdentityRemoveCachedAuthTokenFunction::IdentityRemoveCachedAuthTokenFunction() { 686} 687 688IdentityRemoveCachedAuthTokenFunction:: 689 ~IdentityRemoveCachedAuthTokenFunction() { 690} 691 692bool IdentityRemoveCachedAuthTokenFunction::RunSync() { 693 if (GetProfile()->IsOffTheRecord()) { 694 error_ = identity_constants::kOffTheRecord; 695 return false; 696 } 697 698 scoped_ptr<identity::RemoveCachedAuthToken::Params> params( 699 identity::RemoveCachedAuthToken::Params::Create(*args_)); 700 EXTENSION_FUNCTION_VALIDATE(params.get()); 701 IdentityAPI::GetFactoryInstance()->Get(GetProfile())->EraseCachedToken( 702 GetExtension()->id(), params->details.token); 703 return true; 704} 705 706IdentityLaunchWebAuthFlowFunction::IdentityLaunchWebAuthFlowFunction() {} 707 708IdentityLaunchWebAuthFlowFunction::~IdentityLaunchWebAuthFlowFunction() { 709 if (auth_flow_) 710 auth_flow_.release()->DetachDelegateAndDelete(); 711} 712 713bool IdentityLaunchWebAuthFlowFunction::RunImpl() { 714 if (GetProfile()->IsOffTheRecord()) { 715 error_ = identity_constants::kOffTheRecord; 716 return false; 717 } 718 719 scoped_ptr<identity::LaunchWebAuthFlow::Params> params( 720 identity::LaunchWebAuthFlow::Params::Create(*args_)); 721 EXTENSION_FUNCTION_VALIDATE(params.get()); 722 723 GURL auth_url(params->details.url); 724 WebAuthFlow::Mode mode = 725 params->details.interactive && *params->details.interactive ? 726 WebAuthFlow::INTERACTIVE : WebAuthFlow::SILENT; 727 728 // Set up acceptable target URLs. (Does not include chrome-extension 729 // scheme for this version of the API.) 730 InitFinalRedirectURLPrefix(GetExtension()->id()); 731 732 AddRef(); // Balanced in OnAuthFlowSuccess/Failure. 733 734 auth_flow_.reset(new WebAuthFlow(this, GetProfile(), auth_url, mode)); 735 auth_flow_->Start(); 736 return true; 737} 738 739void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefixForTest( 740 const std::string& extension_id) { 741 InitFinalRedirectURLPrefix(extension_id); 742} 743 744void IdentityLaunchWebAuthFlowFunction::InitFinalRedirectURLPrefix( 745 const std::string& extension_id) { 746 if (final_url_prefix_.is_empty()) { 747 final_url_prefix_ = GURL(base::StringPrintf( 748 kChromiumDomainRedirectUrlPattern, extension_id.c_str())); 749 } 750} 751 752void IdentityLaunchWebAuthFlowFunction::OnAuthFlowFailure( 753 WebAuthFlow::Failure failure) { 754 switch (failure) { 755 case WebAuthFlow::WINDOW_CLOSED: 756 error_ = identity_constants::kUserRejected; 757 break; 758 case WebAuthFlow::INTERACTION_REQUIRED: 759 error_ = identity_constants::kInteractionRequired; 760 break; 761 case WebAuthFlow::LOAD_FAILED: 762 error_ = identity_constants::kPageLoadFailure; 763 break; 764 default: 765 NOTREACHED() << "Unexpected error from web auth flow: " << failure; 766 error_ = identity_constants::kInvalidRedirect; 767 break; 768 } 769 SendResponse(false); 770 Release(); // Balanced in RunImpl. 771} 772 773void IdentityLaunchWebAuthFlowFunction::OnAuthFlowURLChange( 774 const GURL& redirect_url) { 775 if (redirect_url.GetWithEmptyPath() == final_url_prefix_) { 776 SetResult(new base::StringValue(redirect_url.spec())); 777 SendResponse(true); 778 Release(); // Balanced in RunImpl. 779 } 780} 781 782} // namespace extensions 783