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

Side by Side Diff: chrome/browser/managed_mode/managed_user_token_fetcher.cc

Issue 15977002: Add ManagedUserTokenFetcher to fetch scoped-down tokens. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: review Created 7 years, 7 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 "chrome/browser/managed_mode/managed_user_token_fetcher.h"
6
7 #include "base/callback.h"
8 #include "base/json/json_reader.h"
9 #include "base/logging.h"
10 #include "base/string16.h"
11 #include "base/stringprintf.h"
12 #include "base/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "chrome/browser/signin/oauth2_token_service.h"
15 #include "google_apis/gaia/gaia_oauth_client.h"
16 #include "google_apis/gaia/gaia_urls.h"
17 #include "google_apis/gaia/google_service_auth_error.h"
18 #include "google_apis/gaia/oauth2_api_call_flow.h"
19 #include "net/base/escape.h"
20 #include "net/base/load_flags.h"
21 #include "net/base/net_errors.h"
22 #include "net/http/http_status_code.h"
23 #include "net/url_request/url_fetcher.h"
24 #include "net/url_request/url_request_status.h"
25
26 using base::Time;
27 using gaia::GaiaOAuthClient;
28 using net::URLFetcher;
29 using net::URLFetcherDelegate;
30 using net::URLRequestContextGetter;
31
32 namespace {
33
34 const int kNumRetries = 1;
35
36 static const char kChromeSyncManagedScope[] =
37 "https://www.googleapis.com/auth/chromesync_playpen";
38
39 static const char kIssueTokenBodyFormat[] =
40 "client_id=%s"
41 "&scope=&%s"
42 "&response_type=code"
Andrew T Wilson (Slow) 2013/05/28 13:10:08 I wonder if it makes sense to extend OAuth2MintTok
Bernhard Bauer 2013/05/29 11:41:45 Well, there are also other problems: * OAuth2MintT
43 "&profile_id=%s"
44 "&profile_name=%s"
45 "&device_name=%s";
46
47 static const char kAuthorizationHeaderFormat[] =
48 "Authorization: Bearer %s";
49
50 static const char kCodeKey[] = "code";
51
52 class ManagedUserTokenFetcherImpl : public ManagedUserTokenFetcher,
Andrew T Wilson (Slow) 2013/05/28 13:10:08 BTW, I like this pattern of keeping the Impl class
53 public OAuth2TokenService::Consumer,
54 public URLFetcherDelegate,
55 public GaiaOAuthClient::Delegate {
56 public:
57 ManagedUserTokenFetcherImpl(OAuth2TokenService* oauth2_token_service,
58 URLRequestContextGetter* context);
59 virtual ~ManagedUserTokenFetcherImpl();
60
61 // ManagedUserTokenFetcher implementation:
62 virtual void Start(const std::string& managed_user_id,
63 const string16& name,
64 const std::string& device_name,
65 const TokenCallback& callback) OVERRIDE;
66
67 protected:
68 // OAuth2TokenService::Consumer implementation:
69 virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
70 const std::string& access_token,
71 const Time& expiration_time) OVERRIDE;
72 virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
73 const GoogleServiceAuthError& error) OVERRIDE;
74
75 // net::URLFetcherDelegate implementation.
76 virtual void OnURLFetchComplete(const URLFetcher* source) OVERRIDE;
77
78 // GaiaOAuthClient::Delegate implementation:
79 virtual void OnGetTokensResponse(const std::string& refresh_token,
80 const std::string& access_token,
81 int expires_in_seconds) OVERRIDE;
82 virtual void OnRefreshTokenResponse(const std::string& access_token,
83 int expires_in_seconds) OVERRIDE;
84 virtual void OnOAuthError() OVERRIDE;
85 virtual void OnNetworkError(int response_code) OVERRIDE;
86
87 private:
88 // Requests an access token, which is the first thing we need. This is where
89 // we restart when the returned access token has expired.
90 void StartFetching();
91
92 void DispatchNetworkError(int error_code);
93 void DispatchGoogleServiceAuthError(GoogleServiceAuthError::State foo);
94
95 OAuth2TokenService* oauth2_token_service_;
96 URLRequestContextGetter* context_;
97
98 std::string device_name_;
99 std::string managed_user_id_;
100 string16 name_;
101 TokenCallback callback_;
102
103 scoped_ptr<OAuth2TokenService::Request> access_token_request_;
104 std::string access_token_;
105 scoped_ptr<URLFetcher> url_fetcher_;
106 scoped_ptr<GaiaOAuthClient> gaia_oauth_client_;
107 };
108
109 ManagedUserTokenFetcherImpl::ManagedUserTokenFetcherImpl(
110 OAuth2TokenService* oauth2_token_service,
111 URLRequestContextGetter* context)
112 : oauth2_token_service_(oauth2_token_service),
113 context_(context) {}
114
115 ManagedUserTokenFetcherImpl::~ManagedUserTokenFetcherImpl() {}
116
117 void ManagedUserTokenFetcherImpl::Start(
118 const std::string& managed_user_id,
119 const string16& name,
120 const std::string& device_name,
121 const TokenCallback& callback) {
122 DCHECK(callback_.is_null());
123 managed_user_id_ = managed_user_id;
124 name_ = name;
125 device_name_ = device_name;
126 callback_ = callback;
127 StartFetching();
Andrew T Wilson (Slow) 2013/05/28 13:10:08 So, let's imagine that we have a really slow inter
Bernhard Bauer 2013/05/29 11:41:45 Yes, at least on desktop. I'll ask about Chrome OS
Andrew T Wilson (Slow) 2013/05/29 12:05:33 I guess I don't understand the flow, then. 1) Use
Bernhard Bauer 2013/05/29 12:56:55 Yes. I'm planning on adding that as soon as Pam ha
Andrew T Wilson (Slow) 2013/05/29 13:12:17 Adding a TODO in the header file is sufficient for
Bernhard Bauer 2013/05/29 14:11:15 Done.
128 }
129
130 void ManagedUserTokenFetcherImpl::StartFetching() {
131 // An empty set of scopes means the same scope as the refresh token.
Andrew T Wilson (Slow) 2013/05/28 13:10:08 We should document this in OAuth2TokenService (tha
Bernhard Bauer 2013/05/29 11:41:45 Ok. Alternatively, I could pass in the login scope
Andrew T Wilson (Slow) 2013/05/29 12:05:33 Passing in an explicit scope would be ideal.
Bernhard Bauer 2013/05/29 12:56:55 Done.
132 OAuth2TokenService::ScopeSet scopes;
133 access_token_request_ = oauth2_token_service_->StartRequest(scopes, this);
134 }
135
136 void ManagedUserTokenFetcherImpl::OnGetTokenSuccess(
137 const OAuth2TokenService::Request* request,
138 const std::string& access_token,
139 const Time& expiration_time) {
140 DCHECK_EQ(access_token_request_.get(), request);
141 access_token_ = access_token;
142
143 GURL url(GaiaUrls::GetInstance()->oauth2_issue_token_url());
144 // GaiaOAuthClient uses id 0, so we use 1 to distinguish the requests in
145 // unit tests.
146 int id = 1;
Andrew T Wilson (Slow) 2013/05/28 13:10:08 nit: const int id
Bernhard Bauer 2013/05/29 11:41:45 Done.
147
148 url_fetcher_.reset(URLFetcher::Create(id, url, URLFetcher::POST, this));
149
150 url_fetcher_->SetRequestContext(context_);
151 url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
152 net::LOAD_DO_NOT_SAVE_COOKIES);
153 url_fetcher_->SetAutomaticallyRetryOnNetworkChanges(kNumRetries);
154 url_fetcher_->AddExtraRequestHeader(
155 base::StringPrintf(kAuthorizationHeaderFormat, access_token.c_str()));
156
157 std::string body = base::StringPrintf(
158 kIssueTokenBodyFormat,
159 net::EscapeUrlEncodedData(
160 GaiaUrls::GetInstance()->oauth2_chrome_client_id(), true).c_str(),
161 net::EscapeUrlEncodedData(kChromeSyncManagedScope, true).c_str(),
162 net::EscapeUrlEncodedData(managed_user_id_, true).c_str(),
163 net::EscapeUrlEncodedData(UTF16ToUTF8(name_), true).c_str(),
164 net::EscapeUrlEncodedData(device_name_, true).c_str());
165 url_fetcher_->SetUploadData("application/x-www-form-urlencoded", body);
166
167 url_fetcher_->Start();
168 }
169
170 void ManagedUserTokenFetcherImpl::OnGetTokenFailure(
171 const OAuth2TokenService::Request* request,
172 const GoogleServiceAuthError& error) {
173 DCHECK_EQ(access_token_request_.get(), request);
174 callback_.Run(error, std::string());
175 callback_.Reset();
176 }
177
178 void ManagedUserTokenFetcherImpl::OnURLFetchComplete(
179 const URLFetcher* source) {
180 const net::URLRequestStatus& status = source->GetStatus();
181 if (!status.is_success()) {
182 DispatchNetworkError(status.error());
183 return;
184 }
185
186 int response_code = source->GetResponseCode();
187 if (response_code == net::HTTP_UNAUTHORIZED) {
188 oauth2_token_service_->InvalidateToken(OAuth2TokenService::ScopeSet(),
189 access_token_);
190 StartFetching();
Andrew T Wilson (Slow) 2013/05/28 13:10:08 If there's some bug that causes the server to perm
Bernhard Bauer 2013/05/29 11:41:45 Good idea! Done.
191 return;
192 }
193
194 if (response_code != net::HTTP_OK) {
195 // TODO(bauerb): We should return the HTTP response code somehow.
196 LOG(WARNING) << "HTTP error " << response_code;
197 DispatchGoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED);
198 return;
199 }
200
201 std::string response_body;
202 source->GetResponseAsString(&response_body);
203 scoped_ptr<base::Value> value(base::JSONReader::Read(response_body));
204 DictionaryValue* dict = NULL;
205 if (!value.get() || !value->GetAsDictionary(&dict)) {
206 DispatchNetworkError(net::ERR_INVALID_RESPONSE);
207 return;
208 }
209 std::string auth_code;
210 if (!dict->GetString(kCodeKey, &auth_code)) {
211 DispatchNetworkError(net::ERR_INVALID_RESPONSE);
212 return;
213 }
214
215 gaia::OAuthClientInfo client_info;
216 GaiaUrls* urls = GaiaUrls::GetInstance();
217 client_info.client_id = urls->oauth2_chrome_client_id();
218 client_info.client_secret = urls->oauth2_chrome_client_secret();
219 gaia_oauth_client_.reset(
220 new gaia::GaiaOAuthClient(GaiaUrls::GetInstance()->oauth2_token_url(),
221 context_));
222 gaia_oauth_client_->GetTokensFromAuthCode(client_info, auth_code, kNumRetries,
223 this);
224 }
225
226 void ManagedUserTokenFetcherImpl::OnGetTokensResponse(
227 const std::string& refresh_token,
228 const std::string& access_token,
229 int expires_in_seconds) {
230 // TODO(bauerb): It would be nice if we could pass the access token as well,
231 // so we don't need to fetch another one immediately.
232 GoogleServiceAuthError error(GoogleServiceAuthError::NONE);
Andrew T Wilson (Slow) 2013/05/29 12:05:33 Call DispatchGoogleServiceAuthError(GoogleServiceA
Bernhard Bauer 2013/05/29 12:56:55 Done.
233 callback_.Run(error, refresh_token);
234 callback_.Reset();
235 }
236
237 void ManagedUserTokenFetcherImpl::OnRefreshTokenResponse(
238 const std::string& access_token,
239 int expires_in_seconds) {
240 NOTREACHED();
241 }
242
243 void ManagedUserTokenFetcherImpl::OnOAuthError() {
244 DispatchGoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED);
245 }
246
247 void ManagedUserTokenFetcherImpl::OnNetworkError(int response_code) {
248 // TODO(bauerb): We should return the HTTP response code somehow.
249 LOG(WARNING) << "HTTP error " << response_code;
Andrew T Wilson (Slow) 2013/05/29 12:05:33 I'm always leery of adding LOG() statements since
Bernhard Bauer 2013/05/29 12:56:55 Done. It does mean that you can only see the HTTP
250 DispatchGoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED);
251 }
252
253 void ManagedUserTokenFetcherImpl::DispatchNetworkError(int error_code) {
254 callback_.Run(GoogleServiceAuthError::FromConnectionError(error_code),
Andrew T Wilson (Slow) 2013/05/28 13:10:08 Maybe change this to: { DispatchGoogleServiceAu
Bernhard Bauer 2013/05/29 11:41:45 Do you mean adding an overload that takes a Google
Andrew T Wilson (Slow) 2013/05/29 12:05:33 I mean that instead of calling callback_.Run() and
Bernhard Bauer 2013/05/29 12:56:55 Done.
255 std::string());
256 callback_.Reset();
257 }
258
259 void ManagedUserTokenFetcherImpl::DispatchGoogleServiceAuthError(
260 GoogleServiceAuthError::State state) {
261 GoogleServiceAuthError error(state);
262 callback_.Run(error, std::string());
263 callback_.Reset();
264 }
265
266 } // namespace
267
268 // static
269 scoped_ptr<ManagedUserTokenFetcher> ManagedUserTokenFetcher::Create(
270 OAuth2TokenService* oauth2_token_service,
271 URLRequestContextGetter* context) {
272 scoped_ptr<ManagedUserTokenFetcher> fetcher(
273 new ManagedUserTokenFetcherImpl(oauth2_token_service, context));
274 return fetcher.Pass();
275 }
276
277 ManagedUserTokenFetcher::~ManagedUserTokenFetcher() {}
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698