public static AuthenticationChallenge GetChallenge(HttpPipelineMessage message)
            {
                AuthenticationChallenge challenge = null;

                if (message.HasResponse)
                {
                    challenge = GetChallengeFromResponse(message.Response);

                    // if the challenge is non-null cache it
                    if (challenge != null)
                    {
                        lock (_cacheLock)
                        {
                            _cache[GetRequestAuthority(message.Request)] = challenge;
                        }
                    }
                }
                else
                {
                    // try to get the challenge from the cache
                    lock (_cacheLock)
                    {
                        _cache.TryGetValue(GetRequestAuthority(message.Request), out challenge);
                    }
                }

                return(challenge);
            }
 internal AuthenticationResponse (
   AuthenticationChallenge challenge,
   NetworkCredential credentials,
   uint nonceCount)
   : this (challenge.Scheme, challenge.Params, credentials, nonceCount)
 {
 }
            public static AuthenticationChallenge GetChallenge(HttpMessage message)
            {
                AuthenticationChallenge challenge = null;

                if (message.HasResponse)
                {
                    challenge = GetChallengeFromResponse(message.Response);

                    // if the challenge is non-null cache it
                    if (challenge != null)
                    {
                        string authority = GetRequestAuthority(message.Request);
                        lock (s_cacheLock)
                        {
                            s_cache[authority] = challenge;
                        }
                    }
                }
                else
                {
                    // try to get the challenge from the cache
                    string authority = GetRequestAuthority(message.Request);
                    lock (s_cacheLock)
                    {
                        s_cache.TryGetValue(authority, out challenge);
                    }
                }

                return(challenge);
            }
