/// <summary>
        /// Returns the string representation of the bewit, which is a base64 URL encoded string of format
        /// id\exp\mac\ext, where id is the user identifier, exp is the UNIX time until which bewit is
        /// valid, mac is the HMAC of the bewit to protect integrity, and ext is the application specific data.
        /// </summary>
        public string ToBewitString()
        {
            if (request.Method != HttpMethod.Get) // Not supporting HEAD
                throw new InvalidOperationException("Bewit not allowed for methods other than GET");

            ulong now = utcNow.ToUnixTime() + Convert.ToUInt64(this.localOffset);

            var artifacts = new ArtifactsContainer()
            {
                Id = credential.Id,
                Timestamp = now + (ulong)lifeSeconds,
                Nonce = String.Empty,
                ApplicationSpecificData = this.applicationSpecificData ?? String.Empty
            };

            var normalizedRequest = new NormalizedRequest(request, artifacts) { IsBewit = true };
            var crypto = new Cryptographer(normalizedRequest, artifacts, credential);

            // Sign the request
            crypto.Sign(); // Bewit is for GET and GET must have no request body

            // bewit: id\exp\mac\ext
            string bewit = String.Format(@"{0}\{1}\{2}\{3}",
                                credential.Id,
                                artifacts.Timestamp,
                                artifacts.Mac.ToBase64String(),
                                artifacts.ApplicationSpecificData);

            return bewit.ToBytesFromUtf8().ToBase64UrlString();
        }
 internal Cryptographer(NormalizedRequest request, ArtifactsContainer artifacts, Credential credential)
 {
     this.normalizedRequest = request;
     this.artifacts = artifacts;
     this.credential = credential;
     this.hasher = new Hasher(credential.Algorithm);
 }
