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

Side by Side Diff: components/signin/ios/browser/profile_oauth2_token_service_ios.mm

Issue 226643012: Upstream iOS implementation of ProfileOAuth2TokenService (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Nits Created 6 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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.
4
5 #include "components/signin/ios/browser/profile_oauth2_token_service_ios.h"
6
7 #include <Foundation/Foundation.h>
8
9 #include <set>
10 #include <string>
11 #include <vector>
12
13 #include "base/bind.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/strings/sys_string_conversions.h"
16 #include "components/signin/core/browser/signin_client.h"
17 #include "google_apis/gaia/oauth2_access_token_fetcher.h"
18 #include "ios/public/provider/components/signin/browser/profile_oauth2_token_ser vice_ios_provider.h"
19 #include "net/url_request/url_request_status.h"
20
21 namespace {
22
23 const char* kForceInvalidGrantResponsesRefreshToken =
24 "force_invalid_grant_responses_refresh_token";
25
26 // Match the way Chromium handles authentication errors in
27 // google_apis/gaia/oauth2_access_token_fetcher.cc:
28 GoogleServiceAuthError GetGoogleServiceAuthErrorFromNSError(
29 ios::ProfileOAuth2TokenServiceIOSProvider* provider,
30 NSError* error) {
31 if (!error)
32 return GoogleServiceAuthError::AuthErrorNone();
33
34 ios::AuthenticationErrorCategory errorCategory =
35 provider->GetAuthenticationErrorCategory(error);
36 switch (errorCategory) {
37 case ios::kAuthenticationErrorCategoryUnknownErrors:
38 // Treat all unknown error as unexpected service response errors.
39 // This may be too general and may require a finer grain filtering.
40 return GoogleServiceAuthError(
41 GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE);
42 case ios::kAuthenticationErrorCategoryAuthorizationErrors:
43 return GoogleServiceAuthError(
44 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
45 case ios::kAuthenticationErrorCategoryAuthorizationForbiddenErrors:
46 // HTTP_FORBIDDEN (403) is treated as temporary error, because it may be
47 // '403 Rate Limit Exceeded.' (for more details, see
48 // google_apis/gaia/oauth2_access_token_fetcher.cc).
49 return GoogleServiceAuthError(
50 GoogleServiceAuthError::SERVICE_UNAVAILABLE);
51 case ios::kAuthenticationErrorCategoryNetworkServerErrors:
52 // Just set the connection error state to FAILED.
53 return GoogleServiceAuthError::FromConnectionError(
54 net::URLRequestStatus::FAILED);
55 case ios::kAuthenticationErrorCategoryUserCancellationErrors:
56 return GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
57 case ios::kAuthenticationErrorCategoryUnknownIdentityErrors:
58 return GoogleServiceAuthError(GoogleServiceAuthError::USER_NOT_SIGNED_UP);
59 }
60 }
61
62 class SSOAccessTokenFetcher : public OAuth2AccessTokenFetcher {
63 public:
64 SSOAccessTokenFetcher(OAuth2AccessTokenConsumer* consumer,
65 ios::ProfileOAuth2TokenServiceIOSProvider* provider,
66 const std::string account_id);
67 virtual ~SSOAccessTokenFetcher();
68
69 virtual void Start(const std::string& client_id,
70 const std::string& client_secret,
71 const std::vector<std::string>& scopes) OVERRIDE;
72
73 virtual void CancelRequest() OVERRIDE;
74
75 // Handles an access token response.
76 void OnAccessTokenResponse(NSString* token,
77 NSDate* expiration,
78 NSError* error);
79
80 private:
81 base::WeakPtrFactory<SSOAccessTokenFetcher> weak_factory_;
82 ios::ProfileOAuth2TokenServiceIOSProvider* provider_; // weak
83 std::string account_id_;
84 bool request_was_cancelled_;
85
86 DISALLOW_COPY_AND_ASSIGN(SSOAccessTokenFetcher);
87 };
88
89 SSOAccessTokenFetcher::SSOAccessTokenFetcher(
90 OAuth2AccessTokenConsumer* consumer,
91 ios::ProfileOAuth2TokenServiceIOSProvider* provider,
92 const std::string account_id)
93 : OAuth2AccessTokenFetcher(consumer),
94 weak_factory_(this),
95 provider_(provider),
96 account_id_(account_id),
97 request_was_cancelled_(false) {
98 DCHECK(provider_);
99 }
100
101 SSOAccessTokenFetcher::~SSOAccessTokenFetcher() {}
102
103 void SSOAccessTokenFetcher::Start(const std::string& client_id,
104 const std::string& client_secret,
105 const std::vector<std::string>& scopes) {
106 std::set<std::string> scopes_set(scopes.begin(), scopes.end());
107 provider_->GetAccessToken(
108 account_id_, client_id, client_secret, scopes_set,
109 base::Bind(&SSOAccessTokenFetcher::OnAccessTokenResponse,
110 weak_factory_.GetWeakPtr()));
111 }
112
113 void SSOAccessTokenFetcher::CancelRequest() { request_was_cancelled_ = true; }
114
115 void SSOAccessTokenFetcher::OnAccessTokenResponse(NSString* token,
116 NSDate* expiration,
117 NSError* error) {
118 if (request_was_cancelled_) {
119 // Ignore the callback if the request was cancelled.
120 return;
121 }
122 GoogleServiceAuthError auth_error =
123 GetGoogleServiceAuthErrorFromNSError(provider_, error);
124 if (auth_error.state() == GoogleServiceAuthError::NONE) {
125 base::Time expiration_date =
126 base::Time::FromDoubleT([expiration timeIntervalSince1970]);
127 FireOnGetTokenSuccess(base::SysNSStringToUTF8(token), expiration_date);
128 } else {
129 FireOnGetTokenFailure(auth_error);
130 }
131 }
132
133 // Fetcher that returns INVALID_GAIA_CREDENTIALS responses for all requests.
134 class InvalidGrantAccessTokenFetcher : public OAuth2AccessTokenFetcher {
135 public:
136 explicit InvalidGrantAccessTokenFetcher(OAuth2AccessTokenConsumer* consumer);
137 virtual ~InvalidGrantAccessTokenFetcher();
138
139 // OAuth2AccessTokenFetcher
140 virtual void Start(const std::string& client_id,
141 const std::string& client_secret,
142 const std::vector<std::string>& scopes) OVERRIDE;
143 virtual void CancelRequest() OVERRIDE;
144
145 // Fires token failure notifications with INVALID_GAIA_CREDENTIALS error.
146 void FireInvalidGrant();
147
148 private:
149 bool request_was_cancelled_;
150 DISALLOW_COPY_AND_ASSIGN(InvalidGrantAccessTokenFetcher);
151 };
152
153 InvalidGrantAccessTokenFetcher::InvalidGrantAccessTokenFetcher(
154 OAuth2AccessTokenConsumer* consumer)
155 : OAuth2AccessTokenFetcher(consumer),
156 request_was_cancelled_(false) {}
157
158 InvalidGrantAccessTokenFetcher::~InvalidGrantAccessTokenFetcher() {}
159
160 void InvalidGrantAccessTokenFetcher::Start(
161 const std::string& client_id,
162 const std::string& client_secret,
163 const std::vector<std::string>& scopes) {
164 base::MessageLoop::current()->PostTask(
165 FROM_HERE,
166 base::Bind(&InvalidGrantAccessTokenFetcher::FireInvalidGrant,
167 base::Unretained(this)));
168 };
169
170 void InvalidGrantAccessTokenFetcher::FireInvalidGrant() {
171 if (request_was_cancelled_)
172 return;
173 GoogleServiceAuthError auth_error(
174 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
175 FireOnGetTokenFailure(auth_error);
176 }
177
178 void InvalidGrantAccessTokenFetcher::CancelRequest() {
179 request_was_cancelled_ = true;
180 }
181
182 } // namespace
183
184 ProfileOAuth2TokenServiceIOS::AccountInfo::AccountInfo(
185 ProfileOAuth2TokenService* token_service,
186 const std::string& account_id)
187 : token_service_(token_service),
188 account_id_(account_id),
189 last_auth_error_(GoogleServiceAuthError::NONE) {
190 DCHECK(token_service_);
191 DCHECK(!account_id_.empty());
192 token_service_->signin_error_controller()->AddProvider(this);
193 }
194
195 ProfileOAuth2TokenServiceIOS::AccountInfo::~AccountInfo() {
196 token_service_->signin_error_controller()->RemoveProvider(this);
197 }
198
199 void ProfileOAuth2TokenServiceIOS::AccountInfo::SetLastAuthError(
200 const GoogleServiceAuthError& error) {
201 if (error.state() != last_auth_error_.state()) {
202 last_auth_error_ = error;
203 token_service_->signin_error_controller()->AuthStatusChanged();
204 }
205 }
206
207 std::string ProfileOAuth2TokenServiceIOS::AccountInfo::GetAccountId() const {
208 return account_id_;
209 }
210
211 GoogleServiceAuthError
212 ProfileOAuth2TokenServiceIOS::AccountInfo::GetAuthStatus() const {
213 return last_auth_error_;
214 }
215
216 ProfileOAuth2TokenServiceIOS::ProfileOAuth2TokenServiceIOS()
217 : MutableProfileOAuth2TokenService(),
218 use_legacy_token_service_(false) {
219 DCHECK(thread_checker_.CalledOnValidThread());
220 }
221
222 ProfileOAuth2TokenServiceIOS::~ProfileOAuth2TokenServiceIOS() {
223 DCHECK(thread_checker_.CalledOnValidThread());
224 }
225
226 void ProfileOAuth2TokenServiceIOS::Initialize(SigninClient* client) {
227 DCHECK(thread_checker_.CalledOnValidThread());
228 MutableProfileOAuth2TokenService::Initialize(client);
229 }
230
231 void ProfileOAuth2TokenServiceIOS::Shutdown() {
232 DCHECK(thread_checker_.CalledOnValidThread());
233 CancelAllRequests();
234 accounts_.clear();
235 MutableProfileOAuth2TokenService::Shutdown();
236 }
237
238 ios::ProfileOAuth2TokenServiceIOSProvider*
239 ProfileOAuth2TokenServiceIOS::GetProvider() {
240 ios::ProfileOAuth2TokenServiceIOSProvider* provider =
241 client()->GetIOSProvider();
242 DCHECK(provider);
243 return provider;
244 }
245
246 void ProfileOAuth2TokenServiceIOS::LoadCredentials(
247 const std::string& primary_account_id) {
248 DCHECK(thread_checker_.CalledOnValidThread());
249
250 // LoadCredentials() is called iff the user is signed in to Chrome, so the
251 // primary account id must not be empty.
252 DCHECK(!primary_account_id.empty());
253
254 use_legacy_token_service_ = !GetProvider()->IsUsingSharedAuthentication();
255 if (use_legacy_token_service_) {
256 MutableProfileOAuth2TokenService::LoadCredentials(primary_account_id);
257 return;
258 }
259
260 GetProvider()->InitializeSharedAuthentication();
261 ReloadCredentials();
262 FireRefreshTokensLoaded();
263 }
264
265 void ProfileOAuth2TokenServiceIOS::ReloadCredentials() {
266 DCHECK(thread_checker_.CalledOnValidThread());
267 if (use_legacy_token_service_) {
268 NOTREACHED();
269 return;
270 }
271
272 // Remove all old accounts that do not appear in |new_accounts| and then
273 // load |new_accounts|.
274 std::vector<std::string> new_accounts(GetProvider()->GetAllAccountIds());
275 std::vector<std::string> old_accounts(GetAccounts());
276 for (auto i = old_accounts.begin(); i != old_accounts.end(); ++i) {
277 if (std::find(new_accounts.begin(), new_accounts.end(), *i) ==
278 new_accounts.end()) {
279 RemoveAccount(*i);
280 }
281 }
282
283 // Load all new_accounts.
284 for (auto i = new_accounts.begin(); i != new_accounts.end(); ++i) {
285 AddOrUpdateAccount(*i);
286 }
287 }
288
289 void ProfileOAuth2TokenServiceIOS::UpdateCredentials(
290 const std::string& account_id,
291 const std::string& refresh_token) {
292 DCHECK(thread_checker_.CalledOnValidThread());
293 if (use_legacy_token_service_) {
294 MutableProfileOAuth2TokenService::UpdateCredentials(account_id,
295 refresh_token);
296 return;
297 }
298 NOTREACHED() << "Unexpected call to UpdateCredentials when using shared "
299 "authentication.";
300 }
301
302 void ProfileOAuth2TokenServiceIOS::RevokeAllCredentials() {
303 DCHECK(thread_checker_.CalledOnValidThread());
304 if (use_legacy_token_service_) {
305 MutableProfileOAuth2TokenService::RevokeAllCredentials();
306 return;
307 }
308
309 CancelAllRequests();
310 ClearCache();
311 AccountInfoMap toRemove = accounts_;
312 for (AccountInfoMap::iterator i = toRemove.begin(); i != toRemove.end(); ++i)
313 RemoveAccount(i->first);
314
315 DCHECK_EQ(0u, accounts_.size());
316 }
317
318 OAuth2AccessTokenFetcher*
319 ProfileOAuth2TokenServiceIOS::CreateAccessTokenFetcher(
320 const std::string& account_id,
321 net::URLRequestContextGetter* getter,
322 OAuth2AccessTokenConsumer* consumer) {
323 if (use_legacy_token_service_) {
324 std::string refresh_token = GetRefreshToken(account_id);
325 DCHECK(!refresh_token.empty());
326 if (refresh_token == kForceInvalidGrantResponsesRefreshToken) {
327 return new InvalidGrantAccessTokenFetcher(consumer);
328 } else {
329 return MutableProfileOAuth2TokenService::CreateAccessTokenFetcher(
330 account_id, getter, consumer);
331 }
332 }
333
334 return new SSOAccessTokenFetcher(consumer, GetProvider(), account_id);
335 }
336
337 void ProfileOAuth2TokenServiceIOS::ForceInvalidGrantResponses() {
338 if (!use_legacy_token_service_) {
339 NOTREACHED();
340 return;
341 }
342 std::vector<std::string> accounts =
343 MutableProfileOAuth2TokenService::GetAccounts();
344 if (accounts.empty()) {
345 NOTREACHED();
346 return;
347 }
348
349 std::string first_account_id = *accounts.begin();
350 if (RefreshTokenIsAvailable(first_account_id) &&
351 GetRefreshToken(first_account_id) !=
352 kForceInvalidGrantResponsesRefreshToken) {
353 MutableProfileOAuth2TokenService::RevokeAllCredentials();
354 }
355
356 for (auto i = accounts.begin(); i != accounts.end(); ++i) {
357 std::string account_id = *i;
358 MutableProfileOAuth2TokenService::UpdateCredentials(
359 account_id,
360 kForceInvalidGrantResponsesRefreshToken);
361 }
362 }
363
364 void ProfileOAuth2TokenServiceIOS::InvalidateOAuth2Token(
365 const std::string& account_id,
366 const std::string& client_id,
367 const ScopeSet& scopes,
368 const std::string& access_token) {
369 DCHECK(thread_checker_.CalledOnValidThread());
370
371 // Call |MutableProfileOAuth2TokenService::InvalidateOAuth2Token| to clear the
372 // cached access token.
373 MutableProfileOAuth2TokenService::InvalidateOAuth2Token(account_id,
374 client_id,
375 scopes,
376 access_token);
377
378 // There is no need to inform the authentication library that the access
379 // token is invalid as it never caches the token.
380 }
381
382 std::vector<std::string> ProfileOAuth2TokenServiceIOS::GetAccounts() {
383 DCHECK(thread_checker_.CalledOnValidThread());
384 if (use_legacy_token_service_) {
385 return MutableProfileOAuth2TokenService::GetAccounts();
386 }
387
388 std::vector<std::string> account_ids;
389 for (auto i = accounts_.begin(); i != accounts_.end(); ++i)
390 account_ids.push_back(i->first);
391 return account_ids;
392 }
393
394 bool ProfileOAuth2TokenServiceIOS::RefreshTokenIsAvailable(
395 const std::string& account_id) const {
396 DCHECK(thread_checker_.CalledOnValidThread());
397
398 if (use_legacy_token_service_) {
399 return MutableProfileOAuth2TokenService::RefreshTokenIsAvailable(
400 account_id);
401 }
402
403 return accounts_.count(account_id) > 0;
404 }
405
406 std::string ProfileOAuth2TokenServiceIOS::GetRefreshToken(
407 const std::string& account_id) const {
408 DCHECK(thread_checker_.CalledOnValidThread());
409 if (use_legacy_token_service_)
410 return MutableProfileOAuth2TokenService::GetRefreshToken(account_id);
411
412 // On iOS, the refresh token does not exist as ProfileOAuth2TokenServiceIOS
413 // fetches the access token from the iOS authentication library.
414 NOTREACHED();
415 return std::string();
416 }
417
418 std::string
419 ProfileOAuth2TokenServiceIOS::GetRefreshTokenWhenNotUsingSharedAuthentication(
420 const std::string& account_id) {
421 DCHECK(use_legacy_token_service_);
422 return GetRefreshToken(account_id);
423 }
424
425 void ProfileOAuth2TokenServiceIOS::UpdateAuthError(
426 const std::string& account_id,
427 const GoogleServiceAuthError& error) {
428 DCHECK(thread_checker_.CalledOnValidThread());
429
430 if (use_legacy_token_service_) {
431 MutableProfileOAuth2TokenService::UpdateAuthError(account_id, error);
432 return;
433 }
434
435 // Do not report connection errors as these are not actually auth errors.
436 // We also want to avoid masking a "real" auth error just because we
437 // subsequently get a transient network error.
438 if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED ||
439 error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE) {
440 return;
441 }
442
443 if (accounts_.count(account_id) == 0) {
444 NOTREACHED();
445 return;
446 }
447 accounts_[account_id]->SetLastAuthError(error);
448 }
449
450 // Clear the authentication error state and notify all observers that a new
451 // refresh token is available so that they request new access tokens.
452 void ProfileOAuth2TokenServiceIOS::AddOrUpdateAccount(
453 const std::string& account_id) {
454 DCHECK(thread_checker_.CalledOnValidThread());
455 DCHECK(!account_id.empty());
Roger Tawa OOO till Jul 10th 2014/04/10 15:04:08 Add dcheck? DCHECK(!use_legacy_token_service_
msarda 2014/04/10 15:13:36 Done. I'll fix it in a future CL.
456
457 bool account_present = accounts_.count(account_id) > 0;
458 if (account_present && accounts_[account_id]->GetAuthStatus().state() ==
459 GoogleServiceAuthError::NONE) {
460 // No need to update the account if it is already a known account and if
461 // there is no auth error.
462 return;
463 }
464
465 if (account_present) {
466 CancelRequestsForAccount(account_id);
467 ClearCacheForAccount(account_id);
468 } else {
469 accounts_[account_id].reset(new AccountInfo(this, account_id));
470 }
471 UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
472 FireRefreshTokenAvailable(account_id);
473 }
474
475 void ProfileOAuth2TokenServiceIOS::RemoveAccount(
476 const std::string& account_id) {
477 DCHECK(thread_checker_.CalledOnValidThread());
478 DCHECK(!account_id.empty());
Roger Tawa OOO till Jul 10th 2014/04/10 15:04:08 Add dcheck? DCHECK(!use_legacy_token_service_
msarda 2014/04/10 15:13:36 Done. I'll fix it in a future CL.
479
480 if (accounts_.count(account_id) > 0) {
481 CancelRequestsForAccount(account_id);
482 ClearCacheForAccount(account_id);
483 accounts_.erase(account_id);
484 FireRefreshTokenRevoked(account_id);
485 }
486 }
487
488 void ProfileOAuth2TokenServiceIOS::StartUsingSharedAuthentication() {
489 if (!use_legacy_token_service_)
490 return;
491 MutableProfileOAuth2TokenService::RevokeAllCredentials();
492 use_legacy_token_service_ = false;
493 }
494
495 void ProfileOAuth2TokenServiceIOS::SetUseLegacyTokenServiceForTesting(
496 bool use_legacy_token_service) {
497 use_legacy_token_service_ = use_legacy_token_service;
498 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698