internal static HttpServer Create(Func<IResponseMessage, string> normalizationCallback = null,
                                            Func<IRequestMessage, string, bool> verificationCallback = null)
        {
            var configuration = new HttpConfiguration();

            configuration.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            var options = new Options()
            {
                ClockSkewSeconds = 60,
                LocalTimeOffsetMillis = 0,
                CredentialsCallback = (id) => Credentials.FirstOrDefault(c => c.Id == id),
                NormalizationCallback = normalizationCallback,
                VerificationCallback = verificationCallback,
                ResponsePayloadHashabilityCallback = (req) => true
            };

            var hawkHandler = new HawkAuthenticationHandler(options);
            configuration.MessageHandlers.Add(hawkHandler);

            return new HttpServer(configuration);
        }
        /// <summary>
        /// The message handler that authenticates the request using Hawk.
        /// </summary>
        /// <param name="options">Hawk authentication options</param>
        public HawkAuthenticationHandler(Options options)
        {
            if (options == null || options.CredentialsCallback == null)
                throw new ArgumentNullException("Invalid Hawk authentication options. Credentials callback cannot be null.");

            this.options = options;
        }
        /// <summary>
        /// Authenticates the incoming request based on the Authorize request header or bewit query string parameter
        /// </summary>
        /// <param name="request">The request object to be authenticated</param>
        /// <param name="options">Hawk authentication options</param>
        public HawkServer(IRequestMessage request, Options options)
        {
            now = DateTime.UtcNow.ToUnixTimeMillis(); // Record time before doing anything else

            if (options == null || options.CredentialsCallback == null)
                throw new ArgumentNullException("Invalid Hawk authentication options. Credentials callback cannot be null.");

            this.request = request;
            this.options = options;
        }
        /// <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 true if the timestamp sent in by the client is fresh subject to the 
        /// maximum allowed skew and the adjustment offset.
        /// </summary>
        private static bool IsTimestampFresh(ulong now, ArtifactsContainer artifacts, Options options)
        {
            now = now + Convert.ToUInt64(options.LocalTimeOffsetMillis);

            ulong shelfLife = (Convert.ToUInt64(options.ClockSkewSeconds) * 1000);
            var age = Math.Abs((artifacts.Timestamp * 1000.0) - now);

            bool isFresh = (age <= shelfLife);

            if (!isFresh)
                Tracing.Information(
                            String.Format("Stale Timestamp: Age {0} is more than shelf life of {1}", age, shelfLife));

            return isFresh;
        }
        static void Main(string[] args)
        {
            var configuration = new HttpSelfHostConfiguration("http://localhost:12345");

            configuration.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            var credentialStorage = new List<Credential>()
            {
                new Credential()
                {
                    Id = "dh37fgj492je",
                    Algorithm = SupportedAlgorithms.SHA256,
                    User = "******",
                    Key = "werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn"
                }
            };

            var options = new Options()
            {
                ClockSkewSeconds = 60,
                LocalTimeOffsetMillis = 0,
                CredentialsCallback = (id) => credentialStorage.FirstOrDefault(c => c.Id == id),
                ResponsePayloadHashabilityCallback = (r) => true,
                VerificationCallback = (request, ext) =>
                {
                    if (String.IsNullOrEmpty(ext))
                        return true;

                    string name = "X-Request-Header-To-Protect";
                    return ext.Equals(name + ":" + request.Headers[name].First());
                }
            };

            configuration.MessageHandlers.Add(new HawkAuthenticationHandler(options));

            using (HttpSelfHostServer server = new HttpSelfHostServer(configuration))
            {
                server.OpenAsync().Wait();
                Console.WriteLine("Press Enter to terminate the server...");
                Console.ReadLine();
            }
        }
        public void Configuration(IAppBuilder app)
        {
            var credentialStorage = new List<Credential>()
            {
                new Credential()
                {
                    Id = "dh37fgj492je",
                    Algorithm = SupportedAlgorithms.SHA256,
                    User = "******",
                    Key = "werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn"
                }
            };

            var options = new Options()
            {
                ClockSkewSeconds = 60,
                LocalTimeOffsetMillis = 0,
                CredentialsCallback = (id) => credentialStorage.FirstOrDefault(c => c.Id == id),
                ResponsePayloadHashabilityCallback = (r) => true,
                VerificationCallback = (request, ext) =>
                {
                    if (String.IsNullOrEmpty(ext))
                        return true;

                    string name = "X-Request-Header-To-Protect";
                    return ext.Equals(name + ":" + request.Headers[name].First());
                }
            };

            app.UseHawkAuthentication(new HawkAuthenticationOptions(options));

            var config = new HttpConfiguration();

            config.Routes.MapHttpRoute(
                "DefaultWebApi",
                "{controller}/{id}",
                new { id = RouteParameter.Optional });

            app.UseWebApi(config);


        }
        /// <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 };
        }
 public HawkAuthenticationOptions(Options hawkOptions) : base(HawkConstants.Scheme)
 {
     this.HawkOptions = hawkOptions;
 }