Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / components / signin / ios / browser / profile_oauth2_token_service_ios.mm
1 // Copyright 2014 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_service_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 std::string ProfileOAuth2TokenServiceIOS::AccountInfo::GetUsername() const {
212   // TODO(rogerta): when |account_id| becomes the obfuscated gaia id, this
213   // will need to be changed.
214   return account_id_;
215 }
216
217 GoogleServiceAuthError
218 ProfileOAuth2TokenServiceIOS::AccountInfo::GetAuthStatus() const {
219   return last_auth_error_;
220 }
221
222 ProfileOAuth2TokenServiceIOS::ProfileOAuth2TokenServiceIOS()
223     : MutableProfileOAuth2TokenService(),
224       use_legacy_token_service_(false) {
225   DCHECK(thread_checker_.CalledOnValidThread());
226 }
227
228 ProfileOAuth2TokenServiceIOS::~ProfileOAuth2TokenServiceIOS() {
229   DCHECK(thread_checker_.CalledOnValidThread());
230 }
231
232 void ProfileOAuth2TokenServiceIOS::Initialize(SigninClient* client) {
233   DCHECK(thread_checker_.CalledOnValidThread());
234   MutableProfileOAuth2TokenService::Initialize(client);
235 }
236
237 void ProfileOAuth2TokenServiceIOS::Shutdown() {
238   DCHECK(thread_checker_.CalledOnValidThread());
239   CancelAllRequests();
240   accounts_.clear();
241   MutableProfileOAuth2TokenService::Shutdown();
242 }
243
244 ios::ProfileOAuth2TokenServiceIOSProvider*
245 ProfileOAuth2TokenServiceIOS::GetProvider() {
246   ios::ProfileOAuth2TokenServiceIOSProvider* provider =
247       client()->GetIOSProvider();
248   DCHECK(provider);
249   return provider;
250 }
251
252 void ProfileOAuth2TokenServiceIOS::LoadCredentials(
253     const std::string& primary_account_id) {
254   DCHECK(thread_checker_.CalledOnValidThread());
255
256   // LoadCredentials() is called iff the user is signed in to Chrome, so the
257   // primary account id must not be empty.
258   DCHECK(!primary_account_id.empty());
259
260   use_legacy_token_service_ = !GetProvider()->IsUsingSharedAuthentication();
261   if (use_legacy_token_service_) {
262     MutableProfileOAuth2TokenService::LoadCredentials(primary_account_id);
263     return;
264   }
265
266   GetProvider()->InitializeSharedAuthentication();
267   ReloadCredentials();
268   FireRefreshTokensLoaded();
269 }
270
271 void ProfileOAuth2TokenServiceIOS::ReloadCredentials() {
272   DCHECK(thread_checker_.CalledOnValidThread());
273   if (use_legacy_token_service_) {
274     NOTREACHED();
275     return;
276   }
277
278   ScopedBacthChange batch(this);
279
280   // Remove all old accounts that do not appear in |new_accounts| and then
281   // load |new_accounts|.
282   std::vector<std::string> new_accounts(GetProvider()->GetAllAccountIds());
283   std::vector<std::string> old_accounts(GetAccounts());
284   for (auto i = old_accounts.begin(); i != old_accounts.end(); ++i) {
285     if (std::find(new_accounts.begin(), new_accounts.end(), *i) ==
286         new_accounts.end()) {
287       RemoveAccount(*i);
288     }
289   }
290
291   // Load all new_accounts.
292   for (auto i = new_accounts.begin(); i != new_accounts.end(); ++i) {
293     AddOrUpdateAccount(*i);
294   }
295 }
296
297 void ProfileOAuth2TokenServiceIOS::UpdateCredentials(
298     const std::string& account_id,
299     const std::string& refresh_token) {
300   DCHECK(thread_checker_.CalledOnValidThread());
301   if (use_legacy_token_service_) {
302     MutableProfileOAuth2TokenService::UpdateCredentials(account_id,
303                                                         refresh_token);
304     return;
305   }
306   NOTREACHED() << "Unexpected call to UpdateCredentials when using shared "
307                   "authentication.";
308 }
309
310 void ProfileOAuth2TokenServiceIOS::RevokeAllCredentials() {
311   DCHECK(thread_checker_.CalledOnValidThread());
312   if (use_legacy_token_service_) {
313     MutableProfileOAuth2TokenService::RevokeAllCredentials();
314     return;
315   }
316
317   ScopedBacthChange batch(this);
318   CancelAllRequests();
319   ClearCache();
320   AccountInfoMap toRemove = accounts_;
321   for (AccountInfoMap::iterator i = toRemove.begin(); i != toRemove.end(); ++i)
322     RemoveAccount(i->first);
323
324   DCHECK_EQ(0u, accounts_.size());
325 }
326
327 OAuth2AccessTokenFetcher*
328 ProfileOAuth2TokenServiceIOS::CreateAccessTokenFetcher(
329     const std::string& account_id,
330     net::URLRequestContextGetter* getter,
331     OAuth2AccessTokenConsumer* consumer) {
332   if (use_legacy_token_service_) {
333     std::string refresh_token = GetRefreshToken(account_id);
334     DCHECK(!refresh_token.empty());
335     if (refresh_token == kForceInvalidGrantResponsesRefreshToken) {
336       return new InvalidGrantAccessTokenFetcher(consumer);
337     } else {
338       return MutableProfileOAuth2TokenService::CreateAccessTokenFetcher(
339           account_id, getter, consumer);
340     }
341   }
342
343   return new SSOAccessTokenFetcher(consumer, GetProvider(), account_id);
344 }
345
346 void ProfileOAuth2TokenServiceIOS::ForceInvalidGrantResponses() {
347   if (!use_legacy_token_service_) {
348     NOTREACHED();
349     return;
350   }
351   std::vector<std::string> accounts =
352       MutableProfileOAuth2TokenService::GetAccounts();
353   if (accounts.empty()) {
354     NOTREACHED();
355     return;
356   }
357
358   std::string first_account_id = *accounts.begin();
359   if (RefreshTokenIsAvailable(first_account_id) &&
360       GetRefreshToken(first_account_id) !=
361           kForceInvalidGrantResponsesRefreshToken) {
362     MutableProfileOAuth2TokenService::RevokeAllCredentials();
363   }
364
365   ScopedBacthChange batch(this);
366   for (auto i = accounts.begin(); i != accounts.end(); ++i) {
367     std::string account_id = *i;
368     MutableProfileOAuth2TokenService::UpdateCredentials(
369         account_id,
370         kForceInvalidGrantResponsesRefreshToken);
371   }
372 }
373
374 void ProfileOAuth2TokenServiceIOS::InvalidateOAuth2Token(
375     const std::string& account_id,
376     const std::string& client_id,
377     const ScopeSet& scopes,
378     const std::string& access_token) {
379   DCHECK(thread_checker_.CalledOnValidThread());
380
381   // Call |MutableProfileOAuth2TokenService::InvalidateOAuth2Token| to clear the
382   // cached access token.
383   MutableProfileOAuth2TokenService::InvalidateOAuth2Token(account_id,
384                                                           client_id,
385                                                           scopes,
386                                                           access_token);
387
388   // There is no need to inform the authentication library that the access
389   // token is invalid as it never caches the token.
390 }
391
392 std::vector<std::string> ProfileOAuth2TokenServiceIOS::GetAccounts() {
393   DCHECK(thread_checker_.CalledOnValidThread());
394   if (use_legacy_token_service_) {
395     return MutableProfileOAuth2TokenService::GetAccounts();
396   }
397
398   std::vector<std::string> account_ids;
399   for (auto i = accounts_.begin(); i != accounts_.end(); ++i)
400     account_ids.push_back(i->first);
401   return account_ids;
402 }
403
404 bool ProfileOAuth2TokenServiceIOS::RefreshTokenIsAvailable(
405     const std::string& account_id) const {
406   DCHECK(thread_checker_.CalledOnValidThread());
407
408   if (use_legacy_token_service_) {
409     return MutableProfileOAuth2TokenService::RefreshTokenIsAvailable(
410         account_id);
411   }
412
413   return accounts_.count(account_id) > 0;
414 }
415
416 std::string ProfileOAuth2TokenServiceIOS::GetRefreshToken(
417     const std::string& account_id) const {
418   DCHECK(thread_checker_.CalledOnValidThread());
419   if (use_legacy_token_service_)
420     return MutableProfileOAuth2TokenService::GetRefreshToken(account_id);
421
422   // On iOS, the refresh token does not exist as ProfileOAuth2TokenServiceIOS
423   // fetches the access token from the iOS authentication library.
424   NOTREACHED();
425   return std::string();
426 }
427
428 std::string
429 ProfileOAuth2TokenServiceIOS::GetRefreshTokenWhenNotUsingSharedAuthentication(
430     const std::string& account_id) {
431   DCHECK(use_legacy_token_service_);
432   return GetRefreshToken(account_id);
433 }
434
435 void ProfileOAuth2TokenServiceIOS::UpdateAuthError(
436     const std::string& account_id,
437     const GoogleServiceAuthError& error) {
438   DCHECK(thread_checker_.CalledOnValidThread());
439
440   if (use_legacy_token_service_) {
441     MutableProfileOAuth2TokenService::UpdateAuthError(account_id, error);
442     return;
443   }
444
445   // Do not report connection errors as these are not actually auth errors.
446   // We also want to avoid masking a "real" auth error just because we
447   // subsequently get a transient network error.
448   if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED ||
449       error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE) {
450     return;
451   }
452
453   if (accounts_.count(account_id) == 0) {
454     NOTREACHED();
455     return;
456   }
457   accounts_[account_id]->SetLastAuthError(error);
458 }
459
460 // Clear the authentication error state and notify all observers that a new
461 // refresh token is available so that they request new access tokens.
462 void ProfileOAuth2TokenServiceIOS::AddOrUpdateAccount(
463     const std::string& account_id) {
464   DCHECK(thread_checker_.CalledOnValidThread());
465   DCHECK(!account_id.empty());
466   DCHECK(!use_legacy_token_service_);
467
468   bool account_present = accounts_.count(account_id) > 0;
469   if (account_present && accounts_[account_id]->GetAuthStatus().state() ==
470                              GoogleServiceAuthError::NONE) {
471     // No need to update the account if it is already a known account and if
472     // there is no auth error.
473     return;
474   }
475
476   if (account_present) {
477     CancelRequestsForAccount(account_id);
478     ClearCacheForAccount(account_id);
479   } else {
480     accounts_[account_id].reset(new AccountInfo(this, account_id));
481   }
482   UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
483   FireRefreshTokenAvailable(account_id);
484 }
485
486 void ProfileOAuth2TokenServiceIOS::RemoveAccount(
487     const std::string& account_id) {
488   DCHECK(thread_checker_.CalledOnValidThread());
489   DCHECK(!account_id.empty());
490   DCHECK(!use_legacy_token_service_);
491
492   if (accounts_.count(account_id) > 0) {
493     CancelRequestsForAccount(account_id);
494     ClearCacheForAccount(account_id);
495     accounts_.erase(account_id);
496     FireRefreshTokenRevoked(account_id);
497   }
498 }
499
500 void ProfileOAuth2TokenServiceIOS::StartUsingSharedAuthentication() {
501   if (!use_legacy_token_service_)
502     return;
503   MutableProfileOAuth2TokenService::RevokeAllCredentials();
504   use_legacy_token_service_ = false;
505 }
506
507 void ProfileOAuth2TokenServiceIOS::SetUseLegacyTokenServiceForTesting(
508     bool use_legacy_token_service) {
509   use_legacy_token_service_ = use_legacy_token_service;
510 }