Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(26)

Unified Diff: chrome/browser/extensions/api/identity/identity_api.cc

Issue 14329014: Identity API: Add token cache and identity.invalidateAuthToken. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rounding third rebase Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/extensions/api/identity/identity_api.cc
diff --git a/chrome/browser/extensions/api/identity/identity_api.cc b/chrome/browser/extensions/api/identity/identity_api.cc
index 479728cb0ee2e8d383f361988b24ff6f129dc5b1..30e1a4202f40a6e7c1ec5f0bbf521dd673d038a2 100644
--- a/chrome/browser/extensions/api/identity/identity_api.cc
+++ b/chrome/browser/extensions/api/identity/identity_api.cc
@@ -48,6 +48,8 @@ const char kUserRejected[] = "The user did not approve access.";
const char kUserNotSignedIn[] = "The user is not signed in.";
const char kInteractionRequired[] = "User interaction required.";
const char kInvalidRedirect[] = "Did not redirect to the right URL.";
+
+const int kCachedIssueAdviceTTLSeconds = 1;
} // namespace identity_constants
namespace {
@@ -58,6 +60,8 @@ static const char kChromiumDomainRedirectUrlPattern[] =
} // namespace
namespace GetAuthToken = api::experimental_identity::GetAuthToken;
+namespace RemoveCachedAuthToken =
+ api::experimental_identity::RemoveCachedAuthToken;
namespace LaunchWebAuthFlow = api::experimental_identity::LaunchWebAuthFlow;
namespace identity = api::experimental_identity;
@@ -125,6 +129,10 @@ void IdentityGetAuthTokenFunction::CompleteFunctionWithError(
}
void IdentityGetAuthTokenFunction::StartSigninFlow() {
+ // All cached tokens are invalid because the user is not signed in.
+ IdentityAPI* id_api =
+ extensions::IdentityAPI::GetFactoryInstance()->GetForProfile(profile_);
+ id_api->EraseAllCachedTokens();
// Display a login prompt. If the subsequent mint fails, don't display the
// login prompt again.
should_prompt_for_signin_ = false;
@@ -143,14 +151,21 @@ void IdentityGetAuthTokenFunction::StartMintTokenFlow(
IdentityAPI* id_api =
extensions::IdentityAPI::GetFactoryInstance()->GetForProfile(profile_);
- // If there is an interactive flow in progress, non-interactive
- // requests should complete immediately since a consent UI is
- // known to be required.
- if (!should_prompt_for_scopes_ && !id_api->mint_queue()->empty(
- IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE,
- GetExtension()->id(), scopes)) {
- CompleteFunctionWithError(identity_constants::kNoGrant);
- return;
+ 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,
+ GetExtension()->id(), scopes)) {
+ // Another call is going through a consent UI.
+ CompleteFunctionWithError(identity_constants::kNoGrant);
+ return;
+ }
}
id_api->mint_queue()->RequestStart(type,
GetExtension()->id(),
@@ -174,24 +189,53 @@ void IdentityGetAuthTokenFunction::CompleteMintTokenFlow() {
void IdentityGetAuthTokenFunction::StartMintToken(
IdentityMintRequestQueue::MintType type) {
- switch (type) {
- case IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE:
- StartGaiaRequest(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE);
- break;
+ const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
+ IdentityAPI* id_api = IdentityAPI::GetFactoryInstance()->GetForProfile(
+ profile());
+ IdentityTokenCacheValue cache_entry = id_api->GetCachedToken(
+ GetExtension()->id(), oauth2_info.scopes);
+ IdentityTokenCacheValue::CacheValueStatus cache_status =
+ cache_entry.status();
+
+ if (type == IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE) {
+ switch (cache_status) {
+ case IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND:
+ StartGaiaRequest(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE);
+ 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);
- case IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE:
+ if (cache_status == IdentityTokenCacheValue::CACHE_STATUS_TOKEN) {
+ CompleteMintTokenFlow();
+ CompleteFunctionWithResult(cache_entry.token());
+ } else {
install_ui_.reset(new ExtensionInstallPrompt(GetAssociatedWebContents()));
ShowOAuthApprovalDialog(issue_advice_);
- break;
-
- default:
- NOTREACHED() << "Unexepected mint type in StartMintToken: " << type;
- break;
- };
+ }
+ }
}
void IdentityGetAuthTokenFunction::OnMintTokenSuccess(
- const std::string& access_token) {
+ const std::string& access_token, int time_to_live) {
+ const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
+ IdentityTokenCacheValue token(access_token,
+ base::TimeDelta::FromSeconds(time_to_live));
+ IdentityAPI::GetFactoryInstance()->GetForProfile(profile())->SetCachedToken(
+ GetExtension()->id(), oauth2_info.scopes, token);
+
CompleteMintTokenFlow();
CompleteFunctionWithResult(access_token);
}
@@ -223,17 +267,17 @@ void IdentityGetAuthTokenFunction::OnMintTokenFailure(
void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess(
const IssueAdviceInfo& issue_advice) {
+ const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
+ IdentityAPI::GetFactoryInstance()->GetForProfile(profile())->SetCachedToken(
+ GetExtension()->id(), oauth2_info.scopes,
+ IdentityTokenCacheValue(issue_advice));
CompleteMintTokenFlow();
should_prompt_for_signin_ = false;
// Existing grant was revoked and we used NO_FORCE, so we got info back
- // instead.
- if (should_prompt_for_scopes_) {
- issue_advice_ = issue_advice;
- StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE);
- } else {
- CompleteFunctionWithError(identity_constants::kNoGrant);
- }
+ // instead. Start a consent UI if we can.
+ issue_advice_ = issue_advice;
+ StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE);
}
void IdentityGetAuthTokenFunction::SigninSuccess(const std::string& token) {
@@ -305,6 +349,23 @@ bool IdentityGetAuthTokenFunction::HasLoginToken() const {
return token_service->HasOAuthLoginToken();
}
+IdentityRemoveCachedAuthTokenFunction::IdentityRemoveCachedAuthTokenFunction() {
+}
+
+IdentityRemoveCachedAuthTokenFunction::
+ ~IdentityRemoveCachedAuthTokenFunction() {
+}
+
+bool IdentityRemoveCachedAuthTokenFunction::RunImpl() {
+ scoped_ptr<RemoveCachedAuthToken::Params> params(
+ RemoveCachedAuthToken::Params::Create(*args_));
+ EXTENSION_FUNCTION_VALIDATE(params.get());
+ const identity::InvalidTokenDetails& details = params->details;
+ IdentityAPI::GetFactoryInstance()->GetForProfile(profile())->EraseCachedToken(
+ GetExtension()->id(), details.token);
+ return true;
+}
+
IdentityLaunchWebAuthFlowFunction::IdentityLaunchWebAuthFlowFunction() {}
IdentityLaunchWebAuthFlowFunction::~IdentityLaunchWebAuthFlowFunction() {}
@@ -397,6 +458,52 @@ void IdentityLaunchWebAuthFlowFunction::OnAuthFlowURLChange(
}
}
+IdentityTokenCacheValue::IdentityTokenCacheValue()
+ : status_(CACHE_STATUS_NOTFOUND) {
+}
+
+IdentityTokenCacheValue::IdentityTokenCacheValue(
+ const IssueAdviceInfo& issue_advice) : status_(CACHE_STATUS_ADVICE),
+ issue_advice_(issue_advice) {
+ expiration_time_ = base::Time::Now() + base::TimeDelta::FromSeconds(
+ identity_constants::kCachedIssueAdviceTTLSeconds);
+}
+
+IdentityTokenCacheValue::IdentityTokenCacheValue(
+ const std::string& token, base::TimeDelta time_to_live)
+ : status_(CACHE_STATUS_TOKEN),
+ token_(token) {
+ base::TimeDelta zero_delta;
+ if (time_to_live < zero_delta)
+ time_to_live = zero_delta;
+
+ expiration_time_ = base::Time::Now() + time_to_live;
+}
+
+IdentityTokenCacheValue::~IdentityTokenCacheValue() {
+}
+
+IdentityTokenCacheValue::CacheValueStatus
+ IdentityTokenCacheValue::status() const {
+ if (is_expired())
+ return IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND;
+ else
+ return status_;
+}
+
+const IssueAdviceInfo& IdentityTokenCacheValue::issue_advice() const {
+ return issue_advice_;
+}
+
+const std::string& IdentityTokenCacheValue::token() const {
+ return token_;
+}
+
+bool IdentityTokenCacheValue::is_expired() const {
+ return status_ == CACHE_STATUS_NOTFOUND ||
+ expiration_time_ < base::Time::Now();
+}
+
IdentityAPI::IdentityAPI(Profile* profile)
: profile_(profile),
signin_manager_(NULL),
@@ -421,6 +528,44 @@ IdentityMintRequestQueue* IdentityAPI::mint_queue() {
return &mint_queue_;
}
+void IdentityAPI::SetCachedToken(const std::string& extension_id,
+ const std::vector<std::string> scopes,
+ const IdentityTokenCacheValue& token_data) {
+ std::set<std::string> scopeset(scopes.begin(), scopes.end());
+ TokenCacheKey key(extension_id, scopeset);
+
+ std::map<TokenCacheKey, IdentityTokenCacheValue>::iterator it =
+ token_cache_.find(key);
+ if (it != token_cache_.end() && it->second.status() <= token_data.status())
+ token_cache_.erase(it);
+
+ token_cache_.insert(std::make_pair(key, token_data));
+}
+
+void IdentityAPI::EraseCachedToken(const std::string& extension_id,
+ const std::string& token) {
+ std::map<TokenCacheKey, IdentityTokenCacheValue>::iterator it;
+ for (it = token_cache_.begin(); it != token_cache_.end(); ++it) {
+ if (it->first.extension_id == extension_id &&
+ it->second.status() == IdentityTokenCacheValue::CACHE_STATUS_TOKEN &&
+ it->second.token() == token) {
+ token_cache_.erase(it);
+ break;
+ }
+ }
+}
+
+void IdentityAPI::EraseAllCachedTokens() {
+ token_cache_.clear();
+}
+
+const IdentityTokenCacheValue& IdentityAPI::GetCachedToken(
+ const std::string& extension_id, const std::vector<std::string> scopes) {
+ std::set<std::string> scopeset(scopes.begin(), scopes.end());
+ TokenCacheKey key(extension_id, scopeset);
+ return token_cache_[key];
+}
+
void IdentityAPI::ReportAuthError(const GoogleServiceAuthError& error) {
if (!signin_manager_)
Initialize();
@@ -466,4 +611,23 @@ void ProfileKeyedAPIFactory<IdentityAPI>::DeclareFactoryDependencies() {
DependsOn(SigninManagerFactory::GetInstance());
}
+IdentityAPI::TokenCacheKey::TokenCacheKey(const std::string& extension_id,
+ const std::set<std::string> scopes)
+ : extension_id(extension_id),
+ scopes(scopes) {
+}
+
+IdentityAPI::TokenCacheKey::~TokenCacheKey() {
+}
+
+bool IdentityAPI::TokenCacheKey::operator<(
+ const IdentityAPI::TokenCacheKey& rhs) const {
+ if (extension_id < rhs.extension_id)
+ return true;
+ else if (rhs.extension_id < extension_id)
+ return false;
+
+ return scopes < rhs.scopes;
+}
+
} // namespace extensions
« no previous file with comments | « chrome/browser/extensions/api/identity/identity_api.h ('k') | chrome/browser/extensions/api/identity/identity_apitest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698