示例#4
0
        private static bool TryGetValidAuthenticationChallengeForScheme(string scheme, AuthenticationType authenticationType, Uri uri, ICredentials credentials,
                                                                        HttpHeaderValueCollection <AuthenticationHeaderValue> authenticationHeaderValues, out AuthenticationChallenge challenge)
        {
            challenge = default;

            if (!TryGetChallengeDataForScheme(scheme, authenticationHeaderValues, out string?challengeData))
            {
                return(false);
            }

            NetworkCredential?credential = credentials.GetCredential(uri, scheme);

            if (credential == null)
            {
                // We have no credential for this auth type, so we can't respond to the challenge.
                // We'll continue to look for a different auth type that we do have a credential for.
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.AuthenticationInfo(uri, $"Authentication scheme '{scheme}' supported by server, but not by client.");
                }
                return(false);
            }

            challenge = new AuthenticationChallenge(authenticationType, scheme, credential, challengeData);
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.AuthenticationInfo(uri, $"Authentication scheme '{scheme}' selected. Client username={challenge.Credential.UserName}");
            }
            return(true);
        }
        internal bool Authenticate(AuthenticationSchemes scheme, string realm, Func <IIdentity, NetworkCredential> credentialsFinder)
        {
            var chal = new AuthenticationChallenge(scheme, realm).ToString();

            var retry = -1;

            bool auth()
            {
                retry++;
                if (retry > 99)
                {
                    return(false);
                }

                var user = HttpUtility.CreateUser(
                    _request.Headers["Authorization"],
                    scheme,
                    realm,
                    _request.HttpMethod,
                    credentialsFinder
                    );

                if (user != null && user.Identity.IsAuthenticated)
                {
                    _user = user;
                    return(true);
                }

                _request = sendAuthenticationChallenge(chal);
                return(auth());
            }

            return(auth());
        }
        private async Task ProcessCoreAsync(HttpPipelineMessage message, ReadOnlyMemory <HttpPipelinePolicy> pipeline, bool async)
        {
            HttpPipelineRequestContent originalContent = message.Request.Content;

            // if this policy doesn't have _challenge cached try to get it from the static challenge cache
            _challenge ??= AuthenticationChallenge.GetChallenge(message);

            // if we still don't have the challenge for the endpoint
            // remove the content from the request and send without authentication to get the challenge
            if (_challenge == null)
            {
                message.Request.Content = null;
            }
            // otherwise if we already know the challenge authenticate the request
            else
            {
                await AuthenticateRequestAsync(message, async).ConfigureAwait(false);
            }

            if (async)
            {
                await ProcessNextAsync(message, pipeline).ConfigureAwait(false);
            }
            else
            {
                ProcessNext(message, pipeline);
            }

            // if we get a 401
            if (message.Response.Status == 401)
            {
                // set the content to the original content in case it was cleared
                message.Request.Content = originalContent;

                // update the cached challenge
                var challenge = AuthenticationChallenge.GetChallenge(message);

                // if a challenge was returned and it's different from the cached _challenge
                if (challenge != null && !challenge.Equals(_challenge))
                {
                    // update the cached challenge
                    _challenge = challenge;

                    // authenticate the request and resend
                    await AuthenticateRequestAsync(message, async).ConfigureAwait(false);

                    if (async)
                    {
                        await ProcessNextAsync(message, pipeline).ConfigureAwait(false);
                    }
                    else
                    {
                        ProcessNext(message, pipeline);
                    }
                }
            }
        }
        internal bool Authenticate(
            AuthenticationSchemes scheme,
            string realm,
            Func <IIdentity, NetworkCredential> credentialsFinder
            )
        {
            if (scheme == AuthenticationSchemes.Anonymous)
            {
                return(true);
            }

            if (scheme == AuthenticationSchemes.None)
            {
                Close(HttpStatusCode.Forbidden);
                return(false);
            }

            var chal = new AuthenticationChallenge(scheme, realm).ToString();

            var         retry = -1;
            Func <bool> auth  = null;

            auth =
                () =>
            {
                retry++;
                if (retry > 99)
                {
                    Close(HttpStatusCode.Forbidden);
                    return(false);
                }

#if !NETCF || BCC || SSL
                var user =
                    HttpUtility.CreateUser(
                        _request.Headers["Authorization"],
                        scheme,
                        realm,
                        _request.HttpMethod,
                        credentialsFinder
                        );

                if (user != null && user.Identity.IsAuthenticated)
                {
                    _user = user;
                    return(true);
                }
#endif

                SendAuthenticationChallenge(chal);
                return(auth());
            };

            return(auth());
        }
            private static AuthenticationChallenge GetChallengeFromResponse(Response response)
            {
                AuthenticationChallenge challenge = null;

                if (response.Headers.TryGetValue("WWW-Authenticate", out string challengeValue) && challengeValue.StartsWith(BearerChallengePrefix, StringComparison.OrdinalIgnoreCase))
                {
                    challenge = ParseBearerChallengeHeaderValue(challengeValue);
                }

                return(challenge);
            }
        private async Task <bool> AuthenticateRequest(AuthenticationSchemes scheme, TcpListenerWebSocketContext context)
        {
            var chal = scheme == AuthenticationSchemes.Basic
                       ? AuthenticationChallenge.CreateBasicChallenge(Realm).ToBasicString()
                       : scheme == AuthenticationSchemes.Digest
                         ? AuthenticationChallenge.CreateDigestChallenge(Realm).ToDigestString()
                         : null;

            if (chal == null)
            {
                await context.Close(HttpStatusCode.Forbidden).ConfigureAwait(false);

                return(false);
            }

            var retry                = -1;
            var schm                 = scheme.ToString();
            var realm                = Realm;
            var credFinder           = UserCredentialsFinder;
            Func <Task <bool> > auth = () => Task.FromResult(false);

            auth = async() =>
            {
                var auth1 = auth;
                retry++;
                if (retry > 99)
                {
                    await context.Close(HttpStatusCode.Forbidden).ConfigureAwait(false);

                    return(false);
                }

                var res = await context.GetHeader("Authorization").ConfigureAwait(false);

                if (res == null || !res.StartsWith(schm, StringComparison.OrdinalIgnoreCase))
                {
                    context.SendAuthenticationChallenge(chal);
                    return(await auth1().ConfigureAwait(false));
                }

                await context.SetUser(scheme, realm, credFinder).ConfigureAwait(false);

                if (!context.IsAuthenticated)
                {
                    context.SendAuthenticationChallenge(chal);
                    return(await auth1().ConfigureAwait(false));
                }

                return(true);
            };

            return(await auth().ConfigureAwait(false));
        }
        private async Task AuthenticateRequestAsync(HttpMessage message, bool async, AuthenticationChallenge challenge)
        {
            if (_headerValue is null || DateTimeOffset.UtcNow >= _refreshOn)
            {
                AccessToken token = async ?
                                    await _credential.GetTokenAsync(new TokenRequestContext(challenge.Scopes, message.Request.ClientRequestId), message.CancellationToken).ConfigureAwait(false) :
                                    _credential.GetToken(new TokenRequestContext(challenge.Scopes, message.Request.ClientRequestId), message.CancellationToken);

                _headerValue = BearerChallengePrefix + token.Token;
                _refreshOn   = token.ExpiresOn - TimeSpan.FromMinutes(2);
            }

            message.Request.Headers.SetValue(HttpHeader.Names.Authorization, _headerValue);
        }
