OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/host/gaia_oauth_client.h" | 5 #include "remoting/host/gaia_oauth_client.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/json/json_reader.h" | 8 #include "base/json/json_reader.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/memory/scoped_ptr.h" | 10 #include "base/memory/scoped_ptr.h" |
11 #include "base/values.h" | 11 #include "base/values.h" |
12 #include "googleurl/src/gurl.h" | 12 #include "googleurl/src/gurl.h" |
13 #include "net/base/escape.h" | 13 #include "net/base/escape.h" |
14 #include "net/http/http_status_code.h" | 14 #include "net/http/http_status_code.h" |
15 #include "net/url_request/url_request_context_getter.h" | 15 #include "net/url_request/url_request_context_getter.h" |
16 #include "net/url_request/url_request_status.h" | 16 #include "net/url_request/url_request_status.h" |
17 #include "remoting/host/url_fetcher.h" | 17 #include "remoting/host/url_fetcher.h" |
18 | 18 |
19 namespace { | 19 namespace { |
20 | |
21 const char kDefaultOAuth2TokenUrl[] = | |
22 "https://accounts.google.com/o/oauth2/token"; | |
23 const char kDefaultOAuth2UserInfoUrl[] = | |
24 "https://www.googleapis.com/oauth2/v1/userinfo"; | |
25 | |
26 // Values used to parse token response. | |
27 const char kAccessTokenValue[] = "access_token"; | 20 const char kAccessTokenValue[] = "access_token"; |
28 const char kRefreshTokenValue[] = "refresh_token"; | 21 const char kRefreshTokenValue[] = "refresh_token"; |
29 const char kExpiresInValue[] = "expires_in"; | 22 const char kExpiresInValue[] = "expires_in"; |
30 | |
31 // Values used when parsing userinfo response. | |
32 const char kEmailValue[] = "email"; | |
33 | |
34 } // namespace | 23 } // namespace |
35 | 24 |
36 namespace remoting { | 25 namespace remoting { |
37 | 26 |
38 // static | |
39 OAuthProviderInfo OAuthProviderInfo::GetDefault() { | |
40 OAuthProviderInfo result; | |
41 result.access_token_url = kDefaultOAuth2TokenUrl; | |
42 result.user_info_url = kDefaultOAuth2UserInfoUrl; | |
43 return result; | |
44 } | |
45 | |
46 class GaiaOAuthClient::Core | 27 class GaiaOAuthClient::Core |
47 : public base::RefCountedThreadSafe<GaiaOAuthClient::Core> { | 28 : public base::RefCountedThreadSafe<GaiaOAuthClient::Core> { |
48 public: | 29 public: |
49 Core(const OAuthProviderInfo& info, | 30 Core(const std::string& gaia_url, |
50 net::URLRequestContextGetter* request_context_getter) | 31 net::URLRequestContextGetter* request_context_getter) |
51 : request_context_getter_(request_context_getter), | 32 : gaia_url_(gaia_url), |
| 33 request_context_getter_(request_context_getter), |
52 delegate_(NULL) { | 34 delegate_(NULL) { |
53 } | 35 } |
54 | 36 |
55 void RefreshToken(const OAuthClientInfo& oauth_client_info, | 37 void RefreshToken(const OAuthClientInfo& oauth_client_info, |
56 const std::string& refresh_token, | 38 const std::string& refresh_token, |
57 GaiaOAuthClient::Delegate* delegate); | 39 GaiaOAuthClient::Delegate* delegate); |
58 | 40 |
59 private: | 41 private: |
60 friend class base::RefCountedThreadSafe<Core>; | 42 friend class base::RefCountedThreadSafe<Core>; |
61 virtual ~Core() {} | 43 virtual ~Core() {} |
62 | 44 |
63 void OnAuthTokenFetchComplete(const net::URLRequestStatus& status, | 45 void OnUrlFetchComplete(const net::URLRequestStatus& status, |
64 int response_code, | 46 int response_code, |
65 const std::string& response); | 47 const std::string& response); |
66 void FetchUserInfoAndInvokeCallback(); | |
67 void OnUserInfoFetchComplete(const net::URLRequestStatus& status, | |
68 int response_code, | |
69 const std::string& response); | |
70 | 48 |
71 OAuthProviderInfo provider_info_; | 49 GURL gaia_url_; |
72 | |
73 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; | 50 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; |
74 GaiaOAuthClient::Delegate* delegate_; | 51 GaiaOAuthClient::Delegate* delegate_; |
75 scoped_ptr<UrlFetcher> request_; | 52 scoped_ptr<UrlFetcher> request_; |
76 | |
77 std::string access_token_; | |
78 int expires_in_seconds_; | |
79 }; | 53 }; |
80 | 54 |
81 void GaiaOAuthClient::Core::RefreshToken( | 55 void GaiaOAuthClient::Core::RefreshToken( |
82 const OAuthClientInfo& oauth_client_info, | 56 const OAuthClientInfo& oauth_client_info, |
83 const std::string& refresh_token, | 57 const std::string& refresh_token, |
84 GaiaOAuthClient::Delegate* delegate) { | 58 GaiaOAuthClient::Delegate* delegate) { |
85 DCHECK(!request_.get()) << "Tried to fetch two things at once!"; | 59 DCHECK(!request_.get()) << "Tried to fetch two things at once!"; |
86 | 60 |
87 delegate_ = delegate; | |
88 | |
89 access_token_.clear(); | |
90 expires_in_seconds_ = 0; | |
91 | |
92 std::string post_body = | 61 std::string post_body = |
93 "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) + | 62 "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) + |
94 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, | 63 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, |
95 true) + | 64 true) + |
96 "&client_secret=" + | 65 "&client_secret=" + |
97 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + | 66 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + |
98 "&grant_type=refresh_token"; | 67 "&grant_type=refresh_token"; |
99 request_.reset(new UrlFetcher(GURL(provider_info_.access_token_url), | 68 delegate_ = delegate; |
100 UrlFetcher::POST)); | 69 request_.reset(new UrlFetcher(gaia_url_, UrlFetcher::POST)); |
101 request_->SetRequestContext(request_context_getter_); | 70 request_->SetRequestContext(request_context_getter_); |
102 request_->SetUploadData("application/x-www-form-urlencoded", post_body); | 71 request_->SetUploadData("application/x-www-form-urlencoded", post_body); |
103 request_->Start( | 72 request_->Start(base::Bind(&GaiaOAuthClient::Core::OnUrlFetchComplete, this)); |
104 base::Bind(&GaiaOAuthClient::Core::OnAuthTokenFetchComplete, this)); | |
105 } | 73 } |
106 | 74 |
107 void GaiaOAuthClient::Core::OnAuthTokenFetchComplete( | 75 void GaiaOAuthClient::Core::OnUrlFetchComplete( |
108 const net::URLRequestStatus& status, | 76 const net::URLRequestStatus& status, |
109 int response_code, | 77 int response_code, |
110 const std::string& response) { | 78 const std::string& response) { |
111 request_.reset(); | 79 request_.reset(); |
112 | 80 |
113 if (!status.is_success()) { | 81 if (!status.is_success()) { |
114 delegate_->OnNetworkError(response_code); | 82 delegate_->OnNetworkError(response_code); |
115 return; | 83 return; |
116 } | 84 } |
117 | 85 |
118 // HTTP_BAD_REQUEST means the arguments are invalid. | 86 // HTTP_BAD_REQUEST means the arguments are invalid. |
119 if (response_code == net::HTTP_BAD_REQUEST) { | 87 if (response_code == net::HTTP_BAD_REQUEST) { |
120 LOG(ERROR) << "Gaia response: response code=net::HTTP_BAD_REQUEST."; | 88 LOG(ERROR) << "Gaia response: response code=net::HTTP_BAD_REQUEST."; |
121 delegate_->OnOAuthError(); | 89 delegate_->OnOAuthError(); |
122 return; | 90 return; |
123 } | 91 } |
124 | 92 |
| 93 std::string access_token; |
| 94 std::string refresh_token; |
| 95 int expires_in_seconds = 0; |
125 if (response_code == net::HTTP_OK) { | 96 if (response_code == net::HTTP_OK) { |
126 scoped_ptr<Value> message_value(base::JSONReader::Read(response)); | 97 scoped_ptr<Value> message_value(base::JSONReader::Read(response)); |
127 if (message_value.get() && | 98 if (message_value.get() && |
128 message_value->IsType(Value::TYPE_DICTIONARY)) { | 99 message_value->IsType(Value::TYPE_DICTIONARY)) { |
129 scoped_ptr<DictionaryValue> response_dict( | 100 scoped_ptr<DictionaryValue> response_dict( |
130 static_cast<DictionaryValue*>(message_value.release())); | 101 static_cast<DictionaryValue*>(message_value.release())); |
131 response_dict->GetString(kAccessTokenValue, &access_token_); | 102 response_dict->GetString(kAccessTokenValue, &access_token); |
132 response_dict->GetInteger(kExpiresInValue, &expires_in_seconds_); | 103 response_dict->GetString(kRefreshTokenValue, &refresh_token); |
| 104 response_dict->GetInteger(kExpiresInValue, &expires_in_seconds); |
133 } | 105 } |
134 VLOG(1) << "Gaia response: acess_token='" << access_token_ | 106 VLOG(1) << "Gaia response: acess_token='" << access_token |
135 << "', expires in " << expires_in_seconds_ << " second(s)"; | 107 << "', refresh_token='" << refresh_token |
| 108 << "', expires in " << expires_in_seconds << " second(s)"; |
136 } else { | 109 } else { |
137 LOG(ERROR) << "Gaia response: response code=" << response_code; | 110 LOG(ERROR) << "Gaia response: response code=" << response_code; |
138 } | 111 } |
139 | 112 |
140 if (access_token_.empty()) { | 113 if (access_token.empty()) { |
141 delegate_->OnNetworkError(response_code); | 114 delegate_->OnNetworkError(response_code); |
142 } else { | 115 } else if (refresh_token.empty()) { |
143 FetchUserInfoAndInvokeCallback(); | 116 // If we only have an access token, then this was a refresh request. |
| 117 delegate_->OnRefreshTokenResponse(access_token, expires_in_seconds); |
144 } | 118 } |
145 } | 119 } |
146 | 120 |
147 void GaiaOAuthClient::Core::FetchUserInfoAndInvokeCallback() { | 121 GaiaOAuthClient::GaiaOAuthClient(const std::string& gaia_url, |
148 request_.reset(new UrlFetcher( | |
149 GURL(provider_info_.user_info_url), UrlFetcher::GET)); | |
150 request_->SetRequestContext(request_context_getter_); | |
151 request_->SetHeader("Authorization", "Bearer " + access_token_); | |
152 request_->Start( | |
153 base::Bind(&GaiaOAuthClient::Core::OnUserInfoFetchComplete, this)); | |
154 } | |
155 | |
156 void GaiaOAuthClient::Core::OnUserInfoFetchComplete( | |
157 const net::URLRequestStatus& status, | |
158 int response_code, | |
159 const std::string& response) { | |
160 std::string email; | |
161 if (response_code == net::HTTP_OK) { | |
162 scoped_ptr<Value> message_value(base::JSONReader::Read(response)); | |
163 if (message_value.get() && | |
164 message_value->IsType(Value::TYPE_DICTIONARY)) { | |
165 scoped_ptr<DictionaryValue> response_dict( | |
166 static_cast<DictionaryValue*>(message_value.release())); | |
167 response_dict->GetString(kEmailValue, &email); | |
168 } | |
169 } | |
170 | |
171 if (email.empty()) { | |
172 delegate_->OnNetworkError(response_code); | |
173 } else { | |
174 delegate_->OnRefreshTokenResponse( | |
175 email, access_token_, expires_in_seconds_); | |
176 } | |
177 } | |
178 | |
179 GaiaOAuthClient::GaiaOAuthClient(const OAuthProviderInfo& provider_info, | |
180 net::URLRequestContextGetter* context_getter) { | 122 net::URLRequestContextGetter* context_getter) { |
181 core_ = new Core(provider_info, context_getter); | 123 core_ = new Core(gaia_url, context_getter); |
182 } | 124 } |
183 | 125 |
184 GaiaOAuthClient::~GaiaOAuthClient() { | 126 GaiaOAuthClient::~GaiaOAuthClient() { |
185 } | 127 } |
186 | 128 |
187 void GaiaOAuthClient::RefreshToken(const OAuthClientInfo& oauth_client_info, | 129 void GaiaOAuthClient::RefreshToken(const OAuthClientInfo& oauth_client_info, |
188 const std::string& refresh_token, | 130 const std::string& refresh_token, |
189 Delegate* delegate) { | 131 Delegate* delegate) { |
190 return core_->RefreshToken(oauth_client_info, | 132 return core_->RefreshToken(oauth_client_info, |
191 refresh_token, | 133 refresh_token, |
192 delegate); | 134 delegate); |
193 } | 135 } |
194 | 136 |
195 } // namespace remoting | 137 } // namespace remoting |
OLD | NEW |