Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Get OAuth2TokenService working on Android.

In order to get OAuth tokens on Android, we have to call out to Java. This CL
makes it possible. It's not the ideal solution but works for now.

BUG= 222271

Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=190531

Review URL: https://codereview.chromium.org/12880014

git-svn-id: http://src.chromium.org/svn/trunk/src/chrome/browser@190600 4ff67af0-8c30-449e-8e8b-ad334ec8d88c
  • Loading branch information...
commit 2800f259105ad9b3201847cac8bd69e395597041 1 parent fdf4d65
dubroy@chromium.org authored
View
22 history/web_history_service.cc
@@ -64,6 +64,7 @@ class RequestImpl : public WebHistoryService::Request,
: profile_(profile),
url_(GURL(url)),
response_code_(0),
+ auth_retry_count_(0),
callback_(callback) {
}
@@ -81,6 +82,19 @@ class RequestImpl : public WebHistoryService::Request,
virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
DCHECK_EQ(source, url_fetcher_.get());
response_code_ = url_fetcher_->GetResponseCode();
+
+ // If the response code indicates that the token might not be valid,
+ // invalidate the token and try again.
+ if (response_code_ == net::HTTP_UNAUTHORIZED && ++auth_retry_count_ <= 1) {
+ OAuth2TokenService::ScopeSet oauth_scopes;
+ oauth_scopes.insert(kHistoryOAuthScope);
+ OAuth2TokenServiceFactory::GetForProfile(profile_)->InvalidateToken(
+ oauth_scopes, access_token_);
+
+ access_token_ = std::string();
+ Start();
+ return;
+ }
url_fetcher_->GetResponseAsString(&response_body_);
url_fetcher_.reset();
callback_.Run(this, true);
@@ -93,6 +107,7 @@ class RequestImpl : public WebHistoryService::Request,
const base::Time& expiration_time) OVERRIDE {
token_request_.reset();
DCHECK(!access_token.empty());
+ access_token_ = access_token;
// Got an access token -- start the actual API request.
url_fetcher_.reset(CreateUrlFetcher(access_token));
@@ -140,6 +155,9 @@ class RequestImpl : public WebHistoryService::Request,
// The OAuth2 access token request.
scoped_ptr<OAuth2TokenService::Request> token_request_;
+ // The current OAuth2 access token.
+ std::string access_token_;
+
// Handles the actual API requests after the OAuth token is acquired.
scoped_ptr<net::URLFetcher> url_fetcher_;
@@ -149,6 +167,10 @@ class RequestImpl : public WebHistoryService::Request,
// Holds the response body received from the server.
std::string response_body_;
+ // The number of times this request has already been retried due to
+ // authorization problems.
+ int auth_retry_count_;
+
// The callback to execute when the query is complete.
CompletionCallback callback_;
};
View
54 signin/oauth2_token_service.cc
@@ -29,6 +29,10 @@
#include "google_apis/gaia/oauth2_access_token_consumer.h"
#include "google_apis/gaia/oauth2_access_token_fetcher.h"
+#if defined(OS_ANDROID)
+#include "chrome/browser/sync/profile_sync_service_android.h"
+#endif
+
namespace {
// Maximum number of retries in fetching an OAuth2 access token.
@@ -362,9 +366,9 @@ void OAuth2TokenService::Shutdown() {
// static
void OAuth2TokenService::InformConsumer(
base::WeakPtr<OAuth2TokenService::RequestImpl> request,
- GoogleServiceAuthError error,
- std::string access_token,
- base::Time expiration_date) {
+ const GoogleServiceAuthError& error,
+ const std::string& access_token,
+ const base::Time& expiration_date) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
if (request)
@@ -378,6 +382,7 @@ scoped_ptr<OAuth2TokenService::Request> OAuth2TokenService::StartRequest(
scoped_ptr<RequestImpl> request(new RequestImpl(consumer));
+#if !defined(OS_ANDROID)
TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
if (!token_service || !token_service->HasOAuthLoginToken()) {
MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
@@ -388,6 +393,7 @@ scoped_ptr<OAuth2TokenService::Request> OAuth2TokenService::StartRequest(
base::Time()));
return request.PassAs<Request>();
}
+#endif
const CacheEntry* cache_entry = GetCacheEntry(scopes);
if (cache_entry && cache_entry->access_token.length()) {
@@ -400,6 +406,17 @@ scoped_ptr<OAuth2TokenService::Request> OAuth2TokenService::StartRequest(
return request.PassAs<Request>();
}
+#if defined(OS_ANDROID)
+ DCHECK_EQ(scopes.size(), 1U);
+ std::vector<std::string> scope_list(scopes.begin(), scopes.end());
+ ProfileSyncServiceAndroid* sync_service =
+ ProfileSyncServiceAndroid::GetProfileSyncServiceAndroid();
+ sync_service->FetchOAuth2Token(
+ scope_list.front(),
+ base::Bind(&OAuth2TokenService::InformConsumer,
+ request->AsWeakPtr()));
+ return request.PassAs<Request>();
+#else
std::string refresh_token = token_service->GetOAuth2LoginRefreshToken();
if (!refresh_token.length()) {
MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
@@ -425,6 +442,22 @@ scoped_ptr<OAuth2TokenService::Request> OAuth2TokenService::StartRequest(
pending_fetchers_[fetch_parameters] = Fetcher::CreateAndStart(
profile_, getter_, refresh_token, scopes, request->AsWeakPtr());
return request.PassAs<Request>();
+#endif // defined(OS_ANDROID)
+}
+
+void OAuth2TokenService::InvalidateToken(const ScopeSet& scopes,
+ const std::string& invalid_token) {
+ RemoveCacheEntry(scopes, invalid_token);
+
+#if defined(OS_ANDROID)
+ DCHECK_EQ(scopes.size(), 1U);
+ std::vector<std::string> scope_list(scopes.begin(), scopes.end());
+ ProfileSyncServiceAndroid* sync_service =
+ ProfileSyncServiceAndroid::GetProfileSyncServiceAndroid();
+ sync_service->InvalidateOAuth2Token(
+ scope_list.front(),
+ invalid_token);
+#endif
}
void OAuth2TokenService::OnFetchComplete(Fetcher* fetcher) {
@@ -482,6 +515,19 @@ const OAuth2TokenService::CacheEntry* OAuth2TokenService::GetCacheEntry(
return &token_iterator->second;
}
+bool OAuth2TokenService::RemoveCacheEntry(
+ const OAuth2TokenService::ScopeSet& scopes,
+ const std::string& token_to_remove) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ TokenCache::iterator token_iterator = token_cache_.find(scopes);
+ if (token_iterator == token_cache_.end() &&
+ token_iterator->second.access_token == token_to_remove) {
+ token_cache_.erase(token_iterator);
+ return true;
+ }
+ return false;
+}
+
void OAuth2TokenService::RegisterCacheEntry(
const std::string& refresh_token,
const OAuth2TokenService::ScopeSet& scopes,
@@ -489,6 +535,7 @@ void OAuth2TokenService::RegisterCacheEntry(
const base::Time& expiration_date) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+#if !defined(OS_ANDROID)
// Only register OAuth2 access tokens for the refresh token held by
// TokenService.
TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
@@ -499,6 +546,7 @@ void OAuth2TokenService::RegisterCacheEntry(
"Received a token with a refresh token not maintained by TokenService.";
return;
}
+#endif
CacheEntry& token = token_cache_[scopes];
token.access_token = access_token;
View
20 signin/oauth2_token_service.h
@@ -99,6 +99,13 @@ class OAuth2TokenService : public content::NotificationObserver,
const ScopeSet& scopes,
OAuth2TokenService::Consumer* consumer);
+ // Mark an OAuth2 access token as invalid. This should be done if the token
+ // was received from this class, but was not accepted by the server (e.g.,
+ // the server returned 401 Unauthorized). The token will be removed from the
+ // cache for the given scopes.
+ void InvalidateToken(const ScopeSet& scopes,
+ const std::string& invalid_token);
+
// content::NotificationObserver
virtual void Observe(int type,
const content::NotificationSource& source,
@@ -118,9 +125,9 @@ class OAuth2TokenService : public content::NotificationObserver,
// Informs the consumer of |request| fetch results.
static void InformConsumer(
base::WeakPtr<OAuth2TokenService::RequestImpl> request,
- GoogleServiceAuthError error,
- std::string access_token,
- base::Time expiration_date);
+ const GoogleServiceAuthError& error,
+ const std::string& access_token,
+ const base::Time& expiration_date);
// Struct that contains the information of an OAuth2 access token.
struct CacheEntry {
@@ -133,6 +140,7 @@ class OAuth2TokenService : public content::NotificationObserver,
// ensure no entry with the same |scopes| is added before the usage of the
// returned entry is done.
const CacheEntry* GetCacheEntry(const ScopeSet& scopes);
+
// Registers a new access token in the cache if |refresh_token| is the one
// currently held by TokenService.
void RegisterCacheEntry(const std::string& refresh_token,
@@ -140,6 +148,12 @@ class OAuth2TokenService : public content::NotificationObserver,
const std::string& access_token,
const base::Time& expiration_date);
+ // Removes an access token for the given set of scopes from the cache.
+ // Returns true if the entry was removed, otherwise false.
+ bool RemoveCacheEntry(const OAuth2TokenService::ScopeSet& scopes,
+ const std::string& token_to_remove);
+
+
// Called when |fetcher| finishes fetching.
void OnFetchComplete(Fetcher* fetcher);
View
58 sync/profile_sync_service_android.cc
@@ -15,6 +15,7 @@
#include "base/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/signin/oauth2_token_service.h"
#include "chrome/browser/signin/signin_manager.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/signin/token_service.h"
@@ -169,6 +170,55 @@ void ProfileSyncServiceAndroid::TokenAvailable(
GaiaConstants::kSyncService, token);
}
+void ProfileSyncServiceAndroid::InvalidateOAuth2Token(
+ const std::string& scope, const std::string& invalid_token) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> j_scope =
+ ConvertUTF8ToJavaString(env, scope);
+ ScopedJavaLocalRef<jstring> j_invalid_token =
+ ConvertUTF8ToJavaString(env, invalid_token);
+ Java_ProfileSyncService_invalidateOAuth2AuthToken(
+ env, weak_java_profile_sync_service_.get(env).obj(),
+ j_scope.obj(),
+ j_invalid_token.obj());
+}
+
+void ProfileSyncServiceAndroid::FetchOAuth2Token(
+ const std::string& scope, const FetchOAuth2TokenCallback& callback) {
+ const std::string& sync_username =
+ SigninManagerFactory::GetForProfile(profile_)->GetAuthenticatedUsername();
+
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> j_sync_username =
+ ConvertUTF8ToJavaString(env, sync_username);
+ ScopedJavaLocalRef<jstring> j_scope =
+ ConvertUTF8ToJavaString(env, scope);
+
+ // Allocate a copy of the callback on the heap, because the callback
+ // needs to be passed through JNI as an int.
+ // It will be passed back to OAuth2TokenFetched(), where it will be freed.
+ scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
+ new FetchOAuth2TokenCallback(callback));
+
+ // Call into Java to get a new token.
+ Java_ProfileSyncService_getOAuth2AuthToken(
+ env, weak_java_profile_sync_service_.get(env).obj(),
+ j_sync_username.obj(),
+ j_scope.obj(),
+ reinterpret_cast<int>(heap_callback.release()));
+}
+
+void ProfileSyncServiceAndroid::OAuth2TokenFetched(
+ JNIEnv* env, jobject, int callback, jstring auth_token, jboolean result) {
+ std::string token = ConvertJavaStringToUTF8(env, auth_token);
+ scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
+ reinterpret_cast<FetchOAuth2TokenCallback*>(callback));
+ GoogleServiceAuthError err(result ?
+ GoogleServiceAuthError::NONE :
+ GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
+ heap_callback->Run(err, token, base::Time());
+}
+
void ProfileSyncServiceAndroid::EnableSync(JNIEnv* env, jobject) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Don't need to do anything if we're already enabled.
@@ -534,6 +584,14 @@ void ProfileSyncServiceAndroid::NudgeSyncer(JNIEnv* env,
ConvertJavaStringToUTF8(env, state));
}
+// static
+ProfileSyncServiceAndroid*
+ ProfileSyncServiceAndroid::GetProfileSyncServiceAndroid() {
+ return reinterpret_cast<ProfileSyncServiceAndroid*>(
+ Java_ProfileSyncService_getProfileSyncServiceAndroid(
+ AttachCurrentThread(), base::android::GetApplicationContext()));
+}
+
static int Init(JNIEnv* env, jobject obj) {
ProfileSyncServiceAndroid* profile_sync_service_android =
new ProfileSyncServiceAndroid(env, obj);
View
34 sync/profile_sync_service_android.h
@@ -9,8 +9,11 @@
#include <map>
#include "base/android/jni_helper.h"
+#include "base/callback.h"
#include "base/compiler_specific.h"
+#include "base/time.h"
#include "chrome/browser/sync/profile_sync_service_observer.h"
+#include "google_apis/gaia/google_service_auth_error.h"
#include "sync/internal_api/public/base/model_type.h"
class Profile;
@@ -23,6 +26,16 @@ class ProfileSyncService;
// This class should only be accessed from the UI thread.
class ProfileSyncServiceAndroid : public ProfileSyncServiceObserver {
public:
+ // Callback from FetchOAuth2Token.
+ // Arguments:
+ // - the error, or NONE if the token fetch was successful.
+ // - the OAuth2 access token.
+ // - the expiry time of the token (may be null, indicating that the expiry
+ // time is unknown.
+ typedef base::Callback<void(
+ const GoogleServiceAuthError&,const std::string&,const base::Time&)>
+ FetchOAuth2TokenCallback;
+
ProfileSyncServiceAndroid(JNIEnv* env, jobject obj);
// This method should be called once right after contructing the object.
@@ -187,9 +200,30 @@ class ProfileSyncServiceAndroid : public ProfileSyncServiceObserver {
// (GoogleServiceAuthError.State).
jint GetAuthError(JNIEnv* env, jobject obj);
+ // Called by native to invalidate an OAuth2 token, e.g. after a 401 response
+ // from the server. This should be done before fetching a new token.
+ void InvalidateOAuth2Token(const std::string& scope,
+ const std::string& invalid_auth_token);
+
+ // Called by native when an OAuth2 token is required. |invalid_auth_token|
+ // is an old auth token to be invalidated (may be empty). |callback| will be
+ // invoked asynchronously after a new token has been fetched.
+ void FetchOAuth2Token(const std::string& scope,
+ const FetchOAuth2TokenCallback& callback);
+
+ // Called from Java when fetching of an OAuth2 token is finished. The
+ // |authToken| param is only valid when |result| is true.
+ void OAuth2TokenFetched(JNIEnv* env,
+ jobject obj,
+ int callback,
+ jstring auth_token,
+ jboolean result);
+
// ProfileSyncServiceObserver:
virtual void OnStateChanged() OVERRIDE;
+ static ProfileSyncServiceAndroid* GetProfileSyncServiceAndroid();
+
// Registers the ProfileSyncServiceAndroid's native methods through JNI.
static bool Register(JNIEnv* env);
Please sign in to comment.
Something went wrong with that request. Please try again.