示例#11
0
        private bool authenticateRequest(
            AuthenticationSchemes scheme, TcpListenerWebSocketContext context)
        {
            var chal = scheme == AuthenticationSchemes.Basic
                 ? AuthenticationChallenge.CreateBasicChallenge(Realm).ToBasicString()
                 : scheme == AuthenticationSchemes.Digest
                   ? AuthenticationChallenge.CreateDigestChallenge(Realm).ToDigestString()
                   : null;

            if (chal == null)
            {
                context.Close(HttpStatusCode.Forbidden);
                return(false);
            }

            var         retry      = -1;
            var         schm       = scheme.ToString();
            var         realm      = Realm;
            var         credFinder = UserCredentialsFinder;
            Func <bool> auth       = null;

            auth = () => {
                retry++;
                if (retry > 99)
                {
                    context.Close(HttpStatusCode.Forbidden);
                    return(false);
                }

                var res = context.Headers["Authorization"];
                if (res == null || !res.StartsWith(schm, StringComparison.OrdinalIgnoreCase))
                {
                    context.SendAuthenticationChallenge(chal);
                    return(auth());
                }

                context.SetUser(scheme, realm, credFinder);
                if (!context.IsAuthenticated)
                {
                    context.SendAuthenticationChallenge(chal);
                    return(auth());
                }

                return(true);
            };

            return(auth());
        }
