internal static bool IsValidSignature(this WebPubSubConnectionContext connectionContext, WebPubSubValidationOptions options) { // no options skip validation. if (options == null || !options.ContainsHost()) { return(true); } if (options.TryGetKey(connectionContext.Origin, out var accessKey)) { // server side disable signature checks. if (string.IsNullOrEmpty(accessKey)) { return(true); } var signatures = connectionContext.Signature.ToHeaderList(); if (signatures == null) { return(false); } using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(accessKey)); var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(connectionContext.ConnectionId)); var hash = "sha256=" + BitConverter.ToString(hashBytes).Replace("-", ""); if (signatures.Contains(hash, StringComparer.OrdinalIgnoreCase)) { return(true); } } return(false); }
public static async Task <WebPubSubEventRequest> ReadWebPubSubRequestAsync(this HttpRequest request, WebPubSubValidationOptions options) { if (request == null) { throw new ArgumentNullException(nameof(request)); } // validation request. if (request.IsValidationRequest(out var requestHosts)) { if (options == null || !options.ContainsHost()) { return(new PreflightRequest(true)); } else { foreach (var item in requestHosts) { if (options.ContainsHost(item)) { return(new PreflightRequest(true)); } } } return(new PreflightRequest(false)); } if (!request.TryParseCloudEvents(out var context)) { throw new ArgumentException("Invalid Web PubSub upstream request missing required fields in header."); } if (!context.IsValidSignature(options)) { throw new UnauthorizedAccessException("Signature validation failed."); } var requestType = context.GetRequestType(); switch (requestType) { case RequestType.Connect: { var content = await new StreamReader(request.Body).ReadToEndAsync().ConfigureAwait(false); var eventRequest = JsonSerializer.Deserialize <ConnectEventRequest>(content); return(new ConnectEventRequest(context, eventRequest.Claims, eventRequest.Query, eventRequest.Subprotocols, eventRequest.ClientCertificates)); } case RequestType.User: { using var ms = new MemoryStream(); await request.Body.CopyToAsync(ms).ConfigureAwait(false); var data = BinaryData.FromBytes(ms.ToArray()); if (!MediaTypeHeaderValue.Parse(request.ContentType).MediaType.IsValidMediaType(out var dataType)) { throw new ArgumentException($"ContentType is not supported: {request.ContentType}"); } return(new UserEventRequest(context, data, dataType)); } case RequestType.Connected: { return(new ConnectedEventRequest(context)); } case RequestType.Disconnected: { var content = await new StreamReader(request.Body).ReadToEndAsync().ConfigureAwait(false); var eventRequest = JsonSerializer.Deserialize <DisconnectedEventRequest>(content); return(new DisconnectedEventRequest(context, eventRequest.Reason)); } default: return(null); } }