public async Task MustReturn200WhenBewitIsValid()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI))
                {
                    var client = new HawkClient(() => ServerFactory.DefaultCredential);
                    await client.CreateBewitAsync(request, 10);

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
                        Assert.AreEqual("Thanks for flying Hawk", await response.Content.ReadAsAsync<string>());
                        Assert.IsFalse(response.Headers.Contains(HawkConstants.ServerAuthorizationHeaderName));
                    }
                }
            }
        }
        public async Task MustReturn200WhenValidHawkSchemeHeaderIsPresentInGet()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI))
                {
                    var client = new HawkClient(() => ServerFactory.DefaultCredential);
                    await client.CreateClientAuthorizationAsync(request);

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
                        Assert.AreEqual("Thanks for flying Hawk", await response.Content.ReadAsAsync<string>());
                        Assert.IsTrue(await client.AuthenticateAsync(response));
                    }
                }
            }
        }
        public async Task MustReturn401WhenMacCreatedUsingBadSymmetricKey()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI))
                {
                    var badCredential = ServerFactory.Credentials.First();
                    badCredential.Key = "werxhqb98"; // Some key other than werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn

                    var client = new HawkClient(() => badCredential);
                    await client.CreateClientAuthorizationAsync(request);

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode);
                        Assert.IsNotNull(response.Headers.WwwAuthenticate.FirstOrDefault());
                        Assert.AreEqual("hawk", response.Headers.WwwAuthenticate.FirstOrDefault().Scheme);
                    }
                }
            }
        }
        static void Main(string[] args)
        {
            var credential = new Credential()
            {
                Id = "dh37fgj492je",
                Algorithm = SupportedAlgorithms.SHA256,
                User = "******",
                Key = "werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn"
            };

            string uri = "http://localhost:12345/api/values";

            var client = new HttpClient();

            // GET using the Authorization header
            var request = new HttpRequestMessage(HttpMethod.Get, uri);
            request.Headers.Add("X-Request-Header-To-Protect", "Swoosh");

            var hawkClient = new HawkClient(() => credential);
            hawkClient.ApplicationSpecificData = "X-Request-Header-To-Protect:Swoosh"; // Normalized header
            hawkClient.CreateClientAuthorizationAsync(request).Wait();

            var response = client.SendAsync(request).Result;
            var isAuthentic = hawkClient.AuthenticateAsync(response).Result;
            Console.WriteLine(isAuthentic ? response.Content.ReadAsStringAsync().Result : "Response is Tampered");

            // GET using Bewit
            hawkClient = new HawkClient(() => credential);
            string bewit = hawkClient.CreateBewitAsync(new HttpRequestMessage() { RequestUri = new Uri(uri) },
                                                        lifeSeconds:60).Result;

            // Bewit is handed off to a client needing temporary access to the resource.
            var clientNeedingTempAccess = new WebClient();
            var resource = clientNeedingTempAccess.DownloadString(uri + "?bewit=" + bewit);
            Console.WriteLine(resource);

            Console.Read();
        }
        public async Task MustReturn401WhenBodyContentIsChangedAfterMacAndHashAreComputed()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Post, URI))
                {
                    request.Content = new ObjectContent<string>("Steve", new JsonMediaTypeFormatter());

                    var client = new HawkClient(() => ServerFactory.DefaultCredential);
                    await client.CreateClientAuthorizationAsync(request); // Hash is now based on "Steve"

                    // Changing body to "Stephen"
                    request.Content = new ObjectContent<string>("Stephen", new JsonMediaTypeFormatter());

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode);
                        Assert.IsNotNull(response.Headers.WwwAuthenticate.FirstOrDefault());
                        Assert.AreEqual("hawk", response.Headers.WwwAuthenticate.FirstOrDefault().Scheme);
                    }
                }
            }
        }
        public async Task MustReturn401WhenQueryStringIsChangedAfterMacIsComputed()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI + "?firstname=Louis&&lastname=Phillipe"))
                {
                    var client = new HawkClient(() => ServerFactory.DefaultCredential);
                    await client.CreateClientAuthorizationAsync(request); // Hash is now based on ?firstname=Louis&&lastname=Phillipe

                    // Changing query string to ?firstname=Luis&&lastname=Phillip
                    request.RequestUri = new Uri(URI + "?firstname=Luis&&lastname=Phillip");

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode);
                        Assert.IsNotNull(response.Headers.WwwAuthenticate.FirstOrDefault());
                        Assert.AreEqual("hawk", response.Headers.WwwAuthenticate.FirstOrDefault().Scheme);
                    }
                }
            }
        }
        private static async Task MustReturn401WhenHawParameterIsTampered(string tamperedAttribute)
        {
            using (var invoker = new HttpMessageInvoker(ServerFactory.Create()))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Post, URI))
                {
                    request.Content = new ObjectContent<string>("Steve", new JsonMediaTypeFormatter());

                    var client = new HawkClient(() => ServerFactory.DefaultCredential);
                    client.ApplicationSpecificData = "world peace";

                    await client.CreateClientAuthorizationAsync(request);

                    string goodParameter = request.Headers.Authorization.Parameter;

                    // For example, ts="1370846381" becomes ts="bad1370846381"
                    string tampered = goodParameter.Replace((tamperedAttribute + "=\""), (tamperedAttribute + "=\"bad"));

                    request.Headers.Authorization =
                        new AuthenticationHeaderValue("hawk", tampered);

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode);
                        Assert.IsNotNull(response.Headers.WwwAuthenticate.FirstOrDefault());
                        Assert.AreEqual("hawk", response.Headers.WwwAuthenticate.FirstOrDefault().Scheme);
                    }
                }
            }
        }
        public async Task MustReturn401WhenHawkParameterHasDuplicateAttribute()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI))
                {
                    var client = new HawkClient(() => ServerFactory.DefaultCredential);
                    await client.CreateClientAuthorizationAsync(request);

                    string goodParameter = request.Headers.Authorization.Parameter;
                    string duplicates = goodParameter + "," + goodParameter;

                    request.Headers.Authorization =
                        new AuthenticationHeaderValue("hawk", duplicates);

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode);
                        Assert.IsNotNull(response.Headers.WwwAuthenticate.FirstOrDefault());
                        Assert.AreEqual("hawk", response.Headers.WwwAuthenticate.FirstOrDefault().Scheme);
                        Assert.IsTrue(String.IsNullOrEmpty(response.Headers.WwwAuthenticate.FirstOrDefault().Parameter));
                    }
                }
            }
        }
        public async Task MustReturn204WhenValidHawkSchemeHeaderIsPresentInPut()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Put, URI))
                {
                    var client = new HawkClient(() => ServerFactory.DefaultCredential);

                    request.Content = new ObjectContent<string>("Steve", new JsonMediaTypeFormatter());
                    await client.CreateClientAuthorizationAsync(request);

                    string parameter = request.Headers.Authorization.Parameter;

                    // hash must be present, since PUT contains a request body
                    Assert.IsTrue(ParameterChecker.IsFieldPresent(parameter, "hash"));

                    request.Headers.Authorization = new AuthenticationHeaderValue("hawk", parameter);

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.NoContent, response.StatusCode);

                        string authorization = response.Headers.GetValues("Server-Authorization").FirstOrDefault();

                        // mac must be present in Server-Authorization
                        Assert.IsTrue(ParameterChecker.IsFieldPresent(authorization, "mac"));

                        // Since there is no response body, no hash must be present in Server-Authorization
                        Assert.IsFalse(ParameterChecker.IsFieldPresent(authorization, "hash"));

                        Assert.IsTrue(await client.AuthenticateAsync(response));
                    }
                }
            }
        }
        public async Task MustReturn401WhenUriIsTampered()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI))
                {
                    var client = new HawkClient(() => ServerFactory.DefaultCredential);
                    string bewit = await client.CreateBewitAsync(request, 10);

                    var parts = bewit.ToUtf8StringFromBase64Url().Split('\\');
                    string id = parts[0];
                    string timestamp = parts[1];
                    string mac = parts[2];
                    string ext = parts[3];

                    string tamperedBewit = String.Format(@"{0}\{1}\{2}\{3}", "Id of my choice", timestamp, mac, ext);
                    tamperedBewit = tamperedBewit.ToBytesFromUtf8().ToBase64UrlString();

                    using (var freshRequest = new HttpRequestMessage())
                    {
                        string tamperedUri = URI + "/1";
                        freshRequest.RequestUri = new Uri(tamperedUri + "?bewit=" + tamperedBewit);

                        using (var response = await invoker.SendAsync(freshRequest, CancellationToken.None))
                        {
                            Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode);
                        }
                    }
                }
            }
        }
        public async Task MustThrowInvalidOperationExceptionWhenBewitUsedWithPost()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Post, URI))
                {
                    var client = new HawkClient(() => ServerFactory.DefaultCredential);
                    await client.CreateBewitAsync(request, 10);

                    await invoker.SendAsync(request, CancellationToken.None);
                }
            }
        }
        public async Task MustReturn401WhenBewitHasExpired()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI))
                {
                    var client = new HawkClient(() => ServerFactory.DefaultCredential);
                    await client.CreateBewitAsync(request, 0); // no life in it

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode);
                    }
                }
            }
        }
        private static async Task MustReturn401WhenHawParameterHasMissingAttribute(string missingAttribute)
        {
            using (var invoker = new HttpMessageInvoker(ServerFactory.Create()))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI))
                {
                    var client = new HawkClient(() => ServerFactory.DefaultCredential);
                    await client.CreateClientAuthorizationAsync(request);

                    string goodParameter = request.Headers.Authorization.Parameter;
                    string incompleteParameter = goodParameter.Split(',')
                                            .Where(t => !t.Trim().StartsWith(missingAttribute))
                                                .Aggregate((a, b) => a + "," + b).Trim();

                    request.Headers.Authorization =
                        new AuthenticationHeaderValue("hawk", incompleteParameter);

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode);
                        Assert.IsNotNull(response.Headers.WwwAuthenticate.FirstOrDefault());
                        Assert.AreEqual("hawk", response.Headers.WwwAuthenticate.FirstOrDefault().Scheme);
                        Assert.IsTrue(String.IsNullOrEmpty(response.Headers.WwwAuthenticate.FirstOrDefault().Parameter));
                    }
                }
            }
        }
        public async Task MustReturn401WhenHawkParameterTimestampIsStale()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI))
                {
                    var client = new HawkClient(() => ServerFactory.DefaultCredential);

                    await client.CreateClientAuthorizationInternalAsync(request, DateTime.UtcNow.AddMinutes(-2));

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode);
                        Assert.IsNotNull(response.Headers.WwwAuthenticate.FirstOrDefault());
                        Assert.AreEqual("hawk", response.Headers.WwwAuthenticate.FirstOrDefault().Scheme);

                        Assert.IsFalse(String.IsNullOrEmpty(response.Headers.WwwAuthenticate.FirstOrDefault().Parameter));
                        string tsParameter = response.Headers.WwwAuthenticate.FirstOrDefault().Parameter;

                        // ts and tsm must be present
                        Assert.IsTrue(ParameterChecker.IsFieldPresent(tsParameter, "ts"));
                        Assert.IsTrue(ParameterChecker.IsFieldPresent(tsParameter, "tsm"));

                        Assert.IsTrue(await client.AuthenticateAsync(response));

                    }
                }
            }
        }
        public async Task MustReturn401WhenHawkCredentialIdIsInvalid()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI))
                {
                    var badCredential = ServerFactory.Credentials.First();
                    badCredential.Id = "SomeIdOtherThan-dh37fgj492je"; // Some id other than dh37fgj492je

                    var client = new HawkClient(() => badCredential);
                    await client.CreateClientAuthorizationAsync(request);

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode);
                        Assert.IsNotNull(response.Headers.WwwAuthenticate.FirstOrDefault());
                        Assert.AreEqual("hawk", response.Headers.WwwAuthenticate.FirstOrDefault().Scheme);
                        Assert.IsTrue(String.IsNullOrEmpty(response.Headers.WwwAuthenticate.FirstOrDefault().Parameter));
                    }
                }
            }
        }
        public async Task MustReturn200WhenValidHawkSchemeHeaderWithAppSpecifiDataIsPresentInGetAndServerSendsAppSpecificData()
        {
            Func<HttpResponseMessage, string> normalizationCallback = (response) =>
            {
                // Simulate service adding a sensitive header
                response.Headers.Add("X-Header-To-Protect", "Swoop"); // Sensitive header to protect

                // return the status code and the sensitive header in the agreed format
                return (int)response.StatusCode + ":X-Header-To-Protect:Swoop";
            };

            Func<HttpRequestMessage, string, bool> verificationCallback = (request, appSpecificData) =>
            {
                // Client and server decided that appSpecificData will be in the form of
                // header name and header value separated by a colon, like so: X-Header-To-Protect:Swoosh.

                if (String.IsNullOrEmpty(appSpecificData))
                    return true; // Nothing to check against

                var parts = appSpecificData.Split(':');
                string headerName = parts[0];
                string value = parts[1];

                if (request.Headers.Contains(headerName) &&
                    request.Headers.GetValues(headerName).First().Equals(value))
                    return true;

                return false;
            };

            server = ServerFactory.Create(normalizationCallback, verificationCallback);

            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI))
                {
                    request.Headers.Add("X-Header-To-Protect", "Swoosh"); // Sensitive header to protect

                    var client = new HawkClient(() => ServerFactory.DefaultCredential);
                    client.ApplicationSpecificData = "X-Header-To-Protect:Swoosh"; // Put the sensitive header in the format agreed

                    await client.CreateClientAuthorizationAsync(request);

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
                        Assert.AreEqual("Thanks for flying Hawk", await response.Content.ReadAsAsync<string>());
                        Assert.IsTrue(await client.AuthenticateAsync(response));

                        Assert.IsNotNull(client.WebApiSpecificData);
                        Assert.IsFalse(String.IsNullOrWhiteSpace(client.WebApiSpecificData));

                        var parts = client.WebApiSpecificData.Split(':');

                        HttpStatusCode status = (HttpStatusCode)Enum.Parse(typeof(HttpStatusCode), parts[0]);
                        string headerName = parts[1];
                        string value = parts[2];

                        Assert.AreEqual(response.StatusCode, status);
                        Assert.IsTrue(response.Headers.Contains(headerName));
                        Assert.IsTrue(response.Headers.GetValues(headerName).First().Equals(value));
                    }
                }
            }
        }
        public async Task MustReturn200WhenValidHawkSchemeHeaderIsPresentInPost()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Post, URI))
                {
                    var client = new HawkClient(() => ServerFactory.DefaultCredential);

                    request.Content = new ObjectContent<string>("Steve", new JsonMediaTypeFormatter());

                    await client.CreateClientAuthorizationAsync(request);

                    string parameter = request.Headers.Authorization.Parameter;

                    // hash must be present, since POST contains a request body
                    Assert.IsTrue(ParameterChecker.IsFieldPresent(parameter, "hash"));

                    request.Headers.Authorization =
                        new AuthenticationHeaderValue("hawk", parameter);

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
                        Assert.AreEqual("Hello, Steve. Thanks for flying Hawk", await response.Content.ReadAsAsync<string>());
                        Assert.IsTrue(await client.AuthenticateAsync(response));
                    }
                }
            }
        }
        public async Task TamperedResponseMacOrHashMustFailClientAuthentication()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI))
                {
                    var client = new HawkClient(() => ServerFactory.DefaultCredential);
                    await client.CreateClientAuthorizationAsync(request);

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);

                        string header = response.Headers.GetValues(HawkConstants.ServerAuthorizationHeaderName).FirstOrDefault();
                        Assert.IsTrue(await client.AuthenticateAsync(response));

                        string tamperedHeader = header.Replace("mac=\"", "mac=\"1234"); // mac="abc" => mac = "1234abc"
                        response.Headers.Remove(HawkConstants.ServerAuthorizationHeaderName);
                        response.Headers.Add(HawkConstants.ServerAuthorizationHeaderName, tamperedHeader);
                        Assert.IsFalse(await client.AuthenticateAsync(response));

                        tamperedHeader = header.Replace("hash=\"", "hash=\"1234"); // hash="abc" => hash = "1234abc"
                        response.Headers.Remove(HawkConstants.ServerAuthorizationHeaderName);
                        response.Headers.Add(HawkConstants.ServerAuthorizationHeaderName, tamperedHeader);
                        Assert.IsFalse(await client.AuthenticateAsync(response));
                    }
                }
            }
        }
        public async Task TimestampInSubsequentRequestMustBeAdjustedBasedOnTimestampInWwwAuthenticateHeaderOfPrevious()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI))
                {
                    var client = new HawkClient(() => ServerFactory.DefaultCredential);
                    await client.CreateClientAuthorizationAsync(request);

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        // Simulate server clock running 5 minutes slower than client and 
                        // produce the parameter with ts and tsm
                        var timestamp = new NormalizedTimestamp(DateTime.UtcNow.AddMinutes(-5), ServerFactory.DefaultCredential);
                        string timestampHeader = timestamp.ToWwwAuthenticateHeaderParameter();

                        // Add that to the WWW-Authenticate before authenticating with the
                        // client, to simulate clock skew
                        response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("hawk", timestampHeader));

                        // Client must have now calculated an offset of -300 seconds approx
                        Assert.IsTrue(await client.AuthenticateAsync(response));
                        Assert.IsTrue(HawkClient.CompensatorySeconds <= -299 && HawkClient.CompensatorySeconds >= -301);

                        // Create a fresh request and see if this offset is applied to the timestamp
                        using (var subsequentRequest = new HttpRequestMessage(HttpMethod.Get, URI))
                        {
                            await client.CreateClientAuthorizationAsync(subsequentRequest);

                            string header = subsequentRequest.Headers.Authorization.Parameter;

                            ArtifactsContainer artifacts = null;
                            Assert.IsTrue(ArtifactsContainer.TryParse(header, out artifacts));

                            var timestampInSubsequentRequest = artifacts.Timestamp;
                            var now = DateTime.UtcNow.ToUnixTime();

                            // Since server clock is slow, the timestamp going out must be offset by the same 5 minutes
                            // or 300 seconds. Give leeway of a second while asserting.
                            ulong difference = now - timestampInSubsequentRequest;
                            Assert.IsTrue(difference >= 299 && difference <= 301);
                        }
                    }
                }
            }
        }
        public async Task TamperedTimestampMacMustFailClientAuthentication()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI))
                {
                    var client = new HawkClient(() => ServerFactory.DefaultCredential);

                    await client.CreateClientAuthorizationInternalAsync(request, DateTime.UtcNow.AddMinutes(-2));

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode);
                        var wwwheader = response.Headers.WwwAuthenticate.FirstOrDefault();
                        Assert.IsNotNull(wwwheader);
                        Assert.AreEqual("hawk", wwwheader.Scheme);

                        Assert.IsNotNull(wwwheader.Parameter);
                        string tsParameter = wwwheader.Parameter;

                        // ts and tsm must be present
                        Assert.IsTrue(ParameterChecker.IsFieldPresent(tsParameter, "ts"));
                        Assert.IsTrue(ParameterChecker.IsFieldPresent(tsParameter, "tsm"));
                        Assert.IsTrue(await client.AuthenticateAsync(response));

                        string tamperedtsParameter = tsParameter.Replace("tsm=\"", "tsm=\"1234"); // tsm="abc" => tsm = "1234abc"
                        response.Headers.WwwAuthenticate.Remove(wwwheader);
                        response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue("hawk", tamperedtsParameter));
                        Assert.IsFalse(await client.AuthenticateAsync(response));
                    }
                }
            }
        }
        public async Task MustReturn200ForValidHawkSchemeWithXffRequestHeaderContainingIPV6AddressAndPort()
        {
            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI))
                {
                    request.Headers.Add("X-Forwarded-For", "[ipv6]:99");

                    var client = new HawkClient(() => ServerFactory.DefaultCredential);
                    await client.CreateClientAuthorizationAsync(request);

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
                        Assert.AreEqual("Thanks for flying Hawk", await response.Content.ReadAsAsync<string>());
                        Assert.IsTrue(await client.AuthenticateAsync(response));
                    }
                }
            }
        }
        public async Task MustReturn401WhenProtectedCustomHeaderIsTampered()
        {
            Func<HttpRequestMessage, string, bool> verificationCallback = (request, appSpecificData) =>
            {
                // Client and server decided that appSpecificData will be
                // header name:header value, like so: X-Request-Header:Swoosh

                if (String.IsNullOrEmpty(appSpecificData))
                    return true; // Nothing to check against

                var parts = appSpecificData.Split(':');
                string headerName = parts[0];
                string value = parts[1];

                if (request.Headers.Contains(headerName) &&
                    request.Headers.GetValues(headerName).First().Equals(value))
                    return true;

                return false;
            };

            server = ServerFactory.Create(null, verificationCallback);

            using (var invoker = new HttpMessageInvoker(server))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, URI))
                {
                    // Sensitive header to protect but simulate tampering by changing the value
                    request.Headers.Add("X-Request-Header", "Tampered Swoosh");

                    var client = new HawkClient(() => ServerFactory.DefaultCredential);

                    // Put the sensitive header in the earlier decided format in the ApplicationSpecificData property
                    client.ApplicationSpecificData = "X-Request-Header:Swoosh";

                    await client.CreateClientAuthorizationAsync(request);

                    using (var response = await invoker.SendAsync(request, CancellationToken.None))
                    {
                        Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode);
                        Assert.IsNotNull(response.Headers.WwwAuthenticate.FirstOrDefault());
                        Assert.AreEqual("hawk", response.Headers.WwwAuthenticate.FirstOrDefault().Scheme);
                    }
                }
            }
        }