Beispiel #3
0
 internal Cryptographer(NormalizedRequest request, ArtifactsContainer artifacts, Credential credential)
 {
     this.normalizedRequest = request;
     this.artifacts         = artifacts;
     this.credential        = credential;
     this.hasher            = new Hasher(credential.Algorithm);
 }
        /// <summary>
        /// Returns the string representation of the bewit, which is a base64 URL encoded string of format
        /// id\exp\mac\ext, where id is the user identifier, exp is the UNIX time until which bewit is
        /// valid, mac is the HMAC of the bewit to protect integrity, and ext is the application specific data.
        /// </summary>
        public async Task<string> ToBewitStringAsync()
        {
            if (request.Method != HttpMethod.Get) // Not supporting HEAD
                throw new InvalidOperationException("Bewit not allowed for methods other than GET");

            ulong now = utcNow.ToUnixTime() +
                                UInt64.Parse(ConfigurationManager.AppSettings["LocalTimeOffsetMillis"]);

            var artifacts = new ArtifactsContainer()
            {
                Id = credential.Id,
                Timestamp = now + (ulong)lifeSeconds,
                Nonce = String.Empty,
                ApplicationSpecificData = this.applicationSpecificData ?? String.Empty
            };

            var normalizedRequest = new NormalizedRequest(request, artifacts) { IsBewit = true };
            var crypto = new Cryptographer(normalizedRequest, artifacts, credential);

            // Sign the request
            await crypto.SignAsync(null);

            // bewit: id\exp\mac\ext
            string bewit = String.Format(@"{0}\{1}\{2}\{3}",
                                credential.Id,
                                artifacts.Timestamp,
                                artifacts.Mac.ToBase64String(),
                                artifacts.ApplicationSpecificData);

            return bewit.ToBytesFromUtf8().ToBase64UrlString();
        }
        /// <summary>
        /// Returns the string representation of the bewit, which is a base64 URL encoded string of format
        /// id\exp\mac\ext, where id is the user identifier, exp is the UNIX time until which bewit is
        /// valid, mac is the HMAC of the bewit to protect integrity, and ext is the application specific data.
        /// </summary>
        public string ToBewitString()
        {
            if (request.Method != HttpMethod.Get) // Not supporting HEAD
            {
                throw new InvalidOperationException("Bewit not allowed for methods other than GET");
            }

            ulong now = utcNow.ToUnixTime() + Convert.ToUInt64(this.localOffset);

            var artifacts = new ArtifactsContainer()
            {
                Id        = credential.Id,
                Timestamp = now + (ulong)lifeSeconds,
                Nonce     = String.Empty,
                ApplicationSpecificData = this.applicationSpecificData ?? String.Empty
            };

            var normalizedRequest = new NormalizedRequest(request, artifacts)
            {
                IsBewit = true
            };
            var crypto = new Cryptographer(normalizedRequest, artifacts, credential);

            // Sign the request
            crypto.Sign(); // Bewit is for GET and GET must have no request body

            // bewit: id\exp\mac\ext
            string bewit = String.Format(@"{0}\{1}\{2}\{3}",
                                         credential.Id,
                                         artifacts.Timestamp,
                                         artifacts.Mac.ToBase64String(),
                                         artifacts.ApplicationSpecificData);

            return(bewit.ToBytesFromUtf8().ToBase64UrlString());
        }
        /// <summary>
        /// Adds the WWW-Authenticate header in case of a 401 - Unauthorized response and
        /// the Server-Authorization header in case of a successful request.
        /// </summary>
        public async Task CreateServerAuthorizationAsync(HttpResponseMessage response, Func <HttpResponseMessage, string> normalizationCallback)
        {
            if (response.StatusCode == HttpStatusCode.Unauthorized)
            {
                var header = new AuthenticationHeaderValue(HawkConstants.Scheme, request.GetChallengeParameter());
                response.Headers.WwwAuthenticate.Add(header);
            }
            else
            {
                if (this.result != null && this.result.IsAuthentic && !this.isBewitRequest) // No Server-Authorization header for bewit requests
                {
                    if (normalizationCallback != null)
                    {
                        this.result.Artifacts.ApplicationSpecificData = normalizationCallback(response);
                    }

                    // Sign the response
                    var normalizedRequest = new NormalizedRequest(request, this.result.Artifacts);
                    var crypto            = new Cryptographer(normalizedRequest, this.result.Artifacts, this.result.Credential);
                    await crypto.SignAsync(response.Content);

                    string authorization = this.result.Artifacts.ToServerAuthorizationHeaderParameter();

                    if (!String.IsNullOrWhiteSpace(authorization))
                    {
                        response.Headers.Add(HawkConstants.ServerAuthorizationHeaderName,
                                             HawkConstants.Scheme + " " + authorization);
                    }
                }
            }
        }
        /// <summary>
        /// Returns the name of the response header (WWW-Authenticate or Server-Authorization) and the corresponding
        /// value, respectively for an unauthorized and a successful request.
        /// </summary>
        public async Task <Tuple <string, string> > CreateServerAuthorizationAsync(IResponseMessage response)
        {
            if (response.StatusCode == HttpStatusCode.Unauthorized)
            {
                string challenge   = String.Format(" {0}", request.ChallengeParameter ?? String.Empty);
                string headerValue = HawkConstants.Scheme + challenge.TrimEnd(' ');

                return(new Tuple <string, string>(HawkConstants.WwwAuthenticateHeaderName, headerValue));
            }
            else
            {
                // No Server-Authorization header for the following:
                // (1) There is no result or failed authentication.
                // (2) The credential is a bewit.
                // (3) The server is configured to not send the header.
                bool createHeader = this.result != null &&
                                    this.result.IsAuthentic &&
                                    (!this.isBewitRequest) &&
                                    options.EnableServerAuthorization;

                if (createHeader)
                {
                    if (options.NormalizationCallback != null)
                    {
                        this.result.Artifacts.ApplicationSpecificData = options.NormalizationCallback(response);
                    }

                    // Sign the response
                    var normalizedRequest = new NormalizedRequest(request, this.result.Artifacts)
                    {
                        IsServerAuthorization = true
                    };
                    var crypto = new Cryptographer(normalizedRequest, this.result.Artifacts, this.result.Credential);

                    // Response body is needed only if payload hash must be included in the response MAC.
                    string body = null;
                    if (options.ResponsePayloadHashabilityCallback != null &&
                        options.ResponsePayloadHashabilityCallback(this.request))
                    {
                        body = await response.ReadBodyAsStringAsync();
                    }

                    crypto.Sign(body, response.ContentType);

                    string authorization = this.result.Artifacts.ToServerAuthorizationHeaderParameter();

                    if (!String.IsNullOrWhiteSpace(authorization))
                    {
                        return(new Tuple <string, string>(HawkConstants.ServerAuthorizationHeaderName,
                                                          String.Format("{0} {1}", HawkConstants.Scheme, authorization)));
                    }
                }
            }

            return(null);
        }