示例#12
0
        private static bool authenticate(
            TcpListenerWebSocketContext context,
            AuthenticationSchemes scheme,
            string realm,
            Func <IIdentity, NetworkCredential> credentialsFinder)
        {
            var chal = scheme == AuthenticationSchemes.Basic
                                           ? AuthenticationChallenge.CreateBasicChallenge(realm).ToBasicString()
                                           : scheme == AuthenticationSchemes.Digest
                                                 ? AuthenticationChallenge.CreateDigestChallenge(realm).ToDigestString()
                                                 : null;

            if (chal == null)
            {
                context.Close(HttpStatusCode.Forbidden);
                return(false);
            }

            var         retry = -1;
            Func <bool> auth  = null;

            auth = () =>
            {
                retry++;
                if (retry > 99)
                {
                    context.Close(HttpStatusCode.Forbidden);
                    return(false);
                }

                var user = HttpUtility.CreateUser(
                    context.Headers["Authorization"], scheme, realm, context.HttpMethod, credentialsFinder);

                if (user != null && user.Identity.IsAuthenticated)
                {
                    context.SetUser(user);
                    return(true);
                }

                context.SendAuthenticationChallenge(chal);
                return(auth());
            };

            return(auth());
        }
            public override bool Equals(object obj)
            {
                if (base.Equals(obj))
                {
                    return(true);
                }

                AuthenticationChallenge other = obj as AuthenticationChallenge;

                // This assumes that Scopes is always non-null and of length one.
                // This is guaranteed by the way the AuthenticationChallenge cache is constructued.
                if (other != null)
                {
                    return(string.Equals(this.Scopes[0], other.Scopes[0], StringComparison.OrdinalIgnoreCase));
                }

                return(false);
            }
            private static AuthenticationChallenge ParseBearerChallengeHeaderValue(string challengeValue)
            {
                AuthenticationChallenge challenge = null;

                // remove the bearer challenge prefix
                var trimmedChallenge = challengeValue.Substring(BearerChallengePrefix.Length + 1);

                // Split the trimmed challenge into a set of name=value strings that
                // are comma separated. The value fields are expected to be within
                // quotation characters that are stripped here.
                String[] pairs = trimmedChallenge.Split(new String[] { "," }, StringSplitOptions.RemoveEmptyEntries);

                if (pairs.Length > 0)
                {
                    // Process the name=value string
                    for (int i = 0; i < pairs.Length; i++)
                    {
                        String[] pair = pairs[i].Split('=');

                        if (pair.Length == 2)
                        {
                            // We have a key and a value, now need to trim and decode
                            String key   = pair[0].Trim().Trim(new char[] { '\"' });
                            String value = pair[1].Trim().Trim(new char[] { '\"' });

                            if (!string.IsNullOrEmpty(key))
                            {
                                if (string.Equals(key, "scope", StringComparison.OrdinalIgnoreCase))
                                {
                                    challenge = new AuthenticationChallenge(value);

                                    break;
                                }
                                else if (string.Equals(key, "resource", StringComparison.OrdinalIgnoreCase))
                                {
                                    challenge = new AuthenticationChallenge(value + "/.default");
                                }
                            }
                        }
                    }
                }

                return(challenge);
            }
示例#15
0
        private static bool TryGetValidAuthenticationChallengeForScheme(string scheme, AuthenticationType authenticationType, Uri uri, ICredentials credentials,
                                                                        HttpHeaderValueCollection <AuthenticationHeaderValue> authenticationHeaderValues, out AuthenticationChallenge challenge)
        {
            challenge = default;

            if (!TryGetChallengeDataForScheme(scheme, authenticationHeaderValues, out string challengeData))
            {
                return(false);
            }

            NetworkCredential credential = credentials.GetCredential(uri, scheme);

            if (credential == null)
            {
                // We have no credential for this auth type, so we can't respond to the challenge.
                // We'll continue to look for a different auth type that we do have a credential for.
                return(false);
            }

            challenge = new AuthenticationChallenge(authenticationType, scheme, credential, challengeData);
            return(true);
        }
示例#16
0
        private bool authenticateRequest(AuthenticationSchemes scheme, HttpListenerContext context)
        {
            if (context.Request.IsAuthenticated)
            {
                return(true);
            }

            if (scheme == AuthenticationSchemes.Basic)
            {
                context.Response.CloseWithAuthChallenge(
                    AuthenticationChallenge.CreateBasicChallenge(_listener.Realm).ToBasicString());
            }
            else if (scheme == AuthenticationSchemes.Digest)
            {
                context.Response.CloseWithAuthChallenge(
                    AuthenticationChallenge.CreateDigestChallenge(_listener.Realm).ToDigestString());
            }
            else
            {
                context.Response.Close(HttpStatusCode.Forbidden);
            }

            return(false);
        }
