blob: b2b057c6c95a9dd50adf29eb0ee106826f3a3a3e [file] [log] [blame]
// Copyright 2014 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 "extensions/shell/browser/api/identity/identity_api.h"
#include <set>
#include <string>
#include "base/guid.h"
#include "base/memory/ptr_util.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
#include "extensions/common/manifest_handlers/oauth2_manifest_handler.h"
#include "extensions/shell/browser/shell_oauth2_token_service.h"
#include "extensions/shell/common/api/identity.h"
#include "google_apis/gaia/gaia_auth_util.h"
namespace extensions {
namespace shell {
namespace {
const char kIdentityApiId[] = "identity_api";
const char kErrorNoUserAccount[] = "No user account.";
const char kErrorNoRefreshToken[] = "No refresh token.";
const char kErrorNoScopesInManifest[] = "No scopes in manifest.";
const char kErrorUserPermissionRequired[] =
"User permission required but not available in app_shell";
} // namespace
IdentityAPI::IdentityAPI(content::BrowserContext* context)
: device_id_(base::GenerateGUID()) {
}
IdentityAPI::~IdentityAPI() {
}
// static
IdentityAPI* IdentityAPI::Get(content::BrowserContext* context) {
return BrowserContextKeyedAPIFactory<IdentityAPI>::Get(context);
}
// static
BrowserContextKeyedAPIFactory<IdentityAPI>* IdentityAPI::GetFactoryInstance() {
static base::LazyInstance<BrowserContextKeyedAPIFactory<IdentityAPI>>
factory = LAZY_INSTANCE_INITIALIZER;
return factory.Pointer();
}
///////////////////////////////////////////////////////////////////////////////
IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction()
: OAuth2TokenService::Consumer(kIdentityApiId) {
}
IdentityGetAuthTokenFunction::~IdentityGetAuthTokenFunction() {
}
void IdentityGetAuthTokenFunction::SetMintTokenFlowForTesting(
OAuth2MintTokenFlow* flow) {
mint_token_flow_.reset(flow);
}
ExtensionFunction::ResponseAction IdentityGetAuthTokenFunction::Run() {
std::unique_ptr<api::identity::GetAuthToken::Params> params(
api::identity::GetAuthToken::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
ShellOAuth2TokenService* service = ShellOAuth2TokenService::GetInstance();
std::string account_id = service->AccountId();
if (account_id.empty())
return RespondNow(Error(kErrorNoUserAccount));
if (!service->RefreshTokenIsAvailable(account_id))
return RespondNow(Error(kErrorNoRefreshToken));
// Verify that we have scopes.
const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension());
if (oauth2_info.scopes.empty())
return RespondNow(Error(kErrorNoScopesInManifest));
// Balanced in OnGetTokenFailure() and in the OAuth2MintTokenFlow callbacks.
AddRef();
// First, fetch a logged-in-user access token for the Chrome project client ID
// and client secret. This token is used later to get a second access token
// that will be returned to the app.
std::set<std::string> no_scopes;
access_token_request_ =
service->StartRequest(service->AccountId(), no_scopes, this);
return RespondLater();
}
void IdentityGetAuthTokenFunction::OnGetTokenSuccess(
const OAuth2TokenService::Request* request,
const std::string& access_token,
const base::Time& expiration_time) {
// Tests may override the mint token flow.
if (!mint_token_flow_) {
const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension());
DCHECK(!oauth2_info.scopes.empty());
mint_token_flow_.reset(new OAuth2MintTokenFlow(
this,
OAuth2MintTokenFlow::Parameters(
extension()->id(),
oauth2_info.client_id,
oauth2_info.scopes,
IdentityAPI::Get(browser_context())->device_id(),
OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE)));
}
// Use the logging-in-user access token to mint an access token for this app.
mint_token_flow_->Start(
content::BrowserContext::GetDefaultStoragePartition(browser_context())->
GetURLRequestContext(),
access_token);
}
void IdentityGetAuthTokenFunction::OnGetTokenFailure(
const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) {
Respond(Error(error.ToString()));
Release(); // Balanced in Run().
}
void IdentityGetAuthTokenFunction::OnMintTokenSuccess(
const std::string& access_token,
int time_to_live) {
Respond(OneArgument(base::MakeUnique<base::StringValue>(access_token)));
Release(); // Balanced in Run().
}
void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess(
const IssueAdviceInfo& issue_advice) {
Respond(Error(kErrorUserPermissionRequired));
Release(); // Balanced in Run().
}
void IdentityGetAuthTokenFunction::OnMintTokenFailure(
const GoogleServiceAuthError& error) {
Respond(Error(error.ToString()));
Release(); // Balanced in Run().
}
///////////////////////////////////////////////////////////////////////////////
IdentityRemoveCachedAuthTokenFunction::IdentityRemoveCachedAuthTokenFunction() {
}
IdentityRemoveCachedAuthTokenFunction::
~IdentityRemoveCachedAuthTokenFunction() {
}
ExtensionFunction::ResponseAction IdentityRemoveCachedAuthTokenFunction::Run() {
std::unique_ptr<api::identity::RemoveCachedAuthToken::Params> params(
api::identity::RemoveCachedAuthToken::Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params.get());
// This stub identity API does not maintain a token cache, so there is nothing
// to remove.
return RespondNow(NoArguments());
}
} // namespace shell
} // namespace extensions