Beispiel #8
0
        /// <summary>
        /// Returns an AuthenticationResult object corresponding to the result of authentication done
        /// using the client supplied artifacts in the HTTP authorization header in hawk scheme.
        /// </summary>
        /// <param name="now">Current UNIX time in milliseconds.</param>
        /// <param name="request">Request object.</param>
        /// <param name="options">Hawk authentication options</param>
        /// <returns></returns>
        internal static async Task <AuthenticationResult> AuthenticateAsync(ulong now, IRequestMessage request, Options options)
        {
            ArtifactsContainer artifacts  = null;
            Credential         credential = null;

            if (request.HasValidHawkScheme())
            {
                if (ArtifactsContainer.TryParse(request.Authorization.Parameter, out artifacts))
                {
                    if (artifacts != null && artifacts.AreClientArtifactsValid)
                    {
                        credential = options.CredentialsCallback(artifacts.Id);
                        if (credential != null && credential.IsValid)
                        {
                            var normalizedRequest = new NormalizedRequest(request, artifacts);
                            var crypto            = new Cryptographer(normalizedRequest, artifacts, credential);

                            // Request body is needed only when payload hash is present in the request
                            string body = null;
                            if (artifacts.PayloadHash != null && artifacts.PayloadHash.Length > 0)
                            {
                                body = await request.ReadBodyAsStringAsync();
                            }

                            if (crypto.IsSignatureValid(body, request.ContentType)) // MAC and hash checks
                            {
                                if (IsTimestampFresh(now, artifacts, options))
                                {
                                    // If you get this far, you are authentic. Welcome and thanks for flying Hawk!
                                    return(new AuthenticationResult()
                                    {
                                        IsAuthentic = true,
                                        Artifacts = artifacts,
                                        Credential = credential,
                                        ApplicationSpecificData = artifacts.ApplicationSpecificData
                                    });
                                }
                                else
                                {
                                    // Authentic but for the timestamp freshness.
                                    // Give a chance to the client to correct the clocks skew.
                                    var timestamp = new NormalizedTimestamp(DateTime.UtcNow, credential, options.LocalTimeOffsetMillis);
                                    request.ChallengeParameter = timestamp.ToWwwAuthenticateHeaderParameter();
                                }
                            }
                        }
                    }
                }
            }

            return(new AuthenticationResult()
            {
                IsAuthentic = false
            });
        }
        /// <summary>
        /// Returns an AuthenticationResult object corresponding to the result of authentication done
        /// using the client supplied artifacts in the HTTP authorization header in hawk scheme.
        /// </summary>
        /// <param name="now">Current UNIX time in milliseconds.</param>
        /// <param name="request">Request object.</param>
        /// <param name="options">Hawk authentication options</param>
        /// <returns></returns>
        internal static async Task<AuthenticationResult> AuthenticateAsync(ulong now, IRequestMessage request, Options options)
        {
            ArtifactsContainer artifacts = null;
            Credential credential = null;

            if (request.HasValidHawkScheme())
            {
                if (ArtifactsContainer.TryParse(request.Authorization.Parameter, out artifacts))
                {
                    if (artifacts != null && artifacts.AreClientArtifactsValid)
                    {
                        credential = options.CredentialsCallback(artifacts.Id);
                        if (credential != null && credential.IsValid)
                        {
                            var normalizedRequest = new NormalizedRequest(request, artifacts);
                            var crypto = new Cryptographer(normalizedRequest, artifacts, credential);

                            // Request body is needed only when payload hash is present in the request
                            string body = null;
                            if (artifacts.PayloadHash != null && artifacts.PayloadHash.Length > 0)
                            {
                                body = await request.ReadBodyAsStringAsync();
                            }

                            if (crypto.IsSignatureValid(body, request.ContentType)) // MAC and hash checks
                            {
                                if (IsTimestampFresh(now, artifacts, options))
                                {
                                    // If you get this far, you are authentic. Welcome and thanks for flying Hawk!
                                    return new AuthenticationResult()
                                    {
                                        IsAuthentic = true,
                                        Artifacts = artifacts,
                                        Credential = credential,
                                        ApplicationSpecificData = artifacts.ApplicationSpecificData
                                    };
                                }
                                else
                                {
                                    // Authentic but for the timestamp freshness.
                                    // Give a chance to the client to correct the clocks skew.
                                    var timestamp = new NormalizedTimestamp(DateTime.UtcNow, credential, options.LocalTimeOffsetMillis);
                                    request.ChallengeParameter = timestamp.ToWwwAuthenticateHeaderParameter();
                                }
                            }
                        }
                    }
                }
            }

            return new AuthenticationResult() { IsAuthentic = false };
        }
        public void HttpsMustSetPortTo443()
        {
            var request = new HttpRequestMessage();
            request.RequestUri = new Uri("https://server/api/values");

            var normalizedRequest = new NormalizedRequest(request, null);

            PrivateObject po = new PrivateObject(normalizedRequest);

            var port = (string)po.GetField("port");

            Assert.AreEqual("443", port);
        }
        public void PortMustDefaultTo443ForHttpsWhenHostHeaderDoesNotContainPort()
        {
            var request = new HttpRequestMessage();
            request.RequestUri = new Uri("https://server/api/values");
            request.Headers.Host = "myhost";

            var normalizedRequest = new NormalizedRequest(request, null);

            PrivateObject po = new PrivateObject(normalizedRequest);

            var port = (string)po.GetField("port");

            Assert.AreEqual("443", port);
        }
