Upstream version 10.39.225.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 // Match the way Chromium handles authentication errors in
24 // google_apis/gaia/oauth2_access_token_fetcher.cc:
25 GoogleServiceAuthError GetGoogleServiceAuthErrorFromNSError(
26     ios::ProfileOAuth2TokenServiceIOSProvider* provider,
27     NSError* error) {
28   if (!error)
29     return GoogleServiceAuthError::AuthErrorNone();
30
31   ios::AuthenticationErrorCategory errorCategory =
32       provider->GetAuthenticationErrorCategory(error);
33   switch (errorCategory) {
34     case ios::kAuthenticationErrorCategoryUnknownErrors:
35       // Treat all unknown error as unexpected service response errors.
36       // This may be too general and may require a finer grain filtering.
37       return GoogleServiceAuthError(
38           GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE);
39     case ios::kAuthenticationErrorCategoryAuthorizationErrors:
40       return GoogleServiceAuthError(
41           GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
42     case ios::kAuthenticationErrorCategoryAuthorizationForbiddenErrors:
43       // HTTP_FORBIDDEN (403) is treated as temporary error, because it may be
44       // '403 Rate Limit Exceeded.' (for more details, see
45       // google_apis/gaia/oauth2_access_token_fetcher.cc).
46       return GoogleServiceAuthError(
47           GoogleServiceAuthError::SERVICE_UNAVAILABLE);
48     case ios::kAuthenticationErrorCategoryNetworkServerErrors:
49       // Just set the connection error state to FAILED.
50       return GoogleServiceAuthError::FromConnectionError(
51           net::URLRequestStatus::FAILED);
52     case ios::kAuthenticationErrorCategoryUserCancellationErrors:
53       return GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
54     case ios::kAuthenticationErrorCategoryUnknownIdentityErrors:
55       return GoogleServiceAuthError(GoogleServiceAuthError::USER_NOT_SIGNED_UP);
56   }
57 }
58
59 class SSOAccessTokenFetcher : public OAuth2AccessTokenFetcher {
60  public:
61   SSOAccessTokenFetcher(OAuth2AccessTokenConsumer* consumer,
62                         ios::ProfileOAuth2TokenServiceIOSProvider* provider,
63                         const std::string account_id);
64   virtual ~SSOAccessTokenFetcher();
65
66   virtual void Start(const std::string& client_id,
67                      const std::string& client_secret,
68                      const std::vector<std::string>& scopes) OVERRIDE;
69
70   virtual void CancelRequest() OVERRIDE;
71
72   // Handles an access token response.
73   void OnAccessTokenResponse(NSString* token,
74                              NSDate* expiration,
75                              NSError* error);
76
77  private:
78   base::WeakPtrFactory<SSOAccessTokenFetcher> weak_factory_;
79   ios::ProfileOAuth2TokenServiceIOSProvider* provider_;  // weak
80   std::string account_id_;
81   bool request_was_cancelled_;
82
83   DISALLOW_COPY_AND_ASSIGN(SSOAccessTokenFetcher);
84 };
85
86 SSOAccessTokenFetcher::SSOAccessTokenFetcher(
87     OAuth2AccessTokenConsumer* consumer,
88     ios::ProfileOAuth2TokenServiceIOSProvider* provider,
89     const std::string account_id)
90     : OAuth2AccessTokenFetcher(consumer),
91       weak_factory_(this),
92       provider_(provider),
93       account_id_(account_id),
94       request_was_cancelled_(false) {
95   DCHECK(provider_);
96 }
97
98 SSOAccessTokenFetcher::~SSOAccessTokenFetcher() {}
99
100 void SSOAccessTokenFetcher::Start(const std::string& client_id,
101                                   const std::string& client_secret,
102                                   const std::vector<std::string>& scopes) {
103   std::set<std::string> scopes_set(scopes.begin(), scopes.end());
104   provider_->GetAccessToken(
105       account_id_, client_id, client_secret, scopes_set,
106       base::Bind(&SSOAccessTokenFetcher::OnAccessTokenResponse,
107                  weak_factory_.GetWeakPtr()));
108 }
109
110 void SSOAccessTokenFetcher::CancelRequest() { request_was_cancelled_ = true; }
111
112 void SSOAccessTokenFetcher::OnAccessTokenResponse(NSString* token,
113                                                   NSDate* expiration,
114                                                   NSError* error) {
115   if (request_was_cancelled_) {
116     // Ignore the callback if the request was cancelled.
117     return;
118   }
119   GoogleServiceAuthError auth_error =
120       GetGoogleServiceAuthErrorFromNSError(provider_, error);
121   if (auth_error.state() == GoogleServiceAuthError::NONE) {
122     base::Time expiration_date =
123         base::Time::FromDoubleT([expiration timeIntervalSince1970]);
124     FireOnGetTokenSuccess(base::SysNSStringToUTF8(token), expiration_date);
125   } else {
126     FireOnGetTokenFailure(auth_error);
127   }
128 }
129
130 }  // namespace
131
132 ProfileOAuth2TokenServiceIOS::AccountInfo::AccountInfo(
133     ProfileOAuth2TokenService* token_service,
134     const std::string& account_id)
135     : token_service_(token_service),
136       account_id_(account_id),
137       last_auth_error_(GoogleServiceAuthError::NONE) {
138   DCHECK(token_service_);
139   DCHECK(!account_id_.empty());
140   token_service_->signin_error_controller()->AddProvider(this);
141 }
142
143 ProfileOAuth2TokenServiceIOS::AccountInfo::~AccountInfo() {
144   token_service_->signin_error_controller()->RemoveProvider(this);
145 }
146
147 void ProfileOAuth2TokenServiceIOS::AccountInfo::SetLastAuthError(
148     const GoogleServiceAuthError& error) {
149   if (error.state() != last_auth_error_.state()) {
150     last_auth_error_ = error;
151     token_service_->signin_error_controller()->AuthStatusChanged();
152   }
153 }
154
155 std::string ProfileOAuth2TokenServiceIOS::AccountInfo::GetAccountId() const {
156   return account_id_;
157 }
158
159 std::string ProfileOAuth2TokenServiceIOS::AccountInfo::GetUsername() const {
160   // TODO(rogerta): when |account_id| becomes the obfuscated gaia id, this
161   // will need to be changed.
162   return account_id_;
163 }
164
165 GoogleServiceAuthError
166 ProfileOAuth2TokenServiceIOS::AccountInfo::GetAuthStatus() const {
167   return last_auth_error_;
168 }
169
170 ProfileOAuth2TokenServiceIOS::ProfileOAuth2TokenServiceIOS()
171     : ProfileOAuth2TokenService() {
172   DCHECK(thread_checker_.CalledOnValidThread());
173 }
174
175 ProfileOAuth2TokenServiceIOS::~ProfileOAuth2TokenServiceIOS() {
176   DCHECK(thread_checker_.CalledOnValidThread());
177 }
178
179 void ProfileOAuth2TokenServiceIOS::Initialize(SigninClient* client) {
180   DCHECK(thread_checker_.CalledOnValidThread());
181   ProfileOAuth2TokenService::Initialize(client);
182 }
183
184 void ProfileOAuth2TokenServiceIOS::Shutdown() {
185   DCHECK(thread_checker_.CalledOnValidThread());
186   CancelAllRequests();
187   accounts_.clear();
188   ProfileOAuth2TokenService::Shutdown();
189 }
190
191 ios::ProfileOAuth2TokenServiceIOSProvider*
192 ProfileOAuth2TokenServiceIOS::GetProvider() {
193   ios::ProfileOAuth2TokenServiceIOSProvider* provider =
194       client()->GetIOSProvider();
195   DCHECK(provider);
196   return provider;
197 }
198
199 void ProfileOAuth2TokenServiceIOS::LoadCredentials(
200     const std::string& primary_account_id) {
201   DCHECK(thread_checker_.CalledOnValidThread());
202
203   // LoadCredentials() is called iff the user is signed in to Chrome, so the
204   // primary account id must not be empty.
205   DCHECK(!primary_account_id.empty());
206
207   GetProvider()->InitializeSharedAuthentication();
208   ReloadCredentials();
209   FireRefreshTokensLoaded();
210 }
211
212 void ProfileOAuth2TokenServiceIOS::ReloadCredentials() {
213   DCHECK(thread_checker_.CalledOnValidThread());
214
215   ScopedBacthChange batch(this);
216
217   // Remove all old accounts that do not appear in |new_accounts| and then
218   // load |new_accounts|.
219   std::vector<std::string> new_accounts(GetProvider()->GetAllAccountIds());
220   std::vector<std::string> old_accounts(GetAccounts());
221   for (auto i = old_accounts.begin(); i != old_accounts.end(); ++i) {
222     if (std::find(new_accounts.begin(), new_accounts.end(), *i) ==
223         new_accounts.end()) {
224       RemoveAccount(*i);
225     }
226   }
227
228   // Load all new_accounts.
229   for (auto i = new_accounts.begin(); i != new_accounts.end(); ++i) {
230     AddOrUpdateAccount(*i);
231   }
232 }
233
234 void ProfileOAuth2TokenServiceIOS::UpdateCredentials(
235     const std::string& account_id,
236     const std::string& refresh_token) {
237   DCHECK(thread_checker_.CalledOnValidThread());
238   NOTREACHED() << "Unexpected call to UpdateCredentials when using shared "
239                   "authentication.";
240 }
241
242 void ProfileOAuth2TokenServiceIOS::RevokeAllCredentials() {
243   DCHECK(thread_checker_.CalledOnValidThread());
244
245   ScopedBacthChange batch(this);
246   CancelAllRequests();
247   ClearCache();
248   AccountInfoMap toRemove = accounts_;
249   for (AccountInfoMap::iterator i = toRemove.begin(); i != toRemove.end(); ++i)
250     RemoveAccount(i->first);
251
252   DCHECK_EQ(0u, accounts_.size());
253 }
254
255 OAuth2AccessTokenFetcher*
256 ProfileOAuth2TokenServiceIOS::CreateAccessTokenFetcher(
257     const std::string& account_id,
258     net::URLRequestContextGetter* getter,
259     OAuth2AccessTokenConsumer* consumer) {
260   return new SSOAccessTokenFetcher(consumer, GetProvider(), account_id);
261 }
262
263 void ProfileOAuth2TokenServiceIOS::InvalidateOAuth2Token(
264     const std::string& account_id,
265     const std::string& client_id,
266     const ScopeSet& scopes,
267     const std::string& access_token) {
268   DCHECK(thread_checker_.CalledOnValidThread());
269
270   // Call |ProfileOAuth2TokenService::InvalidateOAuth2Token| to clear the
271   // cached access token.
272   ProfileOAuth2TokenService::InvalidateOAuth2Token(account_id,
273                                                    client_id,
274                                                    scopes,
275                                                    access_token);
276
277   // There is no need to inform the authentication library that the access
278   // token is invalid as it never caches the token.
279 }
280
281 std::vector<std::string> ProfileOAuth2TokenServiceIOS::GetAccounts() {
282   DCHECK(thread_checker_.CalledOnValidThread());
283   std::vector<std::string> account_ids;
284   for (auto i = accounts_.begin(); i != accounts_.end(); ++i)
285     account_ids.push_back(i->first);
286   return account_ids;
287 }
288
289 bool ProfileOAuth2TokenServiceIOS::RefreshTokenIsAvailable(
290     const std::string& account_id) const {
291   DCHECK(thread_checker_.CalledOnValidThread());
292
293   return accounts_.count(account_id) > 0;
294 }
295
296 void ProfileOAuth2TokenServiceIOS::UpdateAuthError(
297     const std::string& account_id,
298     const GoogleServiceAuthError& error) {
299   DCHECK(thread_checker_.CalledOnValidThread());
300
301   // Do not report connection errors as these are not actually auth errors.
302   // We also want to avoid masking a "real" auth error just because we
303   // subsequently get a transient network error.
304   if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED ||
305       error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE) {
306     return;
307   }
308
309   if (accounts_.count(account_id) == 0) {
310     NOTREACHED();
311     return;
312   }
313   accounts_[account_id]->SetLastAuthError(error);
314 }
315
316 // Clear the authentication error state and notify all observers that a new
317 // refresh token is available so that they request new access tokens.
318 void ProfileOAuth2TokenServiceIOS::AddOrUpdateAccount(
319     const std::string& account_id) {
320   DCHECK(thread_checker_.CalledOnValidThread());
321   DCHECK(!account_id.empty());
322
323   bool account_present = accounts_.count(account_id) > 0;
324   if (account_present && accounts_[account_id]->GetAuthStatus().state() ==
325                              GoogleServiceAuthError::NONE) {
326     // No need to update the account if it is already a known account and if
327     // there is no auth error.
328     return;
329   }
330
331   if (account_present) {
332     CancelRequestsForAccount(account_id);
333     ClearCacheForAccount(account_id);
334   } else {
335     accounts_[account_id].reset(new AccountInfo(this, account_id));
336   }
337   UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
338   FireRefreshTokenAvailable(account_id);
339 }
340
341 void ProfileOAuth2TokenServiceIOS::RemoveAccount(
342     const std::string& account_id) {
343   DCHECK(thread_checker_.CalledOnValidThread());
344   DCHECK(!account_id.empty());
345
346   if (accounts_.count(account_id) > 0) {
347     CancelRequestsForAccount(account_id);
348     ClearCacheForAccount(account_id);
349     accounts_.erase(account_id);
350     FireRefreshTokenRevoked(account_id);
351   }
352 }