示例#17
0
        private static bool TryGetAuthenticationChallenge(HttpResponseMessage response, bool isProxyAuth, Uri authUri, ICredentials credentials, out AuthenticationChallenge challenge)
        {
            challenge = default;

            if (!IsAuthenticationChallenge(response, isProxyAuth))
            {
                return(false);
            }

            HttpHeaderValueCollection <AuthenticationHeaderValue> authenticationHeaderValues = GetResponseAuthenticationHeaderValues(response, isProxyAuth);

            // Try to get a valid challenge for the schemes we support, in priority order.
            if (!TryGetValidAuthenticationChallengeForScheme(NegotiateScheme, AuthenticationType.Negotiate, authUri, credentials, authenticationHeaderValues, out challenge) &&
                !TryGetValidAuthenticationChallengeForScheme(NtlmScheme, AuthenticationType.Ntlm, authUri, credentials, authenticationHeaderValues, out challenge) &&
                !TryGetValidAuthenticationChallengeForScheme(DigestScheme, AuthenticationType.Digest, authUri, credentials, authenticationHeaderValues, out challenge) &&
                !TryGetValidAuthenticationChallengeForScheme(BasicScheme, AuthenticationType.Basic, authUri, credentials, authenticationHeaderValues, out challenge))
            {
                return(false);
            }

            return(true);
        }
    internal bool Authenticate (
      AuthenticationSchemes scheme,
      string realm,
      Func<IIdentity, NetworkCredential> credentialsFinder
    )
    {
      if (scheme == AuthenticationSchemes.Anonymous)
        return true;

      if (scheme != AuthenticationSchemes.Basic && scheme != AuthenticationSchemes.Digest) {
        Close (HttpStatusCode.Forbidden);
        return false;
      }

      var chal = new AuthenticationChallenge (scheme, realm).ToString ();

      var retry = -1;
      Func<bool> auth = null;
      auth =
        () => {
          retry++;
          if (retry > 99) {
            Close (HttpStatusCode.Forbidden);
            return false;
          }

          var user =
            HttpUtility.CreateUser (
              _request.Headers["Authorization"],
              scheme,
              realm,
              _request.HttpMethod,
              credentialsFinder
            );

          if (user == null || !user.Identity.IsAuthenticated) {
            SendAuthenticationChallenge (chal);
            return auth ();
          }

          _user = user;
          return true;
        };

      return auth ();
    }
示例#19
0
        private static bool TryGetAuthenticationChallenge(HttpResponseMessage response, bool isProxyAuth, Uri authUri, ICredentials credentials, out AuthenticationChallenge challenge)
        {
            if (!IsAuthenticationChallenge(response, isProxyAuth))
            {
                challenge = default;
                return(false);
            }

            // Try to get a valid challenge for the schemes we support, in priority order.
            HttpHeaderValueCollection <AuthenticationHeaderValue> authenticationHeaderValues = GetResponseAuthenticationHeaderValues(response, isProxyAuth);

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.AuthenticationInfo(authUri, $"{(isProxyAuth ? "Proxy" : "Server")} authentication requested with WWW-Authenticate header value '{authenticationHeaderValues}'");
            }
            return
                (TryGetValidAuthenticationChallengeForScheme(NegotiateScheme, AuthenticationType.Negotiate, authUri, credentials, authenticationHeaderValues, out challenge) ||
                 TryGetValidAuthenticationChallengeForScheme(NtlmScheme, AuthenticationType.Ntlm, authUri, credentials, authenticationHeaderValues, out challenge) ||
                 TryGetValidAuthenticationChallengeForScheme(DigestScheme, AuthenticationType.Digest, authUri, credentials, authenticationHeaderValues, out challenge) ||
                 TryGetValidAuthenticationChallengeForScheme(BasicScheme, AuthenticationType.Basic, authUri, credentials, authenticationHeaderValues, out challenge));
        }
        private async ValueTask ProcessCoreAsync(HttpMessage message, ReadOnlyMemory <HttpPipelinePolicy> pipeline, bool async)
        {
            if (message.Request.Uri.Scheme != Uri.UriSchemeHttps)
            {
                throw new InvalidOperationException("Bearer token authentication is not permitted for non TLS protected (https) endpoints.");
            }

            RequestContent originalContent = message.Request.Content;

            // if this policy doesn't have _challenge cached try to get it from the static challenge cache
            AuthenticationChallenge challenge = _challenge ?? AuthenticationChallenge.GetChallenge(message);

            // if we still don't have the challenge for the endpoint
            // remove the content from the request and send without authentication to get the challenge
            if (challenge == null)
            {
                message.Request.Content = null;
            }
            // otherwise if we already know the challenge authenticate the request
            else
            {
                await AuthenticateRequestAsync(message, async, challenge).ConfigureAwait(false);
            }

            if (async)
            {
                await ProcessNextAsync(message, pipeline).ConfigureAwait(false);
            }
            else
            {
                ProcessNext(message, pipeline);
            }

            // if we get a 401
            if (message.Response.Status == 401)
            {
                // set the content to the original content in case it was cleared
                message.Request.Content = originalContent;

                // update the cached challenge
                challenge = AuthenticationChallenge.GetChallenge(message);

                if (challenge != null)
                {
                    // update the cached challenge if not yet set or different from the current challenge (e.g. moved tenants)
                    if (_challenge == null || !challenge.Equals(_challenge))
                    {
                        _challenge = challenge;
                    }

                    // authenticate the request and resend
                    await AuthenticateRequestAsync(message, async, challenge).ConfigureAwait(false);

                    if (async)
                    {
                        await ProcessNextAsync(message, pipeline).ConfigureAwait(false);
                    }
                    else
                    {
                        ProcessNext(message, pipeline);
                    }
                }
            }
        }
