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. |
20 const char kAccessTokenValue[] = "access_token"; | 27 const char kAccessTokenValue[] = "access_token"; |
21 const char kRefreshTokenValue[] = "refresh_token"; | 28 const char kRefreshTokenValue[] = "refresh_token"; |
22 const char kExpiresInValue[] = "expires_in"; | 29 const char kExpiresInValue[] = "expires_in"; |
| 30 |
| 31 // Values used when parsing userinfo response. |
| 32 const char kEmailValue[] = "email"; |
| 33 |
23 } // namespace | 34 } // namespace |
24 | 35 |
25 namespace remoting { | 36 namespace remoting { |
26 | 37 |
| 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 |
27 class GaiaOAuthClient::Core | 46 class GaiaOAuthClient::Core |
28 : public base::RefCountedThreadSafe<GaiaOAuthClient::Core> { | 47 : public base::RefCountedThreadSafe<GaiaOAuthClient::Core> { |
29 public: | 48 public: |
30 Core(const std::string& gaia_url, | 49 Core(const OAuthProviderInfo& info, |
31 net::URLRequestContextGetter* request_context_getter) | 50 net::URLRequestContextGetter* request_context_getter) |
32 : gaia_url_(gaia_url), | 51 : request_context_getter_(request_context_getter), |
33 request_context_getter_(request_context_getter), | |
34 delegate_(NULL) { | 52 delegate_(NULL) { |
35 } | 53 } |
36 | 54 |
37 void RefreshToken(const OAuthClientInfo& oauth_client_info, | 55 void RefreshToken(const OAuthClientInfo& oauth_client_info, |
38 const std::string& refresh_token, | 56 const std::string& refresh_token, |
39 GaiaOAuthClient::Delegate* delegate); | 57 GaiaOAuthClient::Delegate* delegate); |
40 | 58 |
41 private: | 59 private: |
42 friend class base::RefCountedThreadSafe<Core>; | 60 friend class base::RefCountedThreadSafe<Core>; |
43 virtual ~Core() {} | 61 virtual ~Core() {} |
44 | 62 |
45 void OnUrlFetchComplete(const net::URLRequestStatus& status, | 63 void OnAuthTokenFetchComplete(const net::URLRequestStatus& status, |
46 int response_code, | 64 int response_code, |
47 const std::string& response); | 65 const std::string& response); |
| 66 void FetchUserInfoAndInvokeCallback(); |
| 67 void OnUserInfoFetchComplete(const net::URLRequestStatus& status, |
| 68 int response_code, |
| 69 const std::string& response); |
48 | 70 |
49 GURL gaia_url_; | 71 OAuthProviderInfo provider_info_; |
| 72 |
50 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; | 73 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; |
51 GaiaOAuthClient::Delegate* delegate_; | 74 GaiaOAuthClient::Delegate* delegate_; |
52 scoped_ptr<UrlFetcher> request_; | 75 scoped_ptr<UrlFetcher> request_; |
| 76 |
| 77 std::string access_token_; |
| 78 int expires_in_seconds_; |
53 }; | 79 }; |
54 | 80 |
55 void GaiaOAuthClient::Core::RefreshToken( | 81 void GaiaOAuthClient::Core::RefreshToken( |
56 const OAuthClientInfo& oauth_client_info, | 82 const OAuthClientInfo& oauth_client_info, |
57 const std::string& refresh_token, | 83 const std::string& refresh_token, |
58 GaiaOAuthClient::Delegate* delegate) { | 84 GaiaOAuthClient::Delegate* delegate) { |
59 DCHECK(!request_.get()) << "Tried to fetch two things at once!"; | 85 DCHECK(!request_.get()) << "Tried to fetch two things at once!"; |
60 | 86 |
| 87 delegate_ = delegate; |
| 88 |
| 89 access_token_.clear(); |
| 90 expires_in_seconds_ = 0; |
| 91 |
61 std::string post_body = | 92 std::string post_body = |
62 "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) + | 93 "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) + |
63 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, | 94 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, |
64 true) + | 95 true) + |
65 "&client_secret=" + | 96 "&client_secret=" + |
66 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + | 97 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + |
67 "&grant_type=refresh_token"; | 98 "&grant_type=refresh_token"; |
68 delegate_ = delegate; | 99 request_.reset(new UrlFetcher(GURL(provider_info_.access_token_url), |
69 request_.reset(new UrlFetcher(gaia_url_, UrlFetcher::POST)); | 100 UrlFetcher::POST)); |
70 request_->SetRequestContext(request_context_getter_); | 101 request_->SetRequestContext(request_context_getter_); |
71 request_->SetUploadData("application/x-www-form-urlencoded", post_body); | 102 request_->SetUploadData("application/x-www-form-urlencoded", post_body); |
72 request_->Start(base::Bind(&GaiaOAuthClient::Core::OnUrlFetchComplete, this)); | 103 request_->Start( |
| 104 base::Bind(&GaiaOAuthClient::Core::OnAuthTokenFetchComplete, this)); |
73 } | 105 } |
74 | 106 |
75 void GaiaOAuthClient::Core::OnUrlFetchComplete( | 107 void GaiaOAuthClient::Core::OnAuthTokenFetchComplete( |
76 const net::URLRequestStatus& status, | 108 const net::URLRequestStatus& status, |
77 int response_code, | 109 int response_code, |
78 const std::string& response) { | 110 const std::string& response) { |
79 request_.reset(); | 111 request_.reset(); |
80 | 112 |
81 if (!status.is_success()) { | 113 if (!status.is_success()) { |
82 delegate_->OnNetworkError(response_code); | 114 delegate_->OnNetworkError(response_code); |
83 return; | 115 return; |
84 } | 116 } |
85 | 117 |
86 // HTTP_BAD_REQUEST means the arguments are invalid. | 118 // HTTP_BAD_REQUEST means the arguments are invalid. |
87 if (response_code == net::HTTP_BAD_REQUEST) { | 119 if (response_code == net::HTTP_BAD_REQUEST) { |
88 LOG(ERROR) << "Gaia response: response code=net::HTTP_BAD_REQUEST."; | 120 LOG(ERROR) << "Gaia response: response code=net::HTTP_BAD_REQUEST."; |
89 delegate_->OnOAuthError(); | 121 delegate_->OnOAuthError(); |
90 return; | 122 return; |
91 } | 123 } |
92 | 124 |
93 std::string access_token; | |
94 std::string refresh_token; | |
95 int expires_in_seconds = 0; | |
96 if (response_code == net::HTTP_OK) { | 125 if (response_code == net::HTTP_OK) { |
97 scoped_ptr<Value> message_value(base::JSONReader::Read(response)); | 126 scoped_ptr<Value> message_value(base::JSONReader::Read(response)); |
98 if (message_value.get() && | 127 if (message_value.get() && |
99 message_value->IsType(Value::TYPE_DICTIONARY)) { | 128 message_value->IsType(Value::TYPE_DICTIONARY)) { |
100 scoped_ptr<DictionaryValue> response_dict( | 129 scoped_ptr<DictionaryValue> response_dict( |
101 static_cast<DictionaryValue*>(message_value.release())); | 130 static_cast<DictionaryValue*>(message_value.release())); |
102 response_dict->GetString(kAccessTokenValue, &access_token); | 131 response_dict->GetString(kAccessTokenValue, &access_token_); |
103 response_dict->GetString(kRefreshTokenValue, &refresh_token); | 132 response_dict->GetInteger(kExpiresInValue, &expires_in_seconds_); |
104 response_dict->GetInteger(kExpiresInValue, &expires_in_seconds); | |
105 } | 133 } |
106 VLOG(1) << "Gaia response: acess_token='" << access_token | 134 VLOG(1) << "Gaia response: acess_token='" << access_token_ |
107 << "', refresh_token='" << refresh_token | 135 << "', expires in " << expires_in_seconds_ << " second(s)"; |
108 << "', expires in " << expires_in_seconds << " second(s)"; | |
109 } else { | 136 } else { |
110 LOG(ERROR) << "Gaia response: response code=" << response_code; | 137 LOG(ERROR) << "Gaia response: response code=" << response_code; |
111 } | 138 } |
112 | 139 |
113 if (access_token.empty()) { | 140 if (access_token_.empty()) { |
114 delegate_->OnNetworkError(response_code); | 141 delegate_->OnNetworkError(response_code); |
115 } else if (refresh_token.empty()) { | 142 } else { |
116 // If we only have an access token, then this was a refresh request. | 143 FetchUserInfoAndInvokeCallback(); |
117 delegate_->OnRefreshTokenResponse(access_token, expires_in_seconds); | |
118 } | 144 } |
119 } | 145 } |
120 | 146 |
121 GaiaOAuthClient::GaiaOAuthClient(const std::string& gaia_url, | 147 void GaiaOAuthClient::Core::FetchUserInfoAndInvokeCallback() { |
| 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, |
122 net::URLRequestContextGetter* context_getter) { | 180 net::URLRequestContextGetter* context_getter) { |
123 core_ = new Core(gaia_url, context_getter); | 181 core_ = new Core(provider_info, context_getter); |
124 } | 182 } |
125 | 183 |
126 GaiaOAuthClient::~GaiaOAuthClient() { | 184 GaiaOAuthClient::~GaiaOAuthClient() { |
127 } | 185 } |
128 | 186 |
129 void GaiaOAuthClient::RefreshToken(const OAuthClientInfo& oauth_client_info, | 187 void GaiaOAuthClient::RefreshToken(const OAuthClientInfo& oauth_client_info, |
130 const std::string& refresh_token, | 188 const std::string& refresh_token, |
131 Delegate* delegate) { | 189 Delegate* delegate) { |
132 return core_->RefreshToken(oauth_client_info, | 190 return core_->RefreshToken(oauth_client_info, |
133 refresh_token, | 191 refresh_token, |
134 delegate); | 192 delegate); |
135 } | 193 } |
136 | 194 |
137 } // namespace remoting | 195 } // namespace remoting |
OLD | NEW |