Beispiel #12
0
        /// <summary>
        /// Returns an AuthenticationResult object corresponding to the result of authentication done
        /// using the client supplied artifacts in the HTTP authorization header in hawk scheme.
        /// </summary>
        /// <param name="now">Current UNIX time in milliseconds.</param>
        /// <param name="request">Request object.</param>
        /// <param name="callback">The callback function that returns a Credential object corresponding to the identifier passed in.</param>
        /// <returns></returns>
        internal static async Task <AuthenticationResult> AuthenticateAsync(ulong now, HttpRequestMessage request,
                                                                            Func <string, Credential> callback)
        {
            ArtifactsContainer artifacts  = null;
            Credential         credential = null;

            if (request.HasValidHawkScheme())
            {
                if (ArtifactsContainer.TryParse(request.Headers.Authorization.Parameter, out artifacts))
                {
                    if (artifacts != null && artifacts.AreClientArtifactsValid)
                    {
                        credential = callback(artifacts.Id);
                        if (credential != null && credential.IsValid)
                        {
                            var normalizedRequest = new NormalizedRequest(request, artifacts);
                            var crypto            = new Cryptographer(normalizedRequest, artifacts, credential);

                            if (await crypto.IsSignatureValidAsync(request.Content)) // MAC and hash checks
                            {
                                if (IsTimestampFresh(now, artifacts))
                                {
                                    // If you get this far, you are authentic. Welcome and thanks for flying Hawk!
                                    return(new AuthenticationResult()
                                    {
                                        IsAuthentic = true,
                                        Artifacts = artifacts,
                                        Credential = credential,
                                        ApplicationSpecificData = artifacts.ApplicationSpecificData
                                    });
                                }
                                else
                                {
                                    // Authentic but for the timestamp freshness.
                                    // Give a chance to the client to correct the clocks skew.
                                    var timestamp = new NormalizedTimestamp(DateTime.UtcNow, credential);
                                    request.PutChallengeParameter(timestamp.ToWwwAuthenticateHeaderParameter());
                                }
                            }
                        }
                    }
                }
            }

            return(new AuthenticationResult()
            {
                IsAuthentic = false
            });
        }
        public void HostAndPortMustMatchWhatIsInRequestWhenHostAndXffHeadersAreAbsent()
        {
            var request = new HttpRequestMessage();
            request.RequestUri = new Uri("http://server/api/values");

            var normalizedRequest = new NormalizedRequest(request, null);

            PrivateObject po = new PrivateObject(normalizedRequest);

            var hostName = (string)po.GetField("hostName");
            var port = (string)po.GetField("port");

            Assert.AreEqual("server", hostName);
            Assert.AreEqual("80", port);
        }
        public void HostAndPortMustMatchWhatIsInHostHeaderWhenPresent()
        {
            var request = new HttpRequestMessage();
            request.RequestUri = new Uri("http://server/api/values");
            request.Headers.Host = "myhost:899";

            var normalizedRequest = new NormalizedRequest(new WebApiRequestMessage(request), null);

            PrivateObject po = new PrivateObject(normalizedRequest);

            var hostName = (string)po.GetField("hostName");
            var port = (string)po.GetField("port");

            Assert.AreEqual("myhost", hostName);
            Assert.AreEqual("899", port);
        }
        /// <summary>
        /// Returns an AuthenticationResult object corresponding to the result of authentication done
        /// using the client supplied artifacts in the HTTP authorization header in hawk scheme.
        /// </summary>
        /// <param name="now">Current UNIX time in milliseconds.</param>
        /// <param name="request">Request object.</param>
        /// <param name="callback">The callback function that returns a Credential object corresponding to the identifier passed in.</param>
        /// <returns></returns>
        internal static async Task<AuthenticationResult> AuthenticateAsync(ulong now, HttpRequestMessage request,
                                            Func<string, Credential> callback)
        {
            ArtifactsContainer artifacts = null;
            Credential credential = null;

            if (request.HasValidHawkScheme())
            {
                if (ArtifactsContainer.TryParse(request.Headers.Authorization.Parameter, out artifacts))
                {
                    if (artifacts != null && artifacts.AreClientArtifactsValid)
                    {
                        credential = callback(artifacts.Id);
                        if (credential != null && credential.IsValid)
                        {
                            var normalizedRequest = new NormalizedRequest(request, artifacts);
                            var crypto = new Cryptographer(normalizedRequest, artifacts, credential);

                            if (await crypto.IsSignatureValidAsync(request.Content)) // MAC and hash checks
                            {
                                if (IsTimestampFresh(now, artifacts))
                                {
                                    // If you get this far, you are authentic. Welcome and thanks for flying Hawk!
                                    return new AuthenticationResult()
                                    {
                                        IsAuthentic = true,
                                        Artifacts = artifacts,
                                        Credential = credential,
                                        ApplicationSpecificData = artifacts.ApplicationSpecificData
                                    };
                                }
                                else
                                {
                                    // Authentic but for the timestamp freshness.
                                    // Give a chance to the client to correct the clocks skew.
                                    var timestamp = new NormalizedTimestamp(DateTime.UtcNow, credential);
                                    request.PutChallengeParameter(timestamp.ToWwwAuthenticateHeaderParameter());
                                }
                            }
                        }
                    }
                }
            }

            return new AuthenticationResult() { IsAuthentic = false };
        }
