GAIA ID migration for Android.
[chromium-blink-merge.git] / chrome / browser / signin / oauth2_token_service_delegate_android.cc
blobaceb33949d94d19953acaf2d5c4dd166dbe102c6
1 // Copyright 2013 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.
5 #include "chrome/browser/signin/oauth2_token_service_delegate_android.h"
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_array.h"
9 #include "base/android/jni_string.h"
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "chrome/browser/profiles/profile_android.h"
13 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
14 #include "chrome/browser/sync/profile_sync_service_android.h"
15 #include "components/signin/core/browser/account_info.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "google_apis/gaia/gaia_auth_util.h"
18 #include "google_apis/gaia/oauth2_access_token_fetcher.h"
19 #include "jni/OAuth2TokenService_jni.h"
21 using base::android::AttachCurrentThread;
22 using base::android::ConvertJavaStringToUTF8;
23 using base::android::ConvertUTF8ToJavaString;
24 using base::android::ScopedJavaLocalRef;
25 using content::BrowserThread;
27 namespace {
29 // Callback from FetchOAuth2TokenWithUsername().
30 // Arguments:
31 // - the error, or NONE if the token fetch was successful.
32 // - the OAuth2 access token.
33 // - the expiry time of the token (may be null, indicating that the expiry
34 // time is unknown.
35 typedef base::Callback<void(const GoogleServiceAuthError&,
36 const std::string&,
37 const base::Time&)> FetchOAuth2TokenCallback;
39 class AndroidAccessTokenFetcher : public OAuth2AccessTokenFetcher {
40 public:
41 AndroidAccessTokenFetcher(OAuth2AccessTokenConsumer* consumer,
42 const std::string& account_id);
43 ~AndroidAccessTokenFetcher() override;
45 // Overrides from OAuth2AccessTokenFetcher:
46 void Start(const std::string& client_id,
47 const std::string& client_secret,
48 const std::vector<std::string>& scopes) override;
49 void CancelRequest() override;
51 // Handles an access token response.
52 void OnAccessTokenResponse(const GoogleServiceAuthError& error,
53 const std::string& access_token,
54 const base::Time& expiration_time);
56 private:
57 std::string CombineScopes(const std::vector<std::string>& scopes);
59 std::string account_id_;
60 bool request_was_cancelled_;
61 base::WeakPtrFactory<AndroidAccessTokenFetcher> weak_factory_;
63 DISALLOW_COPY_AND_ASSIGN(AndroidAccessTokenFetcher);
66 AndroidAccessTokenFetcher::AndroidAccessTokenFetcher(
67 OAuth2AccessTokenConsumer* consumer,
68 const std::string& account_id)
69 : OAuth2AccessTokenFetcher(consumer),
70 account_id_(account_id),
71 request_was_cancelled_(false),
72 weak_factory_(this) {
75 AndroidAccessTokenFetcher::~AndroidAccessTokenFetcher() {
78 void AndroidAccessTokenFetcher::Start(const std::string& client_id,
79 const std::string& client_secret,
80 const std::vector<std::string>& scopes) {
81 JNIEnv* env = AttachCurrentThread();
82 std::string scope = CombineScopes(scopes);
83 ScopedJavaLocalRef<jstring> j_username =
84 ConvertUTF8ToJavaString(env, account_id_);
85 ScopedJavaLocalRef<jstring> j_scope = ConvertUTF8ToJavaString(env, scope);
86 scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
87 new FetchOAuth2TokenCallback(
88 base::Bind(&AndroidAccessTokenFetcher::OnAccessTokenResponse,
89 weak_factory_.GetWeakPtr())));
91 // Call into Java to get a new token.
92 Java_OAuth2TokenService_getOAuth2AuthToken(
93 env, base::android::GetApplicationContext(), j_username.obj(),
94 j_scope.obj(), reinterpret_cast<intptr_t>(heap_callback.release()));
97 void AndroidAccessTokenFetcher::CancelRequest() {
98 request_was_cancelled_ = true;
101 void AndroidAccessTokenFetcher::OnAccessTokenResponse(
102 const GoogleServiceAuthError& error,
103 const std::string& access_token,
104 const base::Time& expiration_time) {
105 if (request_was_cancelled_) {
106 // Ignore the callback if the request was cancelled.
107 return;
109 if (error.state() == GoogleServiceAuthError::NONE) {
110 FireOnGetTokenSuccess(access_token, expiration_time);
111 } else {
112 FireOnGetTokenFailure(error);
116 // static
117 std::string AndroidAccessTokenFetcher::CombineScopes(
118 const std::vector<std::string>& scopes) {
119 // The Android AccountManager supports multiple scopes separated by a space:
120 // https://code.google.com/p/google-api-java-client/wiki/OAuth2#Android
121 std::string scope;
122 for (std::vector<std::string>::const_iterator it = scopes.begin();
123 it != scopes.end(); ++it) {
124 if (!scope.empty())
125 scope += " ";
126 scope += *it;
128 return scope;
131 } // namespace
133 bool OAuth2TokenServiceDelegateAndroid::is_testing_profile_ = false;
135 OAuth2TokenServiceDelegateAndroid::ErrorInfo::ErrorInfo()
136 : error(GoogleServiceAuthError::NONE) {}
138 OAuth2TokenServiceDelegateAndroid::ErrorInfo::ErrorInfo(
139 const GoogleServiceAuthError& error)
140 : error(error) {}
142 OAuth2TokenServiceDelegateAndroid::OAuth2TokenServiceDelegateAndroid(
143 AccountTrackerService* account_tracker_service)
144 : account_tracker_service_(account_tracker_service),
145 fire_refresh_token_loaded_(RT_LOAD_NOT_START) {
146 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ctor";
147 DCHECK(account_tracker_service_);
148 JNIEnv* env = AttachCurrentThread();
149 base::android::ScopedJavaLocalRef<jobject> local_java_ref =
150 Java_OAuth2TokenService_create(env,
151 base::android::GetApplicationContext(),
152 reinterpret_cast<intptr_t>(this));
153 java_ref_.Reset(env, local_java_ref.obj());
155 if (account_tracker_service_->GetMigrationState() ==
156 AccountTrackerService::MIGRATION_IN_PROGRESS) {
157 std::vector<std::string> accounts = GetAccounts();
158 std::vector<std::string> accounts_id;
159 for (auto account_name : accounts) {
160 AccountInfo account_info =
161 account_tracker_service_->FindAccountInfoByEmail(account_name);
162 DCHECK(!account_info.gaia.empty());
163 accounts_id.push_back(account_info.gaia);
165 ScopedJavaLocalRef<jobjectArray> java_accounts(
166 base::android::ToJavaArrayOfStrings(env, accounts_id));
167 Java_OAuth2TokenService_saveStoredAccounts(
168 env, base::android::GetApplicationContext(), java_accounts.obj());
171 if (!is_testing_profile_) {
172 Java_OAuth2TokenService_validateAccounts(
173 AttachCurrentThread(), java_ref_.obj(),
174 base::android::GetApplicationContext(), JNI_TRUE);
178 OAuth2TokenServiceDelegateAndroid::~OAuth2TokenServiceDelegateAndroid() {
181 // static
182 ScopedJavaLocalRef<jobject> OAuth2TokenServiceDelegateAndroid::GetForProfile(
183 JNIEnv* env,
184 jclass clazz,
185 jobject j_profile_android) {
186 Profile* profile = ProfileAndroid::FromProfileAndroid(j_profile_android);
187 ProfileOAuth2TokenService* service =
188 ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
189 OAuth2TokenServiceDelegate* delegate = service->GetDelegate();
190 return ScopedJavaLocalRef<jobject>(
191 static_cast<OAuth2TokenServiceDelegateAndroid*>(delegate)->java_ref_);
194 static ScopedJavaLocalRef<jobject> GetForProfile(
195 JNIEnv* env,
196 const JavaParamRef<jclass>& clazz,
197 const JavaParamRef<jobject>& j_profile_android) {
198 return OAuth2TokenServiceDelegateAndroid::GetForProfile(env, clazz,
199 j_profile_android);
202 bool OAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable(
203 const std::string& account_id) const {
204 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::RefreshTokenIsAvailable"
205 << " account= " << account_id;
206 std::string account_name = MapAccountIdToAccountName(account_id);
207 JNIEnv* env = AttachCurrentThread();
208 ScopedJavaLocalRef<jstring> j_account_id =
209 ConvertUTF8ToJavaString(env, account_name);
210 jboolean refresh_token_is_available =
211 Java_OAuth2TokenService_hasOAuth2RefreshToken(
212 env, base::android::GetApplicationContext(), j_account_id.obj());
213 return refresh_token_is_available == JNI_TRUE;
216 bool OAuth2TokenServiceDelegateAndroid::RefreshTokenHasError(
217 const std::string& account_id) const {
218 auto it = errors_.find(account_id);
219 // TODO(rogerta): should we distinguish between transient and persistent?
220 return it == errors_.end() ? false : IsError(it->second.error);
223 void OAuth2TokenServiceDelegateAndroid::UpdateAuthError(
224 const std::string& account_id,
225 const GoogleServiceAuthError& error) {
226 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::UpdateAuthError"
227 << " account=" << account_id
228 << " error=" << error.ToString();
229 if (error.state() == GoogleServiceAuthError::NONE) {
230 errors_.erase(account_id);
231 } else {
232 // TODO(rogerta): should we distinguish between transient and persistent?
233 errors_[account_id] = ErrorInfo(error);
237 std::vector<std::string> OAuth2TokenServiceDelegateAndroid::GetAccounts() {
238 std::vector<std::string> accounts;
239 JNIEnv* env = AttachCurrentThread();
240 ScopedJavaLocalRef<jobjectArray> j_accounts =
241 Java_OAuth2TokenService_getAccounts(
242 env, base::android::GetApplicationContext());
243 // TODO(fgorski): We may decide to filter out some of the accounts.
244 base::android::AppendJavaStringArrayToStringVector(env, j_accounts.obj(),
245 &accounts);
246 return accounts;
249 std::vector<std::string>
250 OAuth2TokenServiceDelegateAndroid::GetSystemAccountNames() {
251 std::vector<std::string> account_names;
252 JNIEnv* env = AttachCurrentThread();
253 ScopedJavaLocalRef<jobjectArray> j_accounts =
254 Java_OAuth2TokenService_getSystemAccountNames(
255 env, base::android::GetApplicationContext());
256 base::android::AppendJavaStringArrayToStringVector(env, j_accounts.obj(),
257 &account_names);
258 return account_names;
261 OAuth2AccessTokenFetcher*
262 OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher(
263 const std::string& account_id,
264 net::URLRequestContextGetter* getter,
265 OAuth2AccessTokenConsumer* consumer) {
266 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::CreateAccessTokenFetcher"
267 << " account= " << account_id;
268 ValidateAccountId(account_id);
269 return new AndroidAccessTokenFetcher(consumer,
270 MapAccountIdToAccountName(account_id));
273 void OAuth2TokenServiceDelegateAndroid::InvalidateAccessToken(
274 const std::string& account_id,
275 const std::string& client_id,
276 const OAuth2TokenService::ScopeSet& scopes,
277 const std::string& access_token) {
278 ValidateAccountId(account_id);
279 JNIEnv* env = AttachCurrentThread();
280 ScopedJavaLocalRef<jstring> j_access_token =
281 ConvertUTF8ToJavaString(env, access_token);
282 Java_OAuth2TokenService_invalidateOAuth2AuthToken(
283 env, base::android::GetApplicationContext(), j_access_token.obj());
286 void OAuth2TokenServiceDelegateAndroid::ValidateAccounts(
287 JNIEnv* env,
288 jobject obj,
289 jstring j_current_acc,
290 jboolean j_force_notifications) {
291 std::string signed_in_account;
292 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts from java";
293 if (j_current_acc)
294 signed_in_account = ConvertJavaStringToUTF8(env, j_current_acc);
295 if (!signed_in_account.empty())
296 signed_in_account = gaia::CanonicalizeEmail(signed_in_account);
298 // Clear any auth errors so that client can retry to get access tokens.
299 errors_.clear();
301 ValidateAccounts(MapAccountNameToAccountId(signed_in_account),
302 j_force_notifications != JNI_FALSE);
305 void OAuth2TokenServiceDelegateAndroid::ValidateAccounts(
306 const std::string& signed_in_account,
307 bool force_notifications) {
308 std::vector<std::string> prev_ids = GetAccounts();
309 std::vector<std::string> curr_ids = GetSystemAccountNames();
310 std::vector<std::string> refreshed_ids;
311 std::vector<std::string> revoked_ids;
312 bool account_validation_result = true;
314 for (size_t i = 0; i < curr_ids.size(); ++i)
315 curr_ids[i] = MapAccountNameToAccountId(curr_ids[i]);
317 for (size_t i = 0; i < prev_ids.size(); ++i)
318 ValidateAccountId(prev_ids[i]);
320 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
321 << " sigined_in_account=" << signed_in_account
322 << " prev_ids=" << prev_ids.size() << " curr_ids=" << curr_ids.size()
323 << " force=" << (force_notifications ? "true" : "false");
325 account_validation_result =
326 ValidateAccounts(signed_in_account, prev_ids, curr_ids, refreshed_ids,
327 revoked_ids, force_notifications);
329 ScopedBatchChange batch(this);
331 JNIEnv* env = AttachCurrentThread();
332 ScopedJavaLocalRef<jobjectArray> java_accounts;
333 if (account_validation_result) {
334 java_accounts = base::android::ToJavaArrayOfStrings(env, curr_ids);
335 } else {
336 java_accounts =
337 base::android::ToJavaArrayOfStrings(env, std::vector<std::string>());
339 Java_OAuth2TokenService_saveStoredAccounts(
340 env, base::android::GetApplicationContext(), java_accounts.obj());
342 for (std::vector<std::string>::iterator it = refreshed_ids.begin();
343 it != refreshed_ids.end(); it++) {
344 FireRefreshTokenAvailable(*it);
347 for (std::vector<std::string>::iterator it = revoked_ids.begin();
348 it != revoked_ids.end(); it++) {
349 FireRefreshTokenRevoked(*it);
352 if (fire_refresh_token_loaded_ == RT_WAIT_FOR_VALIDATION) {
353 fire_refresh_token_loaded_ = RT_LOADED;
354 FireRefreshTokensLoaded();
355 } else if (fire_refresh_token_loaded_ == RT_LOAD_NOT_START) {
356 fire_refresh_token_loaded_ = RT_HAS_BEEN_VALIDATED;
359 // Clear accounts no longer exist on device from AccountTrackerService.
360 std::vector<AccountInfo> accounts_info =
361 account_tracker_service_->GetAccounts();
362 for (auto info : accounts_info) {
363 auto it = curr_ids.begin();
364 for (; it != curr_ids.end(); ++it) {
365 if (*it == info.account_id)
366 break;
368 if (it == curr_ids.end())
369 account_tracker_service_->RemoveAccount(info.account_id);
373 bool OAuth2TokenServiceDelegateAndroid::ValidateAccounts(
374 const std::string& signed_in_account,
375 const std::vector<std::string>& prev_account_ids,
376 const std::vector<std::string>& curr_account_ids,
377 std::vector<std::string>& refreshed_ids,
378 std::vector<std::string>& revoked_ids,
379 bool force_notifications) {
380 if (std::find(curr_account_ids.begin(), curr_account_ids.end(),
381 signed_in_account) != curr_account_ids.end()) {
382 // Test to see if an account is removed from the Android AccountManager.
383 // If so, invoke FireRefreshTokenRevoked to notify the reconcilor.
384 for (std::vector<std::string>::const_iterator it = prev_account_ids.begin();
385 it != prev_account_ids.end(); it++) {
386 if (*it == signed_in_account)
387 continue;
389 if (std::find(curr_account_ids.begin(), curr_account_ids.end(), *it) ==
390 curr_account_ids.end()) {
391 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
392 << "revoked=" << *it;
393 revoked_ids.push_back(*it);
397 if (force_notifications ||
398 std::find(prev_account_ids.begin(), prev_account_ids.end(),
399 signed_in_account) == prev_account_ids.end()) {
400 // Always fire the primary signed in account first.
401 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
402 << "refreshed=" << signed_in_account;
403 refreshed_ids.push_back(signed_in_account);
406 for (std::vector<std::string>::const_iterator it = curr_account_ids.begin();
407 it != curr_account_ids.end(); it++) {
408 if (*it != signed_in_account) {
409 if (force_notifications ||
410 std::find(prev_account_ids.begin(), prev_account_ids.end(), *it) ==
411 prev_account_ids.end()) {
412 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
413 << "refreshed=" << *it;
414 refreshed_ids.push_back(*it);
418 return true;
419 } else {
420 // Currently signed in account does not any longer exist among accounts on
421 // system together with all other accounts.
422 if (std::find(prev_account_ids.begin(), prev_account_ids.end(),
423 signed_in_account) != prev_account_ids.end()) {
424 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
425 << "revoked=" << signed_in_account;
426 revoked_ids.push_back(signed_in_account);
428 for (std::vector<std::string>::const_iterator it = prev_account_ids.begin();
429 it != prev_account_ids.end(); it++) {
430 if (*it == signed_in_account)
431 continue;
432 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::ValidateAccounts:"
433 << "revoked=" << *it;
434 revoked_ids.push_back(*it);
436 return false;
440 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailableFromJava(
441 JNIEnv* env,
442 jobject obj,
443 const jstring account_name) {
444 std::string account_id =
445 MapAccountNameToAccountId(ConvertJavaStringToUTF8(env, account_name));
446 // Notify native observers.
447 FireRefreshTokenAvailable(account_id);
450 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailable(
451 const std::string& account_id) {
452 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokenAvailable id="
453 << account_id;
454 JNIEnv* env = AttachCurrentThread();
455 ScopedJavaLocalRef<jstring> account_name =
456 ConvertUTF8ToJavaString(env, MapAccountIdToAccountName(account_id));
457 Java_OAuth2TokenService_notifyRefreshTokenAvailable(env, java_ref_.obj(),
458 account_name.obj());
459 OAuth2TokenServiceDelegate::FireRefreshTokenAvailable(account_id);
462 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevokedFromJava(
463 JNIEnv* env,
464 jobject obj,
465 const jstring account_name) {
466 std::string account_id =
467 MapAccountNameToAccountId(ConvertJavaStringToUTF8(env, account_name));
468 // Notify native observers.
469 FireRefreshTokenRevoked(account_id);
472 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevoked(
473 const std::string& account_id) {
474 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokenRevoked id="
475 << account_id;
476 JNIEnv* env = AttachCurrentThread();
477 ScopedJavaLocalRef<jstring> account_name =
478 ConvertUTF8ToJavaString(env, MapAccountIdToAccountName(account_id));
479 Java_OAuth2TokenService_notifyRefreshTokenRevoked(env, java_ref_.obj(),
480 account_name.obj());
481 OAuth2TokenServiceDelegate::FireRefreshTokenRevoked(account_id);
484 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoadedFromJava(
485 JNIEnv* env,
486 jobject obj) {
487 // Notify native observers.
488 FireRefreshTokensLoaded();
491 void OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoaded() {
492 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::FireRefreshTokensLoaded";
493 JNIEnv* env = AttachCurrentThread();
494 Java_OAuth2TokenService_notifyRefreshTokensLoaded(env, java_ref_.obj());
495 OAuth2TokenServiceDelegate::FireRefreshTokensLoaded();
498 void OAuth2TokenServiceDelegateAndroid::RevokeAllCredentials() {
499 DVLOG(1) << "OAuth2TokenServiceDelegateAndroid::RevokeAllCredentials";
500 ScopedBatchChange batch(this);
501 std::vector<std::string> accounts = GetAccounts();
502 for (std::vector<std::string>::iterator it = accounts.begin();
503 it != accounts.end(); it++) {
504 FireRefreshTokenRevoked(*it);
507 // Clear everything on the Java side as well.
508 std::vector<std::string> empty;
509 JNIEnv* env = AttachCurrentThread();
510 ScopedJavaLocalRef<jobjectArray> java_accounts(
511 base::android::ToJavaArrayOfStrings(env, empty));
512 Java_OAuth2TokenService_saveStoredAccounts(
513 env, base::android::GetApplicationContext(), java_accounts.obj());
516 void OAuth2TokenServiceDelegateAndroid::LoadCredentials(
517 const std::string& primary_account_id) {
518 if (fire_refresh_token_loaded_ == RT_HAS_BEEN_VALIDATED) {
519 fire_refresh_token_loaded_ = RT_LOADED;
520 FireRefreshTokensLoaded();
521 } else if (fire_refresh_token_loaded_ == RT_LOAD_NOT_START) {
522 fire_refresh_token_loaded_ = RT_WAIT_FOR_VALIDATION;
526 std::string OAuth2TokenServiceDelegateAndroid::MapAccountIdToAccountName(
527 const std::string& account_id) const {
528 std::string account_name =
529 account_tracker_service_->GetAccountInfo(account_id).email;
530 DCHECK(!account_name.empty() || account_id.empty());
531 return account_name;
534 std::string OAuth2TokenServiceDelegateAndroid::MapAccountNameToAccountId(
535 const std::string& account_name) const {
536 std::string account_id =
537 account_tracker_service_->FindAccountInfoByEmail(account_name).account_id;
538 DCHECK(!account_id.empty() || account_name.empty());
539 return account_id;
542 // Called from Java when fetching of an OAuth2 token is finished. The
543 // |authToken| param is only valid when |result| is true.
544 void OAuth2TokenFetched(JNIEnv* env,
545 const JavaParamRef<jclass>& clazz,
546 const JavaParamRef<jstring>& authToken,
547 jboolean isTransientError,
548 jlong nativeCallback) {
549 std::string token;
550 if (authToken)
551 token = ConvertJavaStringToUTF8(env, authToken);
552 scoped_ptr<FetchOAuth2TokenCallback> heap_callback(
553 reinterpret_cast<FetchOAuth2TokenCallback*>(nativeCallback));
554 GoogleServiceAuthError
555 err(authToken
556 ? GoogleServiceAuthError::NONE
557 : isTransientError
558 ? GoogleServiceAuthError::CONNECTION_FAILED
559 : GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
560 heap_callback->Run(err, token, base::Time());
563 // static
564 bool OAuth2TokenServiceDelegateAndroid::Register(JNIEnv* env) {
565 return RegisterNativesImpl(env);