1 // Copyright 2012 The Chromium Authors
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 "net/http/http_auth_controller.h"
6
7 #include <utility>
8
9 #include "base/functional/bind.h"
10 #include "base/functional/callback_helpers.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "net/base/auth.h"
16 #include "net/base/url_util.h"
17 #include "net/dns/host_resolver.h"
18 #include "net/http/http_auth_handler.h"
19 #include "net/http/http_auth_handler_factory.h"
20 #include "net/http/http_network_session.h"
21 #include "net/http/http_request_headers.h"
22 #include "net/http/http_request_info.h"
23 #include "net/http/http_response_headers.h"
24 #include "net/log/net_log_event_type.h"
25 #include "net/log/net_log_source.h"
26 #include "net/log/net_log_source_type.h"
27 #include "net/log/net_log_with_source.h"
28 #include "url/scheme_host_port.h"
29
30 namespace net {
31
32 namespace {
33
ControllerParamsToValue(HttpAuth::Target target,const GURL & url)34 base::Value::Dict ControllerParamsToValue(HttpAuth::Target target,
35 const GURL& url) {
36 base::Value::Dict params;
37 params.Set("target", HttpAuth::GetAuthTargetString(target));
38 params.Set("url", url.spec());
39 return params;
40 }
41
42 } // namespace
43
HttpAuthController(HttpAuth::Target target,const GURL & auth_url,const NetworkAnonymizationKey & network_anonymization_key,HttpAuthCache * http_auth_cache,HttpAuthHandlerFactory * http_auth_handler_factory,HostResolver * host_resolver)44 HttpAuthController::HttpAuthController(
45 HttpAuth::Target target,
46 const GURL& auth_url,
47 const NetworkAnonymizationKey& network_anonymization_key,
48 HttpAuthCache* http_auth_cache,
49 HttpAuthHandlerFactory* http_auth_handler_factory,
50 HostResolver* host_resolver)
51 : target_(target),
52 auth_url_(auth_url),
53 auth_scheme_host_port_(auth_url),
54 auth_path_(auth_url.path()),
55 network_anonymization_key_(network_anonymization_key),
56 http_auth_cache_(http_auth_cache),
57 http_auth_handler_factory_(http_auth_handler_factory),
58 host_resolver_(host_resolver) {
59 DCHECK(target != HttpAuth::AUTH_PROXY || auth_path_ == "/");
60 DCHECK(auth_scheme_host_port_.IsValid());
61 }
62
~HttpAuthController()63 HttpAuthController::~HttpAuthController() {
64 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
65 if (net_log_.source().IsValid())
66 net_log_.EndEvent(NetLogEventType::AUTH_CONTROLLER);
67 }
68
BindToCallingNetLog(const NetLogWithSource & caller_net_log)69 void HttpAuthController::BindToCallingNetLog(
70 const NetLogWithSource& caller_net_log) {
71 if (!net_log_.source().IsValid()) {
72 net_log_ = NetLogWithSource::Make(caller_net_log.net_log(),
73 NetLogSourceType::HTTP_AUTH_CONTROLLER);
74 net_log_.BeginEvent(NetLogEventType::AUTH_CONTROLLER, [&] {
75 return ControllerParamsToValue(target_, auth_url_);
76 });
77 }
78 caller_net_log.AddEventReferencingSource(
79 NetLogEventType::AUTH_BOUND_TO_CONTROLLER, net_log_.source());
80 }
81
MaybeGenerateAuthToken(const HttpRequestInfo * request,CompletionOnceCallback callback,const NetLogWithSource & caller_net_log)82 int HttpAuthController::MaybeGenerateAuthToken(
83 const HttpRequestInfo* request,
84 CompletionOnceCallback callback,
85 const NetLogWithSource& caller_net_log) {
86 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
87 DCHECK(!auth_info_);
88 bool needs_auth = HaveAuth() || SelectPreemptiveAuth(caller_net_log);
89 if (!needs_auth)
90 return OK;
91 net_log_.BeginEventReferencingSource(NetLogEventType::AUTH_GENERATE_TOKEN,
92 caller_net_log.source());
93 const AuthCredentials* credentials = nullptr;
94 if (identity_.source != HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS)
95 credentials = &identity_.credentials;
96 DCHECK(auth_token_.empty());
97 DCHECK(callback_.is_null());
98 int rv = handler_->GenerateAuthToken(
99 credentials, request,
100 base::BindOnce(&HttpAuthController::OnGenerateAuthTokenDone,
101 base::Unretained(this)),
102 &auth_token_);
103
104 if (rv == ERR_IO_PENDING) {
105 callback_ = std::move(callback);
106 return rv;
107 }
108
109 return HandleGenerateTokenResult(rv);
110 }
111
SelectPreemptiveAuth(const NetLogWithSource & caller_net_log)112 bool HttpAuthController::SelectPreemptiveAuth(
113 const NetLogWithSource& caller_net_log) {
114 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
115 DCHECK(!HaveAuth());
116 DCHECK(identity_.invalid);
117
118 // Don't do preemptive authorization if the URL contains a username:password,
119 // since we must first be challenged in order to use the URL's identity.
120 if (auth_url_.has_username())
121 return false;
122
123 // SelectPreemptiveAuth() is on the critical path for each request, so it
124 // is expected to be fast. LookupByPath() is fast in the common case, since
125 // the number of http auth cache entries is expected to be very small.
126 // (For most users in fact, it will be 0.)
127 HttpAuthCache::Entry* entry = http_auth_cache_->LookupByPath(
128 auth_scheme_host_port_, target_, network_anonymization_key_, auth_path_);
129 if (!entry)
130 return false;
131
132 BindToCallingNetLog(caller_net_log);
133
134 // Try to create a handler using the previous auth challenge.
135 std::unique_ptr<HttpAuthHandler> handler_preemptive;
136 int rv_create =
137 http_auth_handler_factory_->CreatePreemptiveAuthHandlerFromString(
138 entry->auth_challenge(), target_, network_anonymization_key_,
139 auth_scheme_host_port_, entry->IncrementNonceCount(), net_log_,
140 host_resolver_, &handler_preemptive);
141 if (rv_create != OK)
142 return false;
143
144 // Set the state
145 identity_.source = HttpAuth::IDENT_SRC_PATH_LOOKUP;
146 identity_.invalid = false;
147 identity_.credentials = entry->credentials();
148 handler_.swap(handler_preemptive);
149 return true;
150 }
151
AddAuthorizationHeader(HttpRequestHeaders * authorization_headers)152 void HttpAuthController::AddAuthorizationHeader(
153 HttpRequestHeaders* authorization_headers) {
154 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
155 DCHECK(HaveAuth());
156 // auth_token_ can be empty if we encountered a permanent error with
157 // the auth scheme and want to retry.
158 if (!auth_token_.empty()) {
159 authorization_headers->SetHeader(
160 HttpAuth::GetAuthorizationHeaderName(target_), auth_token_);
161 auth_token_.clear();
162 }
163 }
164
HandleAuthChallenge(scoped_refptr<HttpResponseHeaders> headers,const SSLInfo & ssl_info,bool do_not_send_server_auth,bool establishing_tunnel,const NetLogWithSource & caller_net_log)165 int HttpAuthController::HandleAuthChallenge(
166 scoped_refptr<HttpResponseHeaders> headers,
167 const SSLInfo& ssl_info,
168 bool do_not_send_server_auth,
169 bool establishing_tunnel,
170 const NetLogWithSource& caller_net_log) {
171 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
172 DCHECK(headers.get());
173 DCHECK(auth_scheme_host_port_.IsValid());
174 DCHECK(!auth_info_);
175
176 BindToCallingNetLog(caller_net_log);
177 net_log_.BeginEventReferencingSource(NetLogEventType::AUTH_HANDLE_CHALLENGE,
178 caller_net_log.source());
179
180 // Give the existing auth handler first try at the authentication headers.
181 // This will also evict the entry in the HttpAuthCache if the previous
182 // challenge appeared to be rejected, or is using a stale nonce in the Digest
183 // case.
184 if (HaveAuth()) {
185 std::string challenge_used;
186 HttpAuth::AuthorizationResult result = HttpAuth::HandleChallengeResponse(
187 handler_.get(), *headers, target_, disabled_schemes_, &challenge_used);
188 switch (result) {
189 case HttpAuth::AUTHORIZATION_RESULT_ACCEPT:
190 break;
191 case HttpAuth::AUTHORIZATION_RESULT_INVALID:
192 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
193 break;
194 case HttpAuth::AUTHORIZATION_RESULT_REJECT:
195 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
196 break;
197 case HttpAuth::AUTHORIZATION_RESULT_STALE:
198 if (http_auth_cache_->UpdateStaleChallenge(
199 auth_scheme_host_port_, target_, handler_->realm(),
200 handler_->auth_scheme(), network_anonymization_key_,
201 challenge_used)) {
202 InvalidateCurrentHandler(INVALIDATE_HANDLER);
203 } else {
204 // It's possible that a server could incorrectly issue a stale
205 // response when the entry is not in the cache. Just evict the
206 // current value from the cache.
207 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
208 }
209 break;
210 case HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM:
211 // If the server changes the authentication realm in a
212 // subsequent challenge, invalidate cached credentials for the
213 // previous realm. If the server rejects a preemptive
214 // authorization and requests credentials for a different
215 // realm, we keep the cached credentials.
216 InvalidateCurrentHandler(
217 (identity_.source == HttpAuth::IDENT_SRC_PATH_LOOKUP) ?
218 INVALIDATE_HANDLER :
219 INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
220 break;
221 default:
222 NOTREACHED();
223 break;
224 }
225 }
226
227 identity_.invalid = true;
228 bool can_send_auth = (target_ != HttpAuth::AUTH_SERVER ||
229 !do_not_send_server_auth);
230
231 do {
232 if (!handler_.get() && can_send_auth) {
233 // Find the best authentication challenge that we support.
234 HttpAuth::ChooseBestChallenge(
235 http_auth_handler_factory_, *headers, ssl_info,
236 network_anonymization_key_, target_, auth_scheme_host_port_,
237 disabled_schemes_, net_log_, host_resolver_, &handler_);
238 }
239
240 if (!handler_.get()) {
241 if (establishing_tunnel) {
242 // We are establishing a tunnel, we can't show the error page because an
243 // active network attacker could control its contents. Instead, we just
244 // fail to establish the tunnel.
245 DCHECK_EQ(target_, HttpAuth::AUTH_PROXY);
246 net_log_.EndEventWithNetErrorCode(
247 NetLogEventType::AUTH_HANDLE_CHALLENGE, ERR_PROXY_AUTH_UNSUPPORTED);
248 return ERR_PROXY_AUTH_UNSUPPORTED;
249 }
250 // We found no supported challenge -- let the transaction continue so we
251 // end up displaying the error page.
252 net_log_.EndEvent(NetLogEventType::AUTH_HANDLE_CHALLENGE);
253 return OK;
254 }
255
256 if (handler_->NeedsIdentity()) {
257 // Pick a new auth identity to try, by looking to the URL and auth cache.
258 // If an identity to try is found, it is saved to identity_.
259 SelectNextAuthIdentityToTry();
260 } else {
261 // Proceed with the existing identity or a null identity.
262 identity_.invalid = false;
263 }
264
265 // From this point on, we are restartable.
266
267 if (identity_.invalid) {
268 // We have exhausted all identity possibilities.
269 if (!handler_->AllowsExplicitCredentials()) {
270 // If the handler doesn't accept explicit credentials, then we need to
271 // choose a different auth scheme.
272 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_DISABLE_SCHEME);
273 } else {
274 // Pass the challenge information back to the client.
275 PopulateAuthChallenge();
276 }
277 }
278
279 // If we get here and we don't have a handler_, that's because we
280 // invalidated it due to not having any viable identities to use with it. Go
281 // back and try again.
282 // TODO(asanka): Instead we should create a priority list of
283 // <handler,identity> and iterate through that.
284 } while(!handler_.get());
285 net_log_.EndEvent(NetLogEventType::AUTH_HANDLE_CHALLENGE);
286 return OK;
287 }
288
ResetAuth(const AuthCredentials & credentials)289 void HttpAuthController::ResetAuth(const AuthCredentials& credentials) {
290 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
291 DCHECK(identity_.invalid || credentials.Empty());
292
293 if (identity_.invalid) {
294 // Update the credentials.
295 identity_.source = HttpAuth::IDENT_SRC_EXTERNAL;
296 identity_.invalid = false;
297 identity_.credentials = credentials;
298
299 // auth_info_ is no longer necessary.
300 auth_info_ = absl::nullopt;
301 }
302
303 DCHECK(identity_.source != HttpAuth::IDENT_SRC_PATH_LOOKUP);
304
305 // Add the auth entry to the cache before restarting. We don't know whether
306 // the identity is valid yet, but if it is valid we want other transactions
307 // to know about it. If an entry for (origin, handler->realm()) already
308 // exists, we update it.
309 //
310 // If identity_.source is HttpAuth::IDENT_SRC_NONE or
311 // HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS, identity_ contains no
312 // identity because identity is not required yet or we're using default
313 // credentials.
314 //
315 // TODO(wtc): For NTLM_SSPI, we add the same auth entry to the cache in
316 // round 1 and round 2, which is redundant but correct. It would be nice
317 // to add an auth entry to the cache only once, preferrably in round 1.
318 // See http://crbug.com/21015.
319 switch (identity_.source) {
320 case HttpAuth::IDENT_SRC_NONE:
321 case HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS:
322 break;
323 default:
324 http_auth_cache_->Add(auth_scheme_host_port_, target_, handler_->realm(),
325 handler_->auth_scheme(), network_anonymization_key_,
326 handler_->challenge(), identity_.credentials,
327 auth_path_);
328 break;
329 }
330 }
331
HaveAuthHandler() const332 bool HttpAuthController::HaveAuthHandler() const {
333 return handler_.get() != nullptr;
334 }
335
HaveAuth() const336 bool HttpAuthController::HaveAuth() const {
337 return handler_.get() && !identity_.invalid;
338 }
339
NeedsHTTP11() const340 bool HttpAuthController::NeedsHTTP11() const {
341 return handler_ && handler_->is_connection_based();
342 }
343
InvalidateCurrentHandler(InvalidateHandlerAction action)344 void HttpAuthController::InvalidateCurrentHandler(
345 InvalidateHandlerAction action) {
346 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
347 DCHECK(handler_.get());
348
349 switch (action) {
350 case INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS:
351 InvalidateRejectedAuthFromCache();
352 break;
353
354 case INVALIDATE_HANDLER_AND_DISABLE_SCHEME:
355 DisableAuthScheme(handler_->auth_scheme());
356 break;
357
358 case INVALIDATE_HANDLER:
359 PrepareIdentityForReuse();
360 break;
361 }
362
363 handler_.reset();
364 identity_ = HttpAuth::Identity();
365 }
366
InvalidateRejectedAuthFromCache()367 void HttpAuthController::InvalidateRejectedAuthFromCache() {
368 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
369 DCHECK(HaveAuth());
370
371 // Clear the cache entry for the identity we just failed on.
372 // Note: we require the credentials to match before invalidating
373 // since the entry in the cache may be newer than what we used last time.
374 http_auth_cache_->Remove(auth_scheme_host_port_, target_, handler_->realm(),
375 handler_->auth_scheme(), network_anonymization_key_,
376 identity_.credentials);
377 }
378
PrepareIdentityForReuse()379 void HttpAuthController::PrepareIdentityForReuse() {
380 if (identity_.invalid)
381 return;
382
383 switch (identity_.source) {
384 case HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS:
385 DCHECK(default_credentials_used_);
386 default_credentials_used_ = false;
387 break;
388
389 case HttpAuth::IDENT_SRC_URL:
390 DCHECK(embedded_identity_used_);
391 embedded_identity_used_ = false;
392 break;
393
394 case HttpAuth::IDENT_SRC_NONE:
395 case HttpAuth::IDENT_SRC_PATH_LOOKUP:
396 case HttpAuth::IDENT_SRC_REALM_LOOKUP:
397 case HttpAuth::IDENT_SRC_EXTERNAL:
398 break;
399 }
400 }
401
SelectNextAuthIdentityToTry()402 bool HttpAuthController::SelectNextAuthIdentityToTry() {
403 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
404 DCHECK(handler_.get());
405 DCHECK(identity_.invalid);
406
407 // Try to use the username:password encoded into the URL first.
408 if (target_ == HttpAuth::AUTH_SERVER && auth_url_.has_username() &&
409 !embedded_identity_used_) {
410 identity_.source = HttpAuth::IDENT_SRC_URL;
411 identity_.invalid = false;
412 // Extract the username:password from the URL.
413 std::u16string username;
414 std::u16string password;
415 GetIdentityFromURL(auth_url_, &username, &password);
416 identity_.credentials.Set(username, password);
417 embedded_identity_used_ = true;
418 // TODO(eroman): If the password is blank, should we also try combining
419 // with a password from the cache?
420 UMA_HISTOGRAM_BOOLEAN("net.HttpIdentSrcURL", true);
421 return true;
422 }
423
424 // Check the auth cache for a realm entry.
425 HttpAuthCache::Entry* entry = http_auth_cache_->Lookup(
426 auth_scheme_host_port_, target_, handler_->realm(),
427 handler_->auth_scheme(), network_anonymization_key_);
428
429 if (entry) {
430 identity_.source = HttpAuth::IDENT_SRC_REALM_LOOKUP;
431 identity_.invalid = false;
432 identity_.credentials = entry->credentials();
433 return true;
434 }
435
436 // Use default credentials (single sign-on) if they're allowed and this is the
437 // first attempt at using an identity. Do not allow multiple times as it will
438 // infinite loop. We use default credentials after checking the auth cache so
439 // that if single sign-on doesn't work, we won't try default credentials for
440 // future transactions.
441 if (!default_credentials_used_ && handler_->AllowsDefaultCredentials()) {
442 identity_.source = HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS;
443 identity_.invalid = false;
444 default_credentials_used_ = true;
445 return true;
446 }
447
448 return false;
449 }
450
PopulateAuthChallenge()451 void HttpAuthController::PopulateAuthChallenge() {
452 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
453
454 // Populates response_.auth_challenge with the authentication challenge info.
455 // This info is consumed by URLRequestHttpJob::GetAuthChallengeInfo().
456
457 auth_info_ = AuthChallengeInfo();
458 auth_info_->is_proxy = (target_ == HttpAuth::AUTH_PROXY);
459 auth_info_->challenger = auth_scheme_host_port_;
460 auth_info_->scheme = HttpAuth::SchemeToString(handler_->auth_scheme());
461 auth_info_->realm = handler_->realm();
462 auth_info_->path = auth_path_;
463 auth_info_->challenge = handler_->challenge();
464 }
465
HandleGenerateTokenResult(int result)466 int HttpAuthController::HandleGenerateTokenResult(int result) {
467 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
468 net_log_.EndEventWithNetErrorCode(NetLogEventType::AUTH_GENERATE_TOKEN,
469 result);
470 switch (result) {
471 // Occurs if the credential handle is found to be invalid at the point it is
472 // exercised (i.e. GenerateAuthToken stage). We are going to consider this
473 // to be an error that invalidates the identity but not necessarily the
474 // scheme. Doing so allows a different identity to be used with the same
475 // scheme. See https://crbug.com/648366.
476 case ERR_INVALID_HANDLE:
477
478 // If the GenerateAuthToken call fails with this error, this means that the
479 // handler can no longer be used. However, the authentication scheme is
480 // considered still usable. This allows a scheme that attempted and failed
481 // to use default credentials to recover and use explicit credentials.
482 //
483 // The current handler may be tied to external state that is no longer
484 // valid, hence should be discarded. Since the scheme is still valid, a new
485 // handler can be created for the current scheme.
486 case ERR_INVALID_AUTH_CREDENTIALS:
487 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
488 auth_token_.clear();
489 return OK;
490
491 // Occurs with GSSAPI, if the user has not already logged in.
492 case ERR_MISSING_AUTH_CREDENTIALS:
493 // Usually, GSSAPI doesn't allow explicit credentials and the scheme
494 // cannot succeed anymore hence it gets disabled. However, on ChromeOS
495 // it's not the case so we invalidate the current handler and can ask for
496 // explicit credentials later. (See b/260522530).
497 if (!handler_->AllowsExplicitCredentials()) {
498 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_DISABLE_SCHEME);
499 } else {
500 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
501 }
502 auth_token_.clear();
503 return OK;
504
505 // Can occur with GSSAPI or SSPI if the underlying library reports
506 // a permanent error.
507 case ERR_UNSUPPORTED_AUTH_SCHEME:
508
509 // These two error codes represent failures we aren't handling.
510 case ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS:
511 case ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS:
512
513 // Can be returned by SSPI if the authenticating authority or
514 // target is not known.
515 case ERR_MISCONFIGURED_AUTH_ENVIRONMENT:
516
517 // In these cases, disable the current scheme as it cannot
518 // succeed.
519 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_DISABLE_SCHEME);
520 auth_token_.clear();
521 return OK;
522
523 default:
524 return result;
525 }
526 }
527
OnGenerateAuthTokenDone(int result)528 void HttpAuthController::OnGenerateAuthTokenDone(int result) {
529 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
530 result = HandleGenerateTokenResult(result);
531 if (!callback_.is_null()) {
532 std::move(callback_).Run(result);
533 }
534 }
535
TakeAuthInfo(absl::optional<AuthChallengeInfo> * other)536 void HttpAuthController::TakeAuthInfo(
537 absl::optional<AuthChallengeInfo>* other) {
538 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
539 auth_info_.swap(*other);
540 }
541
IsAuthSchemeDisabled(HttpAuth::Scheme scheme) const542 bool HttpAuthController::IsAuthSchemeDisabled(HttpAuth::Scheme scheme) const {
543 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
544 return disabled_schemes_.find(scheme) != disabled_schemes_.end();
545 }
546
DisableAuthScheme(HttpAuth::Scheme scheme)547 void HttpAuthController::DisableAuthScheme(HttpAuth::Scheme scheme) {
548 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
549 disabled_schemes_.insert(scheme);
550 }
551
DisableEmbeddedIdentity()552 void HttpAuthController::DisableEmbeddedIdentity() {
553 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
554 embedded_identity_used_ = true;
555 }
556
OnConnectionClosed()557 void HttpAuthController::OnConnectionClosed() {
558 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
559 InvalidateCurrentHandler(INVALIDATE_HANDLER);
560 }
561
562 } // namespace net
563