示例#21
0
        private static bool authenticate(TcpListenerWebSocketContext context, WebSocketSharp.Net.AuthenticationSchemes scheme, string realm, Func <IIdentity, WebSocketSharp.Net.NetworkCredential> credentialsFinder)
        {
            string chal = ((scheme == WebSocketSharp.Net.AuthenticationSchemes.Basic) ? AuthenticationChallenge.CreateBasicChallenge(realm).ToBasicString() : ((scheme != WebSocketSharp.Net.AuthenticationSchemes.Digest) ? null : AuthenticationChallenge.CreateDigestChallenge(realm).ToDigestString()));

            if (chal == null)
            {
                context.Close(WebSocketSharp.Net.HttpStatusCode.Forbidden);
                return(false);
            }
            int         retry = -1;
            Func <bool> auth  = null;

            auth = delegate
            {
                retry++;
                if (retry > 99)
                {
                    context.Close(WebSocketSharp.Net.HttpStatusCode.Forbidden);
                    return(false);
                }
                IPrincipal principal = HttpUtility.CreateUser(context.Headers["Authorization"], scheme, realm, context.HttpMethod, credentialsFinder);
                if (principal != null && principal.Identity.IsAuthenticated)
                {
                    context.SetUser(principal);
                    return(true);
                }
                context.SendAuthenticationChallenge(chal);
                return(auth());
            };
            return(auth());
        }
示例#22
0
        // As client
        private HandshakeResponse sendHandshakeRequest()
        {
            var req = createHandshakeRequest ();
              var res = sendHandshakeRequest (req);
              if (res.IsUnauthorized) {
            _authChallenge = res.AuthChallenge;
            if (_credentials != null && (!_preAuth || _authChallenge.Scheme == "digest")) {
              if (res.Headers.Contains ("Connection", "close")) {
            closeClientResources ();
            setClientStream ();
              }

              var authRes = new AuthenticationResponse (_authChallenge, _credentials, _nonceCount);
              _nonceCount = authRes.NonceCount;
              req.Headers ["Authorization"] = authRes.ToString ();
              res = sendHandshakeRequest (req);
            }
              }

              return res;
        }
