Пример #1
0
        public bool ParseHeaders(HttpRequest request, EventSubContext context)
        {
            if (!request.Headers.TryGetValue(EventSubHeaderNames.MessageId, out var msgIdValues))
            {
                return(false);
            }
            if (!request.Headers.TryGetValue(EventSubHeaderNames.MessageRetry, out var retryValues))
            {
                return(false);
            }
            if (!request.Headers.TryGetValue(EventSubHeaderNames.MessageType, out var typeValues))
            {
                return(false);
            }
            if (!request.Headers.TryGetValue(EventSubHeaderNames.MessageSignature, out var signatureValues))
            {
                return(false);
            }
            if (!request.Headers.TryGetValue(EventSubHeaderNames.MessageTimeStamp, out var timestampValues))
            {
                return(false);
            }
            if (!request.Headers.TryGetValue(EventSubHeaderNames.SubscriptionType, out var subTypeValues))
            {
                return(false);
            }
            if (!request.Headers.TryGetValue(EventSubHeaderNames.SubscriptionVersion, out var subVersionValues))
            {
                return(false);
            }

            context.Headers.MessageId           = msgIdValues.First();
            context.Headers.MessageRetry        = int.Parse(retryValues.First());
            context.Headers.MessageType         = typeValues.First();
            context.Headers.MessageSignature    = signatureValues.First();
            context.Headers.MessageTimeStamp    = timestampValues.First();
            context.Headers.SubscriptionType    = subTypeValues.First();
            context.Headers.SubscriptionVersion = subVersionValues.First();

            return(true);
        }
Пример #2
0
        public async Task HandleRequestAsync(HttpContext context)
        {
            var eventSubContext = new EventSubContext
            {
                Logger   = _logger,
                Services = context.RequestServices,
            };

            if (!ParseHeaders(context.Request, eventSubContext))
            {
                context.Response.StatusCode = StatusCodes.Status400BadRequest;
                return;
            }
            TwitchEventSubCallbackPayload payload = null;
            var isFirstSegment = true;

            var key = System.Text.Encoding.UTF8.GetBytes(_options.WebHookSecret);

            using (var signatureAlg = IncrementalHash.CreateHMAC(HashAlgorithmName.SHA256, key))
            {
                ReadResult result;

                signatureAlg.AppendData(System.Text.Encoding.UTF8.GetBytes(eventSubContext.Headers.MessageId));
                signatureAlg.AppendData(System.Text.Encoding.UTF8.GetBytes(eventSubContext.Headers.MessageTimeStamp));

                do
                {
                    result = await context.Request.BodyReader.ReadAsync();

                    foreach (var segment in result.Buffer)
                    {
                        signatureAlg.AppendData(segment.Span);
                        _logger.LogWarning(System.Text.Encoding.UTF8.GetString(segment.Span));
                    }

                    // If the first segment contains the whole body, read the payload from the segment
                    if (isFirstSegment && result.Buffer.IsSingleSegment)
                    {
                        payload = ReadPayload(eventSubContext.Headers.SubscriptionType, result.Buffer);
                        break;
                    }
                    isFirstSegment = false;
                } while (!result.IsCompleted && !result.IsCanceled);

                // Finalizing signature validation
                var hashBytes  = signatureAlg.GetCurrentHash();
                var hashString = Convert.ToHexString(hashBytes).ToLowerInvariant();
                _logger.LogWarning("Signature = {signature}", hashString);
                if (hashString != eventSubContext.Headers.MessageSignature.Split("=").Last())
                {
                    _logger.LogError("Signature mismatch {received} =/= {computer}", eventSubContext.Headers.MessageSignature, hashString);

                    context.Response.StatusCode = StatusCodes.Status400BadRequest;
                    return;
                }

                // Degraded case: Body was more than a single segment, use Stream interface
                if (payload == null)
                {
                    payload = await ReadPayloadAsync(eventSubContext.Headers.SubscriptionType, context.Request.Body);
                }

                if (eventSubContext.Headers.MessageType == EventSubMessageTypes.WebHookCallbackVerification)
                {
                    await context.Response.WriteAsync(payload.Challenge);

                    context.Response.StatusCode  = StatusCodes.Status200OK;
                    context.Response.ContentType = "text/plain";
                    return;
                }

                eventSubContext.Subscription = payload.Subscription;

                foreach (var handler in _handlers)
                {
                    if (handler.CanHandleEvent(eventSubContext, payload.BaseEvent))
                    {
                        await handler.OnEventSubNotification(eventSubContext, payload.BaseEvent);
                    }
                }
            }

            context.Response.StatusCode = StatusCodes.Status200OK;
        }