Beispiel #16
0
        /// <summary>
        /// Returns the string representation of the bewit, which is a base64 URL encoded string of format
        /// id\exp\mac\ext, where id is the user identifier, exp is the UNIX time until which bewit is
        /// valid, mac is the HMAC of the bewit to protect integrity, and ext is the application specific data.
        /// </summary>
        public async Task <string> ToBewitStringAsync()
        {
            if (request.Method != HttpMethod.Get) // Not supporting HEAD
            {
                throw new InvalidOperationException("Bewit not allowed for methods other than GET");
            }

            ulong now = utcNow.ToUnixTime() +
                        UInt64.Parse(ConfigurationManager.AppSettings["LocalTimeOffsetMillis"]);

            var artifacts = new ArtifactsContainer()
            {
                Id        = credential.Id,
                Timestamp = now + (ulong)lifeSeconds,
                Nonce     = String.Empty,
                ApplicationSpecificData = this.applicationSpecificData ?? String.Empty
            };

            var normalizedRequest = new NormalizedRequest(request, artifacts)
            {
                IsBewit = true
            };
            var crypto = new Cryptographer(normalizedRequest, artifacts, credential);

            // Sign the request
            await crypto.SignAsync(null);

            // bewit: id\exp\mac\ext
            string bewit = String.Format(@"{0}\{1}\{2}\{3}",
                                         credential.Id,
                                         artifacts.Timestamp,
                                         artifacts.Mac.ToBase64String(),
                                         artifacts.ApplicationSpecificData);

            return(bewit.ToBytesFromUtf8().ToBase64UrlString());
        }
        /// <summary>
        /// Returns an AuthenticationResult object corresponding to the result of authentication done
        /// using the client supplied artifacts in the bewit query string parameter.
        /// </summary>
        /// <param name="bewit">Value of the query string parameter with the name of 'bewit'.</param>
        /// <param name="now">Date and time in UTC to be used as the base for computing bewit life.</param>
        /// <param name="request">Request object.</param>
        /// <param name="options">Hawk authentication options</param>
        internal static AuthenticationResult Authenticate(string bewit, ulong now, IRequestMessage request, Options options)
        {
            if (!String.IsNullOrWhiteSpace(bewit))
            {
                if (request.Method == HttpMethod.Get)
                {
                    if (options != null && options.CredentialsCallback != null)
                    {
                        var parts = bewit.ToUtf8StringFromBase64Url().Split('\\');

                        if (parts.Length == 4)
                        {
                            ulong timestamp = 0;
                            if (UInt64.TryParse(parts[1], out timestamp) && timestamp * 1000 > now)
                            {
                                string id  = parts[0];
                                string mac = parts[2];
                                string ext = parts[3];

                                if (!String.IsNullOrWhiteSpace(id) && !String.IsNullOrWhiteSpace(mac))
                                {
                                    RemoveBewitFromUri(request);

                                    Credential credential = options.CredentialsCallback(id);
                                    if (credential != null && credential.IsValid)
                                    {
                                        var artifacts = new ArtifactsContainer()
                                        {
                                            Id        = id,
                                            Nonce     = String.Empty,
                                            Timestamp = timestamp,
                                            Mac       = mac.ToBytesFromBase64(),
                                            ApplicationSpecificData = ext ?? String.Empty
                                        };

                                        var normalizedRequest = new NormalizedRequest(request, artifacts)
                                        {
                                            IsBewit = true
                                        };
                                        var crypto = new Cryptographer(normalizedRequest, artifacts, credential);

                                        if (crypto.IsSignatureValid()) // Bewit is for GET and GET must have no request body
                                        {
                                            return(new AuthenticationResult()
                                            {
                                                IsAuthentic = true,
                                                Credential = credential,
                                                Artifacts = artifacts,
                                                ApplicationSpecificData = ext
                                            });
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(new AuthenticationResult()
            {
                IsAuthentic = false
            });
        }
        /// <summary>
        /// Returns the name of the response header (WWW-Authenticate or Server-Authorization) and the corresponding
        /// value, respectively for an unauthorized and a successful request.
        /// </summary>
        public async Task<Tuple<string, string>> CreateServerAuthorizationAsync(IResponseMessage response)
        {
            if (response.StatusCode == HttpStatusCode.Unauthorized)
            {
                string challenge = String.Format(" {0}", request.ChallengeParameter ?? String.Empty);
                string headerValue = HawkConstants.Scheme + challenge.TrimEnd(' ');

                return new Tuple<string, string>(HawkConstants.WwwAuthenticateHeaderName, headerValue);
            }
            else
            {
                // No Server-Authorization header for the following:
                // (1) There is no result or failed authentication.
                // (2) The credential is a bewit.
                // (3) The server is configured to not send the header.
                bool createHeader = this.result != null
                                        && this.result.IsAuthentic
                                            && (!this.isBewitRequest)
                                                && options.EnableServerAuthorization;

                if (createHeader)
                {
                    if (options.NormalizationCallback != null)
                        this.result.Artifacts.ApplicationSpecificData = options.NormalizationCallback(response);

                    // Sign the response
                    var normalizedRequest = new NormalizedRequest(request, this.result.Artifacts);
                    var crypto = new Cryptographer(normalizedRequest, this.result.Artifacts, this.result.Credential);

                    // Response body is needed only if payload hash must be included in the response MAC.
                    string body = null;
                    if (options.ResponsePayloadHashabilityCallback != null && 
                            options.ResponsePayloadHashabilityCallback(this.request))
                    {
                        body = await response.ReadBodyAsStringAsync();
                    }

                    crypto.Sign(body, response.ContentType);

                    string authorization = this.result.Artifacts.ToServerAuthorizationHeaderParameter();

                    if (!String.IsNullOrWhiteSpace(authorization))
                    {
                        return new Tuple<string, string>(HawkConstants.ServerAuthorizationHeaderName,
                            String.Format("{0} {1}", HawkConstants.Scheme, authorization));
                    }
                }
            }

            return null;
        }
        /// <summary>
        /// Adds the WWW-Authenticate header in case of a 401 - Unauthorized response and
        /// the Server-Authorization header in case of a successful request.
        /// </summary>
        public async Task CreateServerAuthorizationAsync(HttpResponseMessage response, Func<HttpResponseMessage, string> normalizationCallback)
        {
            if (response.StatusCode == HttpStatusCode.Unauthorized)
            {
                var header = new AuthenticationHeaderValue(HawkConstants.Scheme, request.GetChallengeParameter());
                response.Headers.WwwAuthenticate.Add(header);
            }
            else
            {
                if (this.result != null && this.result.IsAuthentic && !this.isBewitRequest) // No Server-Authorization header for bewit requests
                {
                    if (normalizationCallback != null)
                        this.result.Artifacts.ApplicationSpecificData = normalizationCallback(response);

                    // Sign the response
                    var normalizedRequest = new NormalizedRequest(request, this.result.Artifacts);
                    var crypto = new Cryptographer(normalizedRequest, this.result.Artifacts, this.result.Credential);
                    await crypto.SignAsync(response.Content);

                    string authorization = this.result.Artifacts.ToServerAuthorizationHeaderParameter();

                    if (!String.IsNullOrWhiteSpace(authorization))
                        response.Headers.Add(HawkConstants.ServerAuthorizationHeaderName,
                                                HawkConstants.Scheme + " " + authorization);
                }
            }
        }
        public void HostAndPortMustMatchWhatIsInTheFirstXffHeaderWhenMultipleXffHeadersArePresentWithIpv6Address()
        {
            var request = new HttpRequestMessage();
            request.RequestUri = new Uri("http://server/api/values");
            request.Headers.Host = "myhost:899";
            request.Headers.Add("X-Forwarded-For", "[111:111:111]:1111");
            request.Headers.Add("X-Forwarded-For", "[222:222:222]:2222"); // Same as "[111:111:111]:1111, [222:222:222]:2222"

            var normalizedRequest = new NormalizedRequest(request, null);

            PrivateObject po = new PrivateObject(normalizedRequest);

            var hostName = (string)po.GetField("hostName");
            var port = (string)po.GetField("port");

            Assert.AreEqual("[111:111:111]", hostName);
            Assert.AreEqual("1111", port);
        }
        public void HostAndPortMustMatchWhatIsInXffHeaderWhenPresent()
        {
            var request = new HttpRequestMessage();
            request.RequestUri = new Uri("http://server/api/values");
            request.Headers.Host = "myhost:899";
            request.Headers.Add("X-Forwarded-For", "xffhost:4444");

            var normalizedRequest = new NormalizedRequest(request, null);

            PrivateObject po = new PrivateObject(normalizedRequest);

            var hostName = (string)po.GetField("hostName");
            var port = (string)po.GetField("port");

            Assert.AreEqual("xffhost", hostName);
            Assert.AreEqual("4444", port);
        }
        /// <summary>
        /// Creates the HTTP Authorization header in hawk scheme.
        /// </summary>
        internal async Task CreateClientAuthorizationInternalAsync(IRequestMessage request, DateTime utcNow)
        {
            var credential = options.CredentialsCallback();
            this.artifacts = new ArtifactsContainer()
            {
                Id = credential.Id,
                Timestamp = utcNow.AddSeconds(HawkClient.CompensatorySeconds).ToUnixTime(),
                Nonce = NonceGenerator.Generate()
            };

            if (options.NormalizationCallback != null)
                this.artifacts.ApplicationSpecificData = options.NormalizationCallback(request);

            var normalizedRequest = new NormalizedRequest(request, this.artifacts);
            this.crypto = new Cryptographer(normalizedRequest, this.artifacts, credential);

            // Sign the request
            bool includePayloadHash = options.RequestPayloadHashabilityCallback != null &&
                                            options.RequestPayloadHashabilityCallback(request);

            string payload = includePayloadHash ? await request.ReadBodyAsStringAsync() : null;
            crypto.Sign(payload, request.ContentType);

            request.Authorization = new AuthenticationHeaderValue(HawkConstants.Scheme,
                                                this.artifacts.ToAuthorizationHeaderParameter());
        }
        /// <summary>
        /// Returns an AuthenticationResult object corresponding to the result of authentication done
        /// using the client supplied artifacts in the bewit query string parameter.
        /// </summary>
        /// <param name="bewit">Value of the query string parameter with the name of 'bewit'.</param>
        /// <param name="now">Date and time in UTC to be used as the base for computing bewit life.</param>
        /// <param name="request">Request object.</param>
        /// <param name="options">Hawk authentication options</param>
        internal static AuthenticationResult Authenticate(string bewit, ulong now, IRequestMessage request, Options options)
        {
            if (!String.IsNullOrWhiteSpace(bewit))
            {
                if (request.Method == HttpMethod.Get)
                {
                    if (options != null && options.CredentialsCallback != null)
                    {
                        var parts = bewit.ToUtf8StringFromBase64Url().Split('\\');

                        if (parts.Length == 4)
                        {
                            ulong timestamp = 0;
                            if (UInt64.TryParse(parts[1], out timestamp) && timestamp * 1000 > now)
                            {
                                string id = parts[0];
                                string mac = parts[2];
                                string ext = parts[3];

                                if (!String.IsNullOrWhiteSpace(id) && !String.IsNullOrWhiteSpace(mac))
                                {
                                    RemoveBewitFromUri(request);

                                    Credential credential = options.CredentialsCallback(id);
                                    if (credential != null && credential.IsValid)
                                    {
                                        var artifacts = new ArtifactsContainer()
                                        {
                                            Id = id,
                                            Nonce = String.Empty,
                                            Timestamp = timestamp,
                                            Mac = mac.ToBytesFromBase64(),
                                            ApplicationSpecificData = ext ?? String.Empty
                                        };

                                        var normalizedRequest = new NormalizedRequest(request, artifacts) { IsBewit = true };
                                        var crypto = new Cryptographer(normalizedRequest, artifacts, credential);

                                        if (crypto.IsSignatureValid()) // Bewit is for GET and GET must have no request body
                                        {
                                            return new AuthenticationResult()
                                            {
                                                IsAuthentic = true,
                                                Credential = credential,
                                                Artifacts = artifacts,
                                                ApplicationSpecificData = ext
                                            };
                                        }   
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return new AuthenticationResult() { IsAuthentic = false };
        }