示例#23
0
    // As client
    private HttpResponse sendHandshakeRequest ()
    {
      var req = createHandshakeRequest ();
      var res = sendHttpRequest (req, 90000);
      if (res.IsUnauthorized) {
        var chal = res.Headers["WWW-Authenticate"];
        _logger.Warn (String.Format ("Received an authentication requirement for '{0}'.", chal));
        if (chal.IsNullOrEmpty ()) {
          _logger.Error ("No authentication challenge is specified.");
          return res;
        }

        _authChallenge = AuthenticationChallenge.Parse (chal);
        if (_authChallenge == null) {
          _logger.Error ("An invalid authentication challenge is specified.");
          return res;
        }

        if (_credentials != null &&
            (!_preAuth || _authChallenge.Scheme == AuthenticationSchemes.Digest)) {
          if (res.HasConnectionClose) {
            releaseClientResources ();
            setClientStream ();
          }

          var authRes = new AuthenticationResponse (_authChallenge, _credentials, _nonceCount);
          _nonceCount = authRes.NonceCount;
          req.Headers["Authorization"] = authRes.ToString ();
          res = sendHttpRequest (req, 15000);
        }
      }

      if (res.IsRedirect) {
        var url = res.Headers["Location"];
        _logger.Warn (String.Format ("Received a redirection to '{0}'.", url));
        if (_enableRedirection) {
          if (url.IsNullOrEmpty ()) {
            _logger.Error ("No url to redirect is located.");
            return res;
          }

          Uri uri;
          string msg;
          if (!url.TryCreateWebSocketUri (out uri, out msg)) {
            _logger.Error ("An invalid url to redirect is located: " + msg);
            return res;
          }

          releaseClientResources ();

          _uri = uri;
          _secure = uri.Scheme == "wss";

          setClientStream ();
          return sendHandshakeRequest ();
        }
      }

      return res;
    }
示例#24
0
        // As client
        private HttpResponse SendHandshakeRequest()
        {
            var req = CreateHandshakeRequest();
            var res = SendHttpRequest(req, 90000);

            if (res.IsUnauthorized)
            {
#if AUTHENTICATION
                var chal = res.Headers["WWW-Authenticate"];
                Log.Warn(String.Format("Received an authentication requirement for '{0}'.", chal));
                if (chal.IsNullOrEmpty())
                {
                    Log.Error("No authentication challenge is specified.");
                    return res;
                }

                _authChallenge = AuthenticationChallenge.Parse(chal);
                if (_authChallenge == null)
                {
                    Log.Error("An invalid authentication challenge is specified.");
                    return res;
                }

                if (_credentials != null &&
                    (!_preAuth || _authChallenge.Scheme == AuthenticationSchemes.Digest))
                {
                    if (res.HasConnectionClose)
                    {
                        releaseClientResources();
                        setClientStream();
                    }

                    var authRes = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount);
                    _nonceCount = authRes.NonceCount;
                    req.Headers["Authorization"] = authRes.ToString();
                    res = sendHttpRequest(req, 15000);
                }
#else
                throw new InvalidOperationException("Authentication is not supported");
#endif
            }


            if (res.IsRedirect)
            {
                var url = res.Headers["Location"];
#if COMPAT
                Log.WarnFormat("Received a redirection to '{0}'.", url);
#else
                $"Received a redirection to '{url}'.".Warn();
#endif

                if (_enableRedirection)
                {
                    if (string.IsNullOrEmpty(url))
                    {
#if COMPAT
                        Log.Error("No url to redirect is located.");
#else
                        "No url to redirect is located.".Error();
#endif
                        return res;
                    }

                    Uri uri;
                    string msg;
                    if (!url.TryCreateWebSocketUri(out uri, out msg))
                    {
#if COMPAT
                        Log.Error("An invalid url to redirect is located: " + msg);
#endif
                        return res;
                    }

                    ReleaseClientResources();

                    _uri = uri;
                    IsSecure = uri.Scheme == "wss";

                    SetClientStream();
                    return SendHandshakeRequest();
                }
            }

            return res;
        }