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

Side by Side Diff: components/signin/core/browser/access_token_fetcher.cc

Issue 2582573002: Signin/OAuth: Create an AccessTokenFetcher helper class (Closed)
Patch Set: return AuthErrors Created 3 years, 11 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
OLDNEW
(Empty)
1 // Copyright 2017 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/core/browser/access_token_fetcher.h"
6
7 #include <utility>
8
9 #include "base/logging.h"
10
11 AccessTokenFetcher::AccessTokenFetcher(
12 const std::string& oauth_consumer_name,
13 SigninManagerBase* signin_manager,
14 OAuth2TokenService* token_service,
15 const OAuth2TokenService::ScopeSet& scopes,
16 TokenCallback callback)
17 : OAuth2TokenService::Consumer(oauth_consumer_name),
18 signin_manager_(signin_manager),
19 token_service_(token_service),
20 scopes_(scopes),
21 callback_(std::move(callback)),
22 waiting_for_sign_in_(false),
23 waiting_for_refresh_token_(false),
24 access_token_retried_(false) {
25 Start();
26 }
27
28 AccessTokenFetcher::~AccessTokenFetcher() {
29 if (waiting_for_sign_in_) {
30 signin_manager_->RemoveObserver(this);
31 }
32 if (waiting_for_refresh_token_) {
33 token_service_->RemoveObserver(this);
34 }
35 }
36
37 void AccessTokenFetcher::Start() {
38 if (signin_manager_->IsAuthenticated()) {
39 // Already signed in: Make sure we have a refresh token, then get the access
40 // token.
41 WaitForRefreshToken();
42 return;
43 }
44
45 // Not signed in: Wait for a sign-in to complete (to get the refresh token),
46 // then get the access token.
47 DCHECK(!waiting_for_sign_in_);
48 waiting_for_sign_in_ = true;
49 signin_manager_->AddObserver(this);
50 }
51
52 void AccessTokenFetcher::WaitForRefreshToken() {
53 DCHECK(signin_manager_->IsAuthenticated());
54 DCHECK(!waiting_for_refresh_token_);
55
56 if (token_service_->RefreshTokenIsAvailable(
57 signin_manager_->GetAuthenticatedAccountId())) {
58 // Already have refresh token: Get the access token directly.
59 StartAccessTokenRequest();
60 return;
61 }
62
63 // Signed in, but refresh token isn't there yet: Wait for the refresh
64 // token to be loaded, then get the access token.
65 waiting_for_refresh_token_ = true;
66 token_service_->AddObserver(this);
67 }
68
69 void AccessTokenFetcher::StartAccessTokenRequest() {
msarda 2017/01/27 10:38:58 Please add a DCHECK(!access_token_request_) to cat
Marc Treib 2017/01/27 11:16:55 Done.
70 DCHECK(token_service_->RefreshTokenIsAvailable(
71 signin_manager_->GetAuthenticatedAccountId()));
72 access_token_request_ = token_service_->StartRequest(
73 signin_manager_->GetAuthenticatedAccountId(), scopes_, this);
74 }
75
76 void AccessTokenFetcher::GoogleSigninSucceeded(const std::string& account_id,
77 const std::string& username,
78 const std::string& password) {
79 DCHECK(waiting_for_sign_in_);
80 DCHECK(!waiting_for_refresh_token_);
81 DCHECK(signin_manager_->IsAuthenticated());
82 waiting_for_sign_in_ = false;
83 signin_manager_->RemoveObserver(this);
84
85 WaitForRefreshToken();
86 }
87
88 void AccessTokenFetcher::GoogleSigninFailed(
89 const GoogleServiceAuthError& error) {
90 DCHECK(waiting_for_sign_in_);
91 DCHECK(!waiting_for_refresh_token_);
92 waiting_for_sign_in_ = false;
93 signin_manager_->RemoveObserver(this);
94
95 std::move(callback_).Run(
96 GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS),
msarda 2017/01/27 10:38:58 I suppose here we should pass the error |error| to
Marc Treib 2017/01/27 11:16:55 D'oh. Done.
97 std::string());
98 }
99
100 void AccessTokenFetcher::OnRefreshTokenAvailable(
101 const std::string& account_id) {
102 DCHECK(waiting_for_refresh_token_);
103 DCHECK(!waiting_for_sign_in_);
104
105 // Only react on tokens for the account the user has signed in with.
106 if (account_id != signin_manager_->GetAuthenticatedAccountId()) {
107 return;
108 }
109
110 waiting_for_refresh_token_ = false;
111 token_service_->RemoveObserver(this);
112 StartAccessTokenRequest();
113 }
114
115 void AccessTokenFetcher::OnRefreshTokensLoaded() {
116 DCHECK(waiting_for_refresh_token_);
117 DCHECK(!waiting_for_sign_in_);
118
119 // All refresh tokens were loaded, but we didn't get one for the account we
120 // care about. We probably won't get one any time soon.
121 waiting_for_refresh_token_ = false;
122 token_service_->RemoveObserver(this);
123 std::move(callback_).Run(
msarda 2017/01/27 10:38:58 I think calling here the callback with INVALID_GAI
Marc Treib 2017/01/27 11:16:55 But if OnRefreshTokenAvailable(authenticated_accou
msarda 2017/01/27 12:35:37 I missed that part where the this object removes i
124 GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS),
125 std::string());
126 }
127
128 void AccessTokenFetcher::OnGetTokenSuccess(
129 const OAuth2TokenService::Request* request,
130 const std::string& access_token,
131 const base::Time& expiration_time) {
132 DCHECK_EQ(request, access_token_request_.get());
133 std::unique_ptr<OAuth2TokenService::Request> request_deleter(
134 std::move(access_token_request_));
135
136 std::move(callback_).Run(GoogleServiceAuthError::AuthErrorNone(),
137 access_token);
138 }
139
140 void AccessTokenFetcher::OnGetTokenFailure(
141 const OAuth2TokenService::Request* request,
142 const GoogleServiceAuthError& error) {
143 DCHECK_EQ(request, access_token_request_.get());
144 std::unique_ptr<OAuth2TokenService::Request> request_deleter(
145 std::move(access_token_request_));
146
147 // There is a special case for Android that RefreshTokenIsAvailable and
148 // StartRequest are called to pre-fetch the account image and name before
149 // sign-in. In that case, our ongoing access token request gets cancelled.
150 // Moreover, OnRefreshTokenAvailable might happen after startup when the
151 // credentials are changed/updated.
152 // To handle these cases, we retry a canceled request once.
153 // However, a request may also get cancelled for legitimate reasons, e.g.
154 // because the user signed out. In those cases, there's no point in retrying,
155 // so only retry if there (still) is a valid refresh token.
156 // TODO(treib): Should we check error.IsTransientError() instead of explicit
msarda 2017/01/27 10:38:58 I think we're defending against a corner case when
Marc Treib 2017/01/27 11:16:55 A comment in google_service_auth_error.cc says "Th
msarda 2017/01/27 12:35:37 Do you plan to do something towards fixing this TO
Marc Treib 2017/01/27 13:16:38 That's a good point - it's a TODO without concrete
157 // checking for REQUEST_CANCELED?
158 if (!access_token_retried_ &&
159 error.state() == GoogleServiceAuthError::State::REQUEST_CANCELED &&
160 token_service_->RefreshTokenIsAvailable(
msarda 2017/01/27 10:38:58 I was thinking that here we may want to always ret
Marc Treib 2017/01/27 11:16:55 An unbounded retry loop is a recipe for DDOSing ou
msarda 2017/01/27 12:35:37 Ok, I am convinced now that your approach is the r
161 signin_manager_->GetAuthenticatedAccountId())) {
162 access_token_retried_ = true;
163 StartAccessTokenRequest();
164 return;
165 }
166
167 std::move(callback_).Run(error, std::string());
168 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698