blob: e535debf8c107670ad1a0f5b929fd3ced3684204 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/api/identity/identity_get_auth_token_function.h"
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/api/identity/identity_api.h"
#include "chrome/browser/extensions/api/identity/identity_constants.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/account_tracker_service_factory.h"
#include "chrome/browser/signin/chrome_device_id_helper.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/common/extensions/api/identity.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/account_tracker_service.h"
#include "components/signin/core/browser/profile_oauth2_token_service.h"
#include "components/signin/core/browser/signin_pref_names.h"
#include "content/public/common/service_manager_connection.h"
#include "extensions/common/extension_l10n_util.h"
#include "google_apis/gaia/gaia_urls.h"
#include "services/identity/public/cpp/scope_set.h"
#include "services/identity/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/app_mode/app_mode_utils.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/login/session/user_session_manager.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
#include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h"
#include "components/user_manager/user_manager.h"
#include "google_apis/gaia/gaia_constants.h"
#endif
namespace extensions {
namespace {
#if defined(OS_CHROMEOS)
// The list of apps that are allowed to use the Identity API to retrieve the
// token from the device robot account in a public session.
const char* const kPublicSessionAllowedOrigins[] = {
// Chrome Remote Desktop - Chromium branding.
"chrome-extension://ljacajndfccfgnfohlgkdphmbnpkjflk/",
// Chrome Remote Desktop - Official branding.
"chrome-extension://gbchcmhmhahfdphkhkmpfmihenigjmpp/"};
#endif
bool IsBrowserSigninAllowed(Profile* profile) {
return profile->GetPrefs()->GetBoolean(prefs::kSigninAllowed);
}
} // namespace
IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction()
:
#if defined(OS_CHROMEOS)
OAuth2TokenService::Consumer("extensions_identity_api"),
#endif
interactive_(false),
should_prompt_for_scopes_(false),
should_prompt_for_signin_(false) {
}
IdentityGetAuthTokenFunction::~IdentityGetAuthTokenFunction() {
TRACE_EVENT_ASYNC_END0("identity", "IdentityGetAuthTokenFunction", this);
}
bool IdentityGetAuthTokenFunction::RunAsync() {
TRACE_EVENT_ASYNC_BEGIN1("identity",
"IdentityGetAuthTokenFunction",
this,
"extension",
extension()->id());
if (GetProfile()->IsOffTheRecord()) {
error_ = identity_constants::kOffTheRecord;
return false;
}
std::unique_ptr<api::identity::GetAuthToken::Params> params(
api::identity::GetAuthToken::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
interactive_ = params->details.get() &&
params->details->interactive.get() &&
*params->details->interactive;
should_prompt_for_scopes_ = interactive_;
should_prompt_for_signin_ =
interactive_ && IsBrowserSigninAllowed(GetProfile());
const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension());
// Check that the necessary information is present in the manifest.
oauth2_client_id_ = GetOAuth2ClientId();
if (oauth2_client_id_.empty()) {
error_ = identity_constants::kInvalidClientId;
return false;
}
std::set<std::string> scopes(oauth2_info.scopes.begin(),
oauth2_info.scopes.end());
std::string gaia_id;
if (params->details.get()) {
if (params->details->account.get())
gaia_id = params->details->account->id;
if (params->details->scopes.get()) {
scopes = std::set<std::string>(params->details->scopes->begin(),
params->details->scopes->end());
}
}
if (scopes.size() == 0) {
error_ = identity_constants::kInvalidScopes;
return false;
}
// From here on out, results must be returned asynchronously.
StartAsyncRun();
GetMojoIdentityManager()->GetPrimaryAccountInfo(base::BindOnce(
&IdentityGetAuthTokenFunction::OnReceivedPrimaryAccountInfo, this, scopes,
gaia_id));
return true;
}
void IdentityGetAuthTokenFunction::OnReceivedPrimaryAccountInfo(
const std::set<std::string>& scopes,
const std::string& extension_gaia_id,
const base::Optional<AccountInfo>& account_info,
const ::identity::AccountState& account_state) {
std::string primary_gaia_id;
if (account_info)
primary_gaia_id = account_info->gaia;
// Detect and handle the case where the extension is using an account other
// than the primary account.
if (!extension_gaia_id.empty() && extension_gaia_id != primary_gaia_id) {
bool primary_account_only = IdentityAPI::GetFactoryInstance()
->Get(GetProfile())
->AreExtensionsRestrictedToPrimaryAccount();
if (primary_account_only) {
// TODO(courage): should this be a different error?
CompleteFunctionWithError(identity_constants::kUserNotSignedIn);
return;
}
// Get the AccountInfo for the account that the extension wishes to use.
mojo_identity_manager_->GetAccountInfoFromGaiaId(
extension_gaia_id,
base::BindOnce(
&IdentityGetAuthTokenFunction::OnReceivedExtensionAccountInfo, this,
false /* not primary account */, scopes));
return;
}
// The extension is using the primary account.
OnReceivedExtensionAccountInfo(true /* primary account */, scopes,
account_info, account_state);
}
void IdentityGetAuthTokenFunction::OnReceivedExtensionAccountInfo(
bool is_primary_account,
const std::set<std::string>& scopes,
const base::Optional<AccountInfo>& account_info,
const ::identity::AccountState& account_state) {
std::string account_id;
if (account_info)
account_id = account_info->account_id;
if (!is_primary_account && account_id.empty()) {
// It is not possible to sign in the user to an account other than the
// primary account, so just error out here.
CompleteFunctionWithError(identity_constants::kUserNotSignedIn);
return;
}
token_key_.reset(
new ExtensionTokenKey(extension()->id(), account_id, scopes));
#if defined(OS_CHROMEOS)
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
bool is_kiosk = user_manager::UserManager::Get()->IsLoggedInAsKioskApp();
bool is_public_session =
user_manager::UserManager::Get()->IsLoggedInAsPublicAccount();
if (connector->IsEnterpriseManaged() && (is_kiosk || is_public_session)) {
if (is_public_session && !IsOriginWhitelistedInPublicSession()) {
CompleteFunctionWithError(identity_constants::kUserNotSignedIn);
return;
}
StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE);
return;
}
#endif
if (!account_state.has_refresh_token) {
if (!ShouldStartSigninFlow()) {
CompleteFunctionWithError(
IsBrowserSigninAllowed(GetProfile())
? identity_constants::kUserNotSignedIn
: identity_constants::kBrowserSigninNotAllowed);
return;
}
// Display a login prompt.
StartSigninFlow();
} else {
StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE);
}
}
void IdentityGetAuthTokenFunction::StartAsyncRun() {
// Balanced in CompleteAsyncRun
AddRef();
identity_api_shutdown_subscription_ =
extensions::IdentityAPI::GetFactoryInstance()
->Get(GetProfile())
->RegisterOnShutdownCallback(base::Bind(
&IdentityGetAuthTokenFunction::OnIdentityAPIShutdown, this));
}
void IdentityGetAuthTokenFunction::CompleteAsyncRun(bool success) {
identity_api_shutdown_subscription_.reset();
SendResponse(success);
Release(); // Balanced in StartAsyncRun
}
void IdentityGetAuthTokenFunction::CompleteFunctionWithResult(
const std::string& access_token) {
SetResult(std::make_unique<base::Value>(access_token));
CompleteAsyncRun(true);
}
void IdentityGetAuthTokenFunction::CompleteFunctionWithError(
const std::string& error) {
TRACE_EVENT_ASYNC_STEP_PAST1("identity",
"IdentityGetAuthTokenFunction",
this,
"CompleteFunctionWithError",
"error",
error);
error_ = error;
CompleteAsyncRun(false);
}
bool IdentityGetAuthTokenFunction::ShouldStartSigninFlow() {
if (!should_prompt_for_signin_)
return false;
ProfileOAuth2TokenService* token_service =
ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
bool account_needs_reauth =
!token_service->RefreshTokenIsAvailable(token_key_->account_id) ||
token_service->RefreshTokenHasError(token_key_->account_id);
return account_needs_reauth;
}
void IdentityGetAuthTokenFunction::StartSigninFlow() {
DCHECK(ShouldStartSigninFlow());
// All cached tokens are invalid because the user is not signed in.
IdentityAPI* id_api =
extensions::IdentityAPI::GetFactoryInstance()->Get(GetProfile());
id_api->EraseAllCachedTokens();
// If the signin flow fails, don't display the login prompt again.
should_prompt_for_signin_ = false;
#if defined(OS_CHROMEOS)
// In normal mode (i.e. non-kiosk mode), the user has to log out to
// re-establish credentials. Let the global error popup handle everything.
// In kiosk mode, interactive sign-in is not supported.
SigninFailed();
return;
#endif
if (g_browser_process->IsShuttingDown()) {
// The login prompt cannot be displayed when the browser process is shutting
// down.
SigninFailed();
return;
}
// Start listening for the primary account being available and display a
// login prompt.
GetMojoIdentityManager()->GetPrimaryAccountWhenAvailable(
base::BindOnce(&IdentityGetAuthTokenFunction::OnPrimaryAccountAvailable,
base::Unretained(this)));
ShowLoginPopup();
}
void IdentityGetAuthTokenFunction::StartMintTokenFlow(
IdentityMintRequestQueue::MintType type) {
mint_token_flow_type_ = type;
// Flows are serialized to prevent excessive traffic to GAIA, and
// to consolidate UI pop-ups.
IdentityAPI* id_api =
extensions::IdentityAPI::GetFactoryInstance()->Get(GetProfile());
if (!should_prompt_for_scopes_) {
// Caller requested no interaction.
if (type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE) {
// GAIA told us to do a consent UI.
CompleteFunctionWithError(identity_constants::kNoGrant);
return;
}
if (!id_api->mint_queue()->empty(
IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE, *token_key_)) {
// Another call is going through a consent UI.
CompleteFunctionWithError(identity_constants::kNoGrant);
return;
}
}
id_api->mint_queue()->RequestStart(type, *token_key_, this);
}
void IdentityGetAuthTokenFunction::CompleteMintTokenFlow() {
IdentityMintRequestQueue::MintType type = mint_token_flow_type_;
extensions::IdentityAPI::GetFactoryInstance()
->Get(GetProfile())
->mint_queue()
->RequestComplete(type, *token_key_, this);
}
void IdentityGetAuthTokenFunction::StartMintToken(
IdentityMintRequestQueue::MintType type) {
TRACE_EVENT_ASYNC_STEP_PAST1("identity",
"IdentityGetAuthTokenFunction",
this,
"StartMintToken",
"type",
type);
const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension());
IdentityAPI* id_api = IdentityAPI::GetFactoryInstance()->Get(GetProfile());
IdentityTokenCacheValue cache_entry = id_api->GetCachedToken(*token_key_);
IdentityTokenCacheValue::CacheValueStatus cache_status =
cache_entry.status();
if (type == IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE) {
switch (cache_status) {
case IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND:
#if defined(OS_CHROMEOS)
// Always force minting token for ChromeOS kiosk app and public session.
if (user_manager::UserManager::Get()->IsLoggedInAsPublicAccount() &&
!IsOriginWhitelistedInPublicSession()) {
CompleteFunctionWithError(identity_constants::kUserNotSignedIn);
return;
}
if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp() ||
user_manager::UserManager::Get()->IsLoggedInAsPublicAccount()) {
gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE;
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()
->browser_policy_connector_chromeos();
if (connector->IsEnterpriseManaged()) {
StartDeviceLoginAccessTokenRequest();
} else {
StartLoginAccessTokenRequest();
}
return;
}
#endif
if (oauth2_info.auto_approve)
// oauth2_info.auto_approve is protected by a whitelist in
// _manifest_features.json hence only selected extensions take
// advantage of forcefully minting the token.
gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE;
else
gaia_mint_token_mode_ = OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE;
StartLoginAccessTokenRequest();
break;
case IdentityTokenCacheValue::CACHE_STATUS_TOKEN:
CompleteMintTokenFlow();
CompleteFunctionWithResult(cache_entry.token());
break;
case IdentityTokenCacheValue::CACHE_STATUS_ADVICE:
CompleteMintTokenFlow();
should_prompt_for_signin_ = false;
issue_advice_ = cache_entry.issue_advice();
StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE);
break;
}
} else {
DCHECK(type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE);
if (cache_status == IdentityTokenCacheValue::CACHE_STATUS_TOKEN) {
CompleteMintTokenFlow();
CompleteFunctionWithResult(cache_entry.token());
} else {
ShowOAuthApprovalDialog(issue_advice_);
}
}
}
void IdentityGetAuthTokenFunction::OnMintTokenSuccess(
const std::string& access_token, int time_to_live) {
TRACE_EVENT_ASYNC_STEP_PAST0("identity",
"IdentityGetAuthTokenFunction",
this,
"OnMintTokenSuccess");
IdentityTokenCacheValue token(access_token,
base::TimeDelta::FromSeconds(time_to_live));
IdentityAPI::GetFactoryInstance()->Get(GetProfile())->SetCachedToken(
*token_key_, token);
CompleteMintTokenFlow();
CompleteFunctionWithResult(access_token);
}
void IdentityGetAuthTokenFunction::OnMintTokenFailure(
const GoogleServiceAuthError& error) {
TRACE_EVENT_ASYNC_STEP_PAST1("identity",
"IdentityGetAuthTokenFunction",
this,
"OnMintTokenFailure",
"error",
error.ToString());
CompleteMintTokenFlow();
switch (error.state()) {
case GoogleServiceAuthError::SERVICE_ERROR:
if (ShouldStartSigninFlow()) {
StartSigninFlow();
return;
}
break;
case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
case GoogleServiceAuthError::ACCOUNT_DELETED:
case GoogleServiceAuthError::ACCOUNT_DISABLED:
// TODO(courage): flush ticket and retry once
if (ShouldStartSigninFlow()) {
StartSigninFlow();
return;
}
break;
default:
// Return error to caller.
break;
}
CompleteFunctionWithError(
std::string(identity_constants::kAuthFailure) + error.ToString());
}
void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess(
const IssueAdviceInfo& issue_advice) {
TRACE_EVENT_ASYNC_STEP_PAST0("identity",
"IdentityGetAuthTokenFunction",
this,
"OnIssueAdviceSuccess");
IdentityAPI::GetFactoryInstance()->Get(GetProfile())->SetCachedToken(
*token_key_, IdentityTokenCacheValue(issue_advice));
CompleteMintTokenFlow();
should_prompt_for_signin_ = false;
// Existing grant was revoked and we used NO_FORCE, so we got info back
// instead. Start a consent UI if we can.
issue_advice_ = issue_advice;
StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE);
}
void IdentityGetAuthTokenFunction::OnPrimaryAccountAvailable(
const AccountInfo& account_info,
const ::identity::AccountState& account_state) {
TRACE_EVENT_ASYNC_STEP_PAST0("identity", "IdentityGetAuthTokenFunction", this,
"OnPrimaryAccountAvailable");
// If there was no account associated this profile before the
// sign-in, we may not have an account_id in the token_key yet.
if (token_key_->account_id.empty()) {
token_key_->account_id = account_info.account_id;
}
StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE);
}
void IdentityGetAuthTokenFunction::SigninFailed() {
TRACE_EVENT_ASYNC_STEP_PAST0("identity",
"IdentityGetAuthTokenFunction",
this,
"SigninFailed");
CompleteFunctionWithError(identity_constants::kUserNotSignedIn);
}
void IdentityGetAuthTokenFunction::OnGaiaFlowFailure(
GaiaWebAuthFlow::Failure failure,
GoogleServiceAuthError service_error,
const std::string& oauth_error) {
CompleteMintTokenFlow();
std::string error;
switch (failure) {
case GaiaWebAuthFlow::WINDOW_CLOSED:
error = identity_constants::kUserRejected;
break;
case GaiaWebAuthFlow::INVALID_REDIRECT:
error = identity_constants::kInvalidRedirect;
break;
case GaiaWebAuthFlow::SERVICE_AUTH_ERROR:
// If this is really an authentication error and not just a transient
// network error, then we show signin UI if appropriate.
if (service_error.state() != GoogleServiceAuthError::CONNECTION_FAILED &&
service_error.state() !=
GoogleServiceAuthError::SERVICE_UNAVAILABLE) {
if (ShouldStartSigninFlow()) {
StartSigninFlow();
return;
}
}
error = std::string(identity_constants::kAuthFailure) +
service_error.ToString();
break;
case GaiaWebAuthFlow::OAUTH_ERROR:
error = MapOAuth2ErrorToDescription(oauth_error);
break;
case GaiaWebAuthFlow::LOAD_FAILED:
error = identity_constants::kPageLoadFailure;
break;
default:
NOTREACHED() << "Unexpected error from gaia web auth flow: " << failure;
error = identity_constants::kInvalidRedirect;
break;
}
CompleteFunctionWithError(error);
}
void IdentityGetAuthTokenFunction::OnGaiaFlowCompleted(
const std::string& access_token,
const std::string& expiration) {
TRACE_EVENT_ASYNC_STEP_PAST0("identity",
"IdentityGetAuthTokenFunction",
this,
"OnGaiaFlowCompleted");
int time_to_live;
if (!expiration.empty() && base::StringToInt(expiration, &time_to_live)) {
IdentityTokenCacheValue token_value(
access_token, base::TimeDelta::FromSeconds(time_to_live));
IdentityAPI::GetFactoryInstance()->Get(GetProfile())->SetCachedToken(
*token_key_, token_value);
}
CompleteMintTokenFlow();
CompleteFunctionWithResult(access_token);
}
void IdentityGetAuthTokenFunction::OnGetAccessTokenComplete(
const base::Optional<std::string>& access_token,
base::Time expiration_time,
const GoogleServiceAuthError& error) {
// By the time we get here we should no longer have an outstanding O2TS
// request (either because we never made a request to O2TS directly or because
// the request was already fulfilled).
DCHECK(!login_token_request_);
if (access_token) {
TRACE_EVENT_ASYNC_STEP_PAST1("identity", "IdentityGetAuthTokenFunction",
this, "OnGetAccessTokenComplete", "account",
token_key_->account_id);
StartGaiaRequest(access_token.value());
} else {
TRACE_EVENT_ASYNC_STEP_PAST1("identity", "IdentityGetAuthTokenFunction",
this, "OnGetAccessTokenComplete", "error",
error.ToString());
OnGaiaFlowFailure(GaiaWebAuthFlow::SERVICE_AUTH_ERROR, error,
std::string());
}
}
#if defined(OS_CHROMEOS)
void IdentityGetAuthTokenFunction::OnGetTokenSuccess(
const OAuth2TokenService::Request* request,
const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
login_token_request_.reset();
OnGetAccessTokenComplete(token_response.access_token,
token_response.expiration_time,
GoogleServiceAuthError::AuthErrorNone());
}
void IdentityGetAuthTokenFunction::OnGetTokenFailure(
const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) {
login_token_request_.reset();
OnGetAccessTokenComplete(base::nullopt, base::Time(), error);
}
#endif
void IdentityGetAuthTokenFunction::OnIdentityAPIShutdown() {
gaia_web_auth_flow_.reset();
login_token_request_.reset();
mojo_identity_manager_.reset();
// Note that if |token_key_| hasn't yet been populated then this instance has
// definitely not made a request with the MintQueue.
if (token_key_) {
extensions::IdentityAPI::GetFactoryInstance()
->Get(GetProfile())
->mint_queue()
->RequestCancel(*token_key_, this);
}
CompleteFunctionWithError(identity_constants::kCanceled);
}
#if defined(OS_CHROMEOS)
void IdentityGetAuthTokenFunction::StartDeviceLoginAccessTokenRequest() {
chromeos::DeviceOAuth2TokenService* service =
chromeos::DeviceOAuth2TokenServiceFactory::Get();
// Since robot account refresh tokens are scoped down to [any-api] only,
// request access token for [any-api] instead of login.
OAuth2TokenService::ScopeSet scopes;
scopes.insert(GaiaConstants::kAnyApiOAuth2Scope);
login_token_request_ =
service->StartRequest(service->GetRobotAccountId(),
scopes,
this);
}
bool IdentityGetAuthTokenFunction::IsOriginWhitelistedInPublicSession() {
DCHECK(extension());
GURL extension_url = extension()->url();
for (size_t i = 0; i < base::size(kPublicSessionAllowedOrigins); i++) {
URLPattern allowed_origin(URLPattern::SCHEME_ALL,
kPublicSessionAllowedOrigins[i]);
if (allowed_origin.MatchesSecurityOrigin(extension_url)) {
return true;
}
}
return false;
}
#endif
void IdentityGetAuthTokenFunction::StartLoginAccessTokenRequest() {
#if defined(OS_CHROMEOS)
if (chrome::IsRunningInForcedAppMode()) {
std::string app_client_id;
std::string app_client_secret;
if (chromeos::UserSessionManager::GetInstance()->
GetAppModeChromeClientOAuthInfo(&app_client_id,
&app_client_secret)) {
ProfileOAuth2TokenService* service =
ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
login_token_request_ =
service->StartRequestForClient(token_key_->account_id,
app_client_id,
app_client_secret,
OAuth2TokenService::ScopeSet(),
this);
return;
}
}
#endif
GetMojoIdentityManager()->GetAccessToken(
token_key_->account_id, ::identity::ScopeSet(), "extensions_identity_api",
base::BindOnce(&IdentityGetAuthTokenFunction::OnGetAccessTokenComplete,
base::Unretained(this)));
}
void IdentityGetAuthTokenFunction::StartGaiaRequest(
const std::string& login_access_token) {
DCHECK(!login_access_token.empty());
mint_token_flow_.reset(CreateMintTokenFlow());
mint_token_flow_->Start(GetProfile()->GetURLLoaderFactory(),
login_access_token);
}
void IdentityGetAuthTokenFunction::ShowLoginPopup() {
LoginUIService* login_ui_service =
LoginUIServiceFactory::GetForProfile(GetProfile());
login_ui_service->ShowLoginPopup();
}
void IdentityGetAuthTokenFunction::ShowOAuthApprovalDialog(
const IssueAdviceInfo& issue_advice) {
const std::string locale = extension_l10n_util::CurrentLocaleOrDefault();
gaia_web_auth_flow_.reset(new GaiaWebAuthFlow(
this, GetProfile(), token_key_.get(), oauth2_client_id_, locale));
gaia_web_auth_flow_->Start();
}
OAuth2MintTokenFlow* IdentityGetAuthTokenFunction::CreateMintTokenFlow() {
std::string signin_scoped_device_id =
GetSigninScopedDeviceIdForProfile(GetProfile());
OAuth2MintTokenFlow* mint_token_flow = new OAuth2MintTokenFlow(
this,
OAuth2MintTokenFlow::Parameters(
extension()->id(),
oauth2_client_id_,
std::vector<std::string>(token_key_->scopes.begin(),
token_key_->scopes.end()),
signin_scoped_device_id,
gaia_mint_token_mode_));
return mint_token_flow;
}
bool IdentityGetAuthTokenFunction::HasLoginToken() const {
ProfileOAuth2TokenService* token_service =
ProfileOAuth2TokenServiceFactory::GetForProfile(GetProfile());
return token_service->RefreshTokenIsAvailable(token_key_->account_id);
}
std::string IdentityGetAuthTokenFunction::MapOAuth2ErrorToDescription(
const std::string& error) {
const char kOAuth2ErrorAccessDenied[] = "access_denied";
const char kOAuth2ErrorInvalidScope[] = "invalid_scope";
if (error == kOAuth2ErrorAccessDenied)
return std::string(identity_constants::kUserRejected);
else if (error == kOAuth2ErrorInvalidScope)
return std::string(identity_constants::kInvalidScopes);
else
return std::string(identity_constants::kAuthFailure) + error;
}
std::string IdentityGetAuthTokenFunction::GetOAuth2ClientId() const {
const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension());
std::string client_id = oauth2_info.client_id;
// Component apps using auto_approve may use Chrome's client ID by
// omitting the field.
if (client_id.empty() && extension()->location() == Manifest::COMPONENT &&
oauth2_info.auto_approve) {
client_id = GaiaUrls::GetInstance()->oauth2_chrome_client_id();
}
return client_id;
}
::identity::mojom::IdentityManager*
IdentityGetAuthTokenFunction::GetMojoIdentityManager() {
if (!mojo_identity_manager_.is_bound()) {
content::BrowserContext::GetConnectorFor(GetProfile())
->BindInterface(::identity::mojom::kServiceName,
mojo::MakeRequest(&mojo_identity_manager_));
}
return mojo_identity_manager_.get();
}
} // namespace extensions