protected async override Task <AuthenticateResult> HandleAuthenticateAsync()
        {
            var header = SplitAuthenticationHeader();

            if (header == null)
            {
                return(AuthenticateResult.NoResult());
            }

            // Verify that request data is within acceptable time
            if (!DateTimeOffset.TryParseExact(Request.Headers[DateHeader], "r", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AssumeUniversal, out DateTimeOffset requestDate))
            {
                return(AuthenticateResult.Fail("Unable to parse Date header"));
            }

            if (requestDate > Clock.UtcNow.Add(Options.AllowedDateDrift) || requestDate < Clock.UtcNow.Subtract(Options.AllowedDateDrift))
            {
                return(AuthenticateResult.Fail("Date is drifted more than allowed, adjust your time settings."));
            }

            // Lookup and verify secret
            Logger.LogDebug("Looking up secret for {Id}", header.Value.id);
            var secret = await lookup.LookupAsync(header.Value.id);

            if (secret == null)
            {
                Logger.LogInformation("No secret found for {Id}", header.Value.id);
                return(AuthenticateResult.Fail("Invalid id"));
            }
            else if (secret.Length != 32)
            {
                Logger.LogError("Secret must be 32 bytes in size");
                throw new InvalidOperationException("Incorrect secret size");
            }

            // Check signature
            string serverSignature = SignatureHelper.Calculate(secret, SignatureHelper.Generate(requestDate, Request.ContentLength ?? 0, Request.Method, Request.Path, Request.QueryString.Value));

            Logger.LogDebug("Calculated server side signature {signature}", serverSignature);

            if (serverSignature.Equals(header.Value.signature))
            {
                return(AuthenticateResult.Success(new AuthenticationTicket(
                                                      new GenericPrincipal(new GenericIdentity(header.Value.id), Options.GetRolesForId?.Invoke(header.Value.id) ?? null),
                                                      new AuthenticationProperties()
                {
                    IsPersistent = false, AllowRefresh = false
                },
                                                      Options.Schema)));
            }
            else
            {
                return(AuthenticateResult.Fail("Invalid signature"));
            }
        }
        protected async override Task <AuthenticateResult> HandleAuthenticateAsync()
        {
            var header = SplitAuthenticationHeader();

            if (header == null)
            {
                return(AuthenticateResult.NoResult());
            }

            // Verify that request data is within acceptable time
            if (!DateTimeOffset.TryParseExact(Request.Headers[DateHeader], "r", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AssumeUniversal, out DateTimeOffset requestDate))
            {
                return(AuthenticateResult.Fail("Unable to parse Date header"));
            }

            if (requestDate > Clock.UtcNow.Add(Options.AllowedDateDrift) || requestDate < Clock.UtcNow.Subtract(Options.AllowedDateDrift))
            {
                return(AuthenticateResult.Fail("Date drifted more than allowed, adjust your time settings."));
            }

            var nonce = Request.Headers[NonceHeader];

            if (!string.IsNullOrEmpty(nonce))
            {
                if (cache.TryGetValue(nonce, out string _))
                {
                    return(AuthenticateResult.Fail("This message has already been processed"));
                }

                //At two times the allowed drift the nonce cache will make sure we never have a repeat message within the allowed drift and outside the allowed drift the message will be invalid due to drift
                cache.Set <string>(nonce, nonce, TimeSpan.FromTicks(Options.AllowedDateDrift.Ticks * 2));
            }

            // Lookup and verify secret
            Logger.LogDebug("Looking up secret for {Id}", header.Value.id);
            var secret = await lookup.LookupAsync(header.Value.id);

            if (secret == null)
            {
                Logger.LogInformation("No secret found for {Id}", header.Value.id);
                return(AuthenticateResult.Fail("Invalid id"));
            }

            // Check signature
            string serverSignature = SignatureHelper.Calculate(secret, SignatureHelper.Generate(requestDate, await StreamToStringAsync(Request), Request.Method, Request.Path, Request.QueryString.Value, nonce.ToString()));;

            Logger.LogDebug("Calculated server side signature {signature}", serverSignature);

            if (serverSignature.Equals(header.Value.signature))
            {
                return(AuthenticateResult.Success(new AuthenticationTicket(
                                                      new GenericPrincipal(new GenericIdentity(header.Value.id), Options.GetRolesForId?.Invoke(header.Value.id) ?? null),
                                                      new AuthenticationProperties()
                {
                    IsPersistent = false, AllowRefresh = false
                },
                                                      Options.Schema)));
            }
            else
            {
                return(AuthenticateResult.Fail("Invalid signature"));
            }
        }