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

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

Issue 2923733003: [signin] Add DICe flow for account consistency requests. (Closed)
Patch Set: fix style Created 3 years, 6 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
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 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 "components/signin/core/browser/signin_header_helper.h" 5 #include "components/signin/core/browser/signin_header_helper.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <map>
8 9
10 #include "base/logging.h"
9 #include "base/macros.h" 11 #include "base/macros.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_split.h" 12 #include "base/strings/string_split.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "build/build_config.h" 13 #include "build/build_config.h"
15 #include "components/content_settings/core/browser/cookie_settings.h" 14 #include "components/content_settings/core/browser/cookie_settings.h"
16 #include "components/google/core/browser/google_util.h" 15 #include "components/google/core/browser/google_util.h"
16 #include "components/signin/core/browser/chrome_connected_header_helper.h"
17 #include "components/signin/core/common/profile_management_switches.h" 17 #include "components/signin/core/common/profile_management_switches.h"
18 #include "google_apis/gaia/gaia_auth_util.h" 18 #include "google_apis/gaia/gaia_auth_util.h"
19 #include "google_apis/gaia/gaia_urls.h" 19 #include "google_apis/gaia/gaia_urls.h"
20 #include "net/base/escape.h" 20 #include "net/base/escape.h"
21 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
22 #include "net/http/http_response_headers.h" 21 #include "net/http/http_response_headers.h"
23 #include "net/url_request/url_request.h" 22 #include "net/url_request/url_request.h"
24 #include "url/gurl.h" 23 #include "url/gurl.h"
25 24
25 #if !defined(OS_IOS) && !defined(OS_ANDROID)
26 #include "components/signin/core/browser/dice_header_helper.h"
27 #endif
28
26 namespace signin { 29 namespace signin {
27 30
28 namespace { 31 namespace {
29 32
30 // Dictionary of fields in a mirror response header. 33 // Dictionary of fields in a mirror response header.
31 typedef std::map<std::string, std::string> MirrorResponseHeaderDictionary; 34 typedef std::map<std::string, std::string> MirrorResponseHeaderDictionary;
32 35
33 const char kChromeManageAccountsHeader[] = "X-Chrome-Manage-Accounts"; 36 const char kChromeManageAccountsHeader[] = "X-Chrome-Manage-Accounts";
34 const char kContinueUrlAttrName[] = "continue_url"; 37 const char kContinueUrlAttrName[] = "continue_url";
35 const char kEmailAttrName[] = "email"; 38 const char kEmailAttrName[] = "email";
36 const char kEnableAccountConsistencyAttrName[] = "enable_account_consistency";
37 const char kGaiaIdAttrName[] = "id";
38 const char kProfileModeAttrName[] = "mode";
39 const char kIsSameTabAttrName[] = "is_same_tab"; 39 const char kIsSameTabAttrName[] = "is_same_tab";
40 const char kIsSamlAttrName[] = "is_saml"; 40 const char kIsSamlAttrName[] = "is_saml";
41 const char kServiceTypeAttrName[] = "action"; 41 const char kServiceTypeAttrName[] = "action";
42 42
43 bool IsDriveOrigin(const GURL& url) { 43 // Determines the service type that has been passed from Gaia in the header.
44 if (!url.SchemeIsCryptographic())
45 return false;
46
47 const GURL kGoogleDriveURL("https://drive.google.com");
48 const GURL kGoogleDocsURL("https://docs.google.com");
49 return url == kGoogleDriveURL || url == kGoogleDocsURL;
50 }
51
52 bool IsUrlEligibleToIncludeGaiaId(const GURL& url, bool is_header_request) {
53 if (is_header_request) {
54 // GAIA Id is only necessary for Drive. Don't set it otherwise.
55 return IsDriveOrigin(url.GetOrigin());
56 }
57
58 // Cookie requests don't have the granularity to only include the GAIA Id for
59 // Drive origin. Set it on all google.com instead.
60 if (!url.SchemeIsCryptographic())
61 return false;
62
63 const std::string kGoogleDomain = "google.com";
64 std::string domain = net::registry_controlled_domains::GetDomainAndRegistry(
65 url, net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
66 return domain == kGoogleDomain;
67 }
68
69 // Determines the service type that has been passed from GAIA in the header.
70 GAIAServiceType GetGAIAServiceTypeFromHeader(const std::string& header_value) { 44 GAIAServiceType GetGAIAServiceTypeFromHeader(const std::string& header_value) {
71 if (header_value == "SIGNOUT") 45 if (header_value == "SIGNOUT")
72 return GAIA_SERVICE_TYPE_SIGNOUT; 46 return GAIA_SERVICE_TYPE_SIGNOUT;
73 else if (header_value == "INCOGNITO") 47 else if (header_value == "INCOGNITO")
74 return GAIA_SERVICE_TYPE_INCOGNITO; 48 return GAIA_SERVICE_TYPE_INCOGNITO;
75 else if (header_value == "ADDSESSION") 49 else if (header_value == "ADDSESSION")
76 return GAIA_SERVICE_TYPE_ADDSESSION; 50 return GAIA_SERVICE_TYPE_ADDSESSION;
77 else if (header_value == "REAUTH") 51 else if (header_value == "REAUTH")
78 return GAIA_SERVICE_TYPE_REAUTH; 52 return GAIA_SERVICE_TYPE_REAUTH;
79 else if (header_value == "SIGNUP") 53 else if (header_value == "SIGNUP")
80 return GAIA_SERVICE_TYPE_SIGNUP; 54 return GAIA_SERVICE_TYPE_SIGNUP;
81 else if (header_value == "DEFAULT") 55 else if (header_value == "DEFAULT")
82 return GAIA_SERVICE_TYPE_DEFAULT; 56 return GAIA_SERVICE_TYPE_DEFAULT;
83 else 57 else
84 return GAIA_SERVICE_TYPE_NONE; 58 return GAIA_SERVICE_TYPE_NONE;
85 } 59 }
86 60
87 // Parses the mirror response header. Its expected format is 61 // Parses the mirror response header. Its expected format is
88 // "key1=value1,key2=value2,...". 62 // "key1=value1,key2=value2,...".
89 MirrorResponseHeaderDictionary ParseMirrorResponseHeader( 63 MirrorResponseHeaderDictionary ParseMirrorResponseHeader(
90 const std::string& header_value) { 64 const std::string& header_value) {
91 MirrorResponseHeaderDictionary dictionary; 65 MirrorResponseHeaderDictionary dictionary;
92 for (const base::StringPiece& field : 66 for (const base::StringPiece& field :
93 base::SplitStringPiece(header_value, ",", base::KEEP_WHITESPACE, 67 base::SplitStringPiece(header_value, ",", base::KEEP_WHITESPACE,
94 base::SPLIT_WANT_NONEMPTY)) { 68 base::SPLIT_WANT_NONEMPTY)) {
95 size_t delim = field.find_first_of('='); 69 size_t delim = field.find_first_of('=');
96 if (delim == std::string::npos) { 70 if (delim == std::string::npos) {
97 DLOG(WARNING) << "Unexpected GAIA header field '" << field << "'."; 71 DLOG(WARNING) << "Unexpected Gaia header field '" << field << "'.";
98 continue; 72 continue;
99 } 73 }
100 dictionary[field.substr(0, delim).as_string()] = net::UnescapeURLComponent( 74 dictionary[field.substr(0, delim).as_string()] = net::UnescapeURLComponent(
101 field.substr(delim + 1).as_string(), 75 field.substr(delim + 1).as_string(),
102 net::UnescapeRule::PATH_SEPARATORS | 76 net::UnescapeRule::PATH_SEPARATORS |
103 net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS); 77 net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS);
104 } 78 }
105 return dictionary; 79 return dictionary;
106 } 80 }
107 81
108 // Checks if the url has the required properties to have a X-Chrome-Connected
109 // header.
110 bool IsUrlEligibleForXChromeConnectedHeader(const GURL& url) {
111 // Only set the header for Drive and Gaia always, and other Google properties
112 // if account consistency is enabled.
113 // Vasquette, which is integrated with most Google properties, needs the
114 // header to redirect certain user actions to Chrome native UI. Drive and Gaia
115 // need the header to tell if the current user is connected. The drive path is
116 // a temporary workaround until the more generic chrome.principals API is
117 // available.
118
119 // Consider the account id sensitive and limit it to secure domains.
120 if (!url.SchemeIsCryptographic())
121 return false;
122
123 GURL origin(url.GetOrigin());
124 bool is_enable_account_consistency =
125 switches::IsAccountConsistencyMirrorEnabled();
126 bool is_google_url = is_enable_account_consistency &&
127 (google_util::IsGoogleDomainUrl(
128 url, google_util::ALLOW_SUBDOMAIN,
129 google_util::DISALLOW_NON_STANDARD_PORTS) ||
130 google_util::IsYoutubeDomainUrl(
131 url, google_util::ALLOW_SUBDOMAIN,
132 google_util::DISALLOW_NON_STANDARD_PORTS));
133 return is_google_url || IsDriveOrigin(origin) ||
134 gaia::IsGaiaSignonRealm(origin);
135 }
136
137 // Checks if the url has the required properties to have an account consistency
138 // header.
139 bool IsUrlEligibleForAccountConsistencyRequestHeader(const GURL& url) {
140 // TODO(droger): Support X-Chrome-ID-Consistency-Request.
141 return IsUrlEligibleForXChromeConnectedHeader(url);
142 }
143
144 std::string BuildMirrorRequestIfPossible(
145 bool is_header_request,
146 const GURL& url,
147 const std::string& account_id,
148 const content_settings::CookieSettings* cookie_settings,
149 int profile_mode_mask) {
150 if (account_id.empty())
151 return std::string();
152
153 // If signin cookies are not allowed, don't add the header.
154 if (!SettingsAllowSigninCookies(cookie_settings)) {
155 return std::string();
156 }
157
158 // Check if url is elligible for the header.
159 if (!IsUrlEligibleForXChromeConnectedHeader(url))
160 return std::string();
161
162 std::vector<std::string> parts;
163 if (IsUrlEligibleToIncludeGaiaId(url, is_header_request)) {
164 // Only set the GAIA Id on domains that actually requires it.
165 parts.push_back(
166 base::StringPrintf("%s=%s", kGaiaIdAttrName, account_id.c_str()));
167 }
168 parts.push_back(
169 base::StringPrintf("%s=%s", kProfileModeAttrName,
170 base::IntToString(profile_mode_mask).c_str()));
171 parts.push_back(base::StringPrintf(
172 "%s=%s", kEnableAccountConsistencyAttrName,
173 switches::IsAccountConsistencyMirrorEnabled() ? "true" : "false"));
174
175 return base::JoinString(parts, is_header_request ? "," : ":");
176 }
177
178 } // namespace 82 } // namespace
179 83
180 extern const char kChromeConnectedHeader[] = "X-Chrome-Connected"; 84 extern const char kChromeConnectedHeader[] = "X-Chrome-Connected";
85 extern const char kDiceRequestHeader[] = "X-Chrome-ID-Consistency-Request";
181 86
182 ManageAccountsParams::ManageAccountsParams() 87 ManageAccountsParams::ManageAccountsParams()
183 : service_type(GAIA_SERVICE_TYPE_NONE), 88 : service_type(GAIA_SERVICE_TYPE_NONE),
184 email(""), 89 email(""),
185 is_saml(false), 90 is_saml(false),
186 continue_url(""), 91 continue_url(""),
187 is_same_tab(false) { 92 is_same_tab(false) {
188 #if !defined(OS_IOS) 93 #if !defined(OS_IOS)
189 child_id = 0; 94 child_id = 0;
190 route_id = 0; 95 route_id = 0;
(...skipping 10 matching lines...) Expand all
201 return cookie_settings && 106 return cookie_settings &&
202 cookie_settings->IsCookieAccessAllowed(gaia_url, gaia_url) && 107 cookie_settings->IsCookieAccessAllowed(gaia_url, gaia_url) &&
203 cookie_settings->IsCookieAccessAllowed(google_url, google_url); 108 cookie_settings->IsCookieAccessAllowed(google_url, google_url);
204 } 109 }
205 110
206 std::string BuildMirrorRequestCookieIfPossible( 111 std::string BuildMirrorRequestCookieIfPossible(
207 const GURL& url, 112 const GURL& url,
208 const std::string& account_id, 113 const std::string& account_id,
209 const content_settings::CookieSettings* cookie_settings, 114 const content_settings::CookieSettings* cookie_settings,
210 int profile_mode_mask) { 115 int profile_mode_mask) {
211 return BuildMirrorRequestIfPossible(false /* is_header_request */, url, 116 return signin::ChromeConnectedHeaderHelper::BuildRequestCookieIfPossible(
212 account_id, cookie_settings, 117 url, account_id, cookie_settings, profile_mode_mask);
213 profile_mode_mask);
214 } 118 }
215 119
216 bool AppendOrRemoveAccountConsistentyRequestHeader( 120 bool SigninHeaderHelper::AppendOrRemoveRequestHeader(
217 net::URLRequest* request, 121 net::URLRequest* request,
122 const char* header_name,
218 const GURL& redirect_url, 123 const GURL& redirect_url,
219 const std::string& account_id, 124 const std::string& account_id,
220 const content_settings::CookieSettings* cookie_settings, 125 const content_settings::CookieSettings* cookie_settings,
221 int profile_mode_mask) { 126 int profile_mode_mask) {
222 const GURL& url = redirect_url.is_empty() ? request->url() : redirect_url; 127 const GURL& url = redirect_url.is_empty() ? request->url() : redirect_url;
223 128 std::string header_value = BuildRequestHeaderIfPossible(
224 // TODO(droger): Support X-Chrome-ID-Consistency-Request.
225 std::string header_name = kChromeConnectedHeader;
226 std::string header_value = BuildMirrorRequestIfPossible(
227 true /* is_header_request */, url, account_id, cookie_settings, 129 true /* is_header_request */, url, account_id, cookie_settings,
228 profile_mode_mask); 130 profile_mode_mask);
229 131
230 if (!header_name.empty() && header_value.empty()) { 132 if (header_value.empty()) {
231 // If the request is being redirected, and it has the account consistency 133 // If the request is being redirected, and it has the account consistency
232 // header, and current url is a Google URL, and the redirected one is not, 134 // header, and current url is a Google URL, and the redirected one is not,
233 // remove the header. 135 // remove the header.
234 if (!redirect_url.is_empty() && 136 if (!redirect_url.is_empty() &&
235 request->extra_request_headers().HasHeader(header_name) && 137 request->extra_request_headers().HasHeader(header_name) &&
236 IsUrlEligibleForAccountConsistencyRequestHeader(request->url()) && 138 IsUrlEligibleForRequestHeader(request->url()) &&
237 !IsUrlEligibleForAccountConsistencyRequestHeader(redirect_url)) { 139 !IsUrlEligibleForRequestHeader(redirect_url)) {
238 request->RemoveRequestHeaderByName(header_name); 140 request->RemoveRequestHeaderByName(header_name);
239 } 141 }
240 return false; 142 return false;
241 } 143 }
242 request->SetExtraRequestHeaderByName(header_name, header_value, false); 144 request->SetExtraRequestHeaderByName(header_name, header_value, false);
243 return true; 145 return true;
244 } 146 }
245 147
148 std::string SigninHeaderHelper::BuildRequestHeaderIfPossible(
149 bool is_header_request,
150 const GURL& url,
151 const std::string& account_id,
152 const content_settings::CookieSettings* cookie_settings,
153 int profile_mode_mask) {
154 // If signin cookies are not allowed, don't add the header.
155 if (!SettingsAllowSigninCookies(cookie_settings))
156 return std::string();
157
158 // Check if url is eligible for the header.
159 if (!IsUrlEligibleForRequestHeader(url))
160 return std::string();
161
162 return BuildRequestHeader(is_header_request, url, account_id,
163 profile_mode_mask);
164 }
165
166 void AppendOrRemoveAccountConsistentyRequestHeader(
167 net::URLRequest* request,
168 const GURL& redirect_url,
169 const std::string& account_id,
170 const content_settings::CookieSettings* cookie_settings,
171 int profile_mode_mask) {
172 // Dice is not enabled on mobile.
173 #if !defined(OS_IOS) && !defined(OS_ANDROID)
174 DiceHeaderHelper dice_helper;
175 dice_helper.AppendOrRemoveRequestHeader(request, kDiceRequestHeader,
176 redirect_url, account_id,
177 cookie_settings, profile_mode_mask);
178 #endif
179
180 ChromeConnectedHeaderHelper chrome_connected_helper;
181 chrome_connected_helper.AppendOrRemoveRequestHeader(
182 request, kChromeConnectedHeader, redirect_url, account_id,
183 cookie_settings, profile_mode_mask);
184 }
185
246 ManageAccountsParams BuildManageAccountsParams( 186 ManageAccountsParams BuildManageAccountsParams(
247 const std::string& header_value) { 187 const std::string& header_value) {
248 ManageAccountsParams params; 188 ManageAccountsParams params;
249 MirrorResponseHeaderDictionary header_dictionary = 189 MirrorResponseHeaderDictionary header_dictionary =
250 ParseMirrorResponseHeader(header_value); 190 ParseMirrorResponseHeader(header_value);
251 MirrorResponseHeaderDictionary::const_iterator it = header_dictionary.begin(); 191 MirrorResponseHeaderDictionary::const_iterator it = header_dictionary.begin();
252 for (; it != header_dictionary.end(); ++it) { 192 for (; it != header_dictionary.end(); ++it) {
253 const std::string key_name(it->first); 193 const std::string key_name(it->first);
254 if (key_name == kServiceTypeAttrName) { 194 if (key_name == kServiceTypeAttrName) {
255 params.service_type = 195 params.service_type =
256 GetGAIAServiceTypeFromHeader(header_dictionary[kServiceTypeAttrName]); 196 GetGAIAServiceTypeFromHeader(header_dictionary[kServiceTypeAttrName]);
257 } else if (key_name == kEmailAttrName) { 197 } else if (key_name == kEmailAttrName) {
258 params.email = header_dictionary[kEmailAttrName]; 198 params.email = header_dictionary[kEmailAttrName];
259 } else if (key_name == kIsSamlAttrName) { 199 } else if (key_name == kIsSamlAttrName) {
260 params.is_saml = header_dictionary[kIsSamlAttrName] == "true"; 200 params.is_saml = header_dictionary[kIsSamlAttrName] == "true";
261 } else if (key_name == kContinueUrlAttrName) { 201 } else if (key_name == kContinueUrlAttrName) {
262 params.continue_url = header_dictionary[kContinueUrlAttrName]; 202 params.continue_url = header_dictionary[kContinueUrlAttrName];
263 } else if (key_name == kIsSameTabAttrName) { 203 } else if (key_name == kIsSameTabAttrName) {
264 params.is_same_tab = header_dictionary[kIsSameTabAttrName] == "true"; 204 params.is_same_tab = header_dictionary[kIsSameTabAttrName] == "true";
265 } else { 205 } else {
266 DLOG(WARNING) << "Unexpected GAIA header attribute '" << key_name << "'."; 206 DLOG(WARNING) << "Unexpected Gaia header attribute '" << key_name << "'.";
267 } 207 }
268 } 208 }
269 return params; 209 return params;
270 } 210 }
271 211
272 ManageAccountsParams BuildManageAccountsParamsIfExists(net::URLRequest* request, 212 ManageAccountsParams BuildManageAccountsParamsIfExists(net::URLRequest* request,
273 bool is_off_the_record) { 213 bool is_off_the_record) {
274 ManageAccountsParams empty_params; 214 ManageAccountsParams empty_params;
275 empty_params.service_type = GAIA_SERVICE_TYPE_NONE; 215 empty_params.service_type = GAIA_SERVICE_TYPE_NONE;
276 if (!gaia::IsGaiaSignonRealm(request->url().GetOrigin())) 216 if (!gaia::IsGaiaSignonRealm(request->url().GetOrigin()))
277 return empty_params; 217 return empty_params;
278 218
279 std::string header_value; 219 std::string header_value;
280 net::HttpResponseHeaders* response_headers = request->response_headers(); 220 net::HttpResponseHeaders* response_headers = request->response_headers();
281 if (!response_headers || 221 if (!response_headers ||
282 !response_headers->GetNormalizedHeader( 222 !response_headers->GetNormalizedHeader(
283 kChromeManageAccountsHeader, &header_value)) { 223 kChromeManageAccountsHeader, &header_value)) {
284 return empty_params; 224 return empty_params;
285 } 225 }
286 226
287 DCHECK(switches::IsAccountConsistencyMirrorEnabled() && !is_off_the_record); 227 DCHECK(switches::IsAccountConsistencyMirrorEnabled() && !is_off_the_record);
288 return BuildManageAccountsParams(header_value); 228 return BuildManageAccountsParams(header_value);
289 } 229 }
290 230
291 } // namespace signin 231 } // namespace signin
OLDNEW
« no previous file with comments | « components/signin/core/browser/signin_header_helper.h ('k') | components/signin/core/browser/signin_header_helper_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698