Howto register a webhook with AuthType=HttpHeader using "Dynamics 365 Web API" - webhooks

To register a webhook on Dynamics 365 there is a tool called Plug-in Registration (https://learn.microsoft.com/en-us/powerapps/developer/data-platform/register-web-hook).
However, I would like to automate the webhook registration process and thus would like to use the Web API endpoint for webhook registration:
/api/data/v9.2/serviceendpoints
The problem I have is the setting of the value for the parameter authvalue in the JSON request body, because I want to use Http Header authtype:
{
"name": "Test Webhook",
"url": "https://myendpoint",
"contract": 8,
"authtype": 5,
"authvalue": "<========= ??? what comes here ???"
}
Assuming I want to have this header be sent to my webhook endpoint:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
What would be the value of authvalue in the JSON above ?
The value should be a string according to the data type of authvalue, but how that should be formatted?
If I use the value Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ= as is for authvalue,
when the webhook is fired, I got the following error in System Job:
Plugin Trace:
[Microsoft.Crm.ServiceBus: Microsoft.Crm.ServiceBus.WebhookPlugin]
[ad9a4124-ab57-ec11-8f8f-6045bd8aed3b: Test for Step Creation]
Error Message:
System.ServiceModel.FaultException`1[Microsoft.Xrm.Sdk.OrganizationServiceFault]:
The webhook call failed because of invalid http headers in authValue.
Check if the authValue format, header names and values are valid for
your Service Endpoint entity. (Fault Detail is equal to Exception
details: ErrorCode: 0x80050203 Message: The webhook call failed
because of invalid http headers in authValue. Check if the authValue
format, header names and values are valid for your Service Endpoint
entity. TimeStamp: 2021-12-08T10:15:26.8637496Z
-- Exception details: ErrorCode: 0x80040216 Message: Received exception when adding custom http headers: for
OrgId:xxxxxx-925f-4958-9aee-xxxxxxxxxxxx, serviceEndpointId:
c099d16c-a057-ec11-8f8f-6045bd8aed3b, name: Test Webhook,
exception:System.Xml.XmlException: Data at the root level is invalid.
Line 1, position 1. at System.Xml.XmlTextReaderImpl.Throw(Exception
e) at System.Xml.XmlTextReaderImpl.ParseRootLevelWhitespace() at
System.Xml.XmlTextReaderImpl.ParseDocumentContent() at
System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean
preserveWhitespace) at System.Xml.XmlDocument.Load(XmlReader
reader) at System.Xml.XmlDocument.LoadXml(String xml) at
Microsoft.Crm.ServiceBus.WebhookClient.ExtractKe...).

For HtppHeader authentication type the key value pair for AuthValue must be set in the following xml format:
<settings><setting name="Authorization" value="Basic dXNlcm5hbWU6cGFzc3dvcmQ=" /></settings>

Related

Revenuecat Webhook Issue

I have implemented webhook for revenuecat in .net core C#. But for some reason I am getting 400 badrequest with empty response. I am mostly sure that I am not getting the json response in webook through revenuecat for any of the event occurring.
I have also added endpoint on revenue cat webhook with authentication. I have tried several time and as I have not way to test this on local machine. I need help from revenue cat team to provide some reference doc with sample implementation just to get proper json response. Below is the code snippet that I am using to get json response from the webhook endpoint added in revenuecat.
var webHookSecret = _configuration[Constants.RevenueCatWebHookSecret]; var headerAuthorization = HttpContext.Request.Headers["Authorization"].ToString(); #region Check authorization if (webHookSecret == headerAuthorization) { json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync(); } else { _logger.Information($"Un-Authorized token access in revenuecat webhook. Returning BadRequest response."); throw new APIException(HttpStatusCode.Unauthorized, new APIError(_configuration, "InternalServerError")); }

Twilio Notify toBindings

I have multiple bindings to an identity, let's say APN and SMS.
If I read and what I understand is that you can specify what type of binding you want to sent it to via the "toBinding" attribute? But I'm getting an error if I use that attribute...
const notificationOpts = {
identity: '45A4B6D0-BBA8-4C9E-B0B8-DCA1433C1E65',
toBinding: ['sms'],
body: 'Knok-Knok! This is your first Notify SMS',
};
client.notify
.services('ISXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
.notifications.create(notificationOpts)
.then(notification => console.log(notification))
.catch(error => console.log(error));
The ERROR message I get when I use the code above.
{ [Error: Can not convert incoming parameters to Notification object: Parameter 'ToBinding' is invalid]
status: 400,
message: 'Can not convert incoming parameters to Notification object: Parameter \'ToBinding\' is invalid',
code: 20001,
moreInfo: 'https://www.twilio.com/docs/errors/20001',
detail: undefined }
Twilio developer evangelist here.
The toBinding attribute is actually for sending notifications to users that you haven't previously created bindings for. To select the correct channel to send the notification you should use the Tag parameter.
As the documentation says:
Tag
A tag that selects the Bindings to notify. Repeat this parameter to specify more than one tag, up to a total of 5 tags. The implicit tag all is available to notify all Bindings in a Service instance. Similarly, the implicit tags apn, fcm, gcm, sms and facebook-messenger are available to notify all Bindings in a specific channel.
So, you should use the following notificationOpts in your original example:
const notificationOpts = {
identity: '45A4B6D0-BBA8-4C9E-B0B8-DCA1433C1E65',
tag: ['sms'],
body: 'Knok-Knok! This is your first Notify SMS',
};
Let me know if that helps.

Azure Microsoft Graph API - Subscription - validation request failed

I try to set up a notification for changes in Azure user data (https://learn.microsoft.com/en-us/graph/webhooks).
I send a request from a local client to the Azure Graph API and I have a publicly available server (with SSL) in the payload of the request as notification URL.
Azure now sends a posts request to my server (like in the documentation - exact post request see below) and I try to send the token I got back (like in the documentation). But I always get the following error message "Subscription validation request failed. Response must exactly match validationToken query parameter."
Post request from Azure:
Path: /?validationToken=Validation%3a+Testing+client+application+reachability+for+subscription+Request-Id%3a+3b3f9821-ce3f-23d9-879b-00a23f3
Body: is empty
I tried every part and encoding of the path (like just the request ID or the whole path) but I always get the error message. So whats the right thing to send back?
Firstly, the validation token you receive should be treated as an opaque value and returned unchanged and the error Subscription validation request failed. Response must exactly match validationToken query parameter is trying to tell you that something changed.
Since the validation token comes to you as a URL query parameter, make sure you're working with a properly decoded value in your code before returning it.
Here is the relevant documentation from Microsoft Docs: Notification endpoint validation
POST https://{notificationUrl}?validationToken={opaqueTokenCreatedByMicrosoftGraph}
Other requirements(from the same reference):
response within 10 seconds
200 (OK) status code.
content type must be text/plain.
body must include the validation token.
Code Samples
ASP.NET MVC Sample - Specifically look at the NotificationController.cs file
[HttpPost]
public async Task<ActionResult> Listen()
{
// Validate the new subscription by sending the token back to Microsoft Graph.
// This response is required for each subscription.
if (Request.QueryString["validationToken"] != null)
{
var token = Request.QueryString["validationToken"];
return Content(token, "plain/text");
}
Node.js code sample - Specifically look at listen.js
/* Default listen route */
listenRouter.post('/', (req, res, next) => {
let status;
let clientStatesValid;
// If there's a validationToken parameter in the query string,
// then this is the request that Office 365 sends to check
// that this is a valid endpoint.
// Just send the validationToken back.
if (req.query && req.query.validationToken) {
res.send(req.query.validationToken);
// Send a status of 'Ok'
status = 200;
}
You should return the validationToken from the query string with an HTTP 200 response code. You also have to do that within a few seconds, or the graph will fail the request and your call to create the subscription will fail.
Here is an example of the validation endpoint in ASP.NET Web API 2:
public ActionResult<string> Post([FromQuery]string validationToken = null)
{
if(!string.IsNullOrEmpty(validationToken))
{
Console.WriteLine($"Token: '{validationToken}'");
return Ok(validationToken);
}
}

RabbitMQ, EasyNetQ With NodeJS?

I'm trying to understand what's reasonable for integrating these technologies. How would I go about integrating NodeJS (currently using amqplib, but that could be changed) across RabbitMQ to EasyNetQ?
I have it sort of working, except EasyNetQ is expecting an object (I think) and Node/amqplib can only send strings.
C# code:
Bus.Subscribe<BusManifestHolla>(HollaID,
msg => {
Console.WriteLine("Received Manifest Holla ID {0}", msg.ManifestID.ToString());
Console.WriteLine("Responding with Manifest Yo ID {0}", YoID_1);
Bus.Publish(new BusManifestYo { ManifestID = msg.ManifestID, ServiceName = YoID_1 });
}
);
NodeJS code:
var b = new Buffer(JSON.stringify(new dto.BusManifestHolla(uuid.v4())));
ch.publish(Play.exchangeName, '#', b);
The result:
DEBUG: HandleBasicDeliver on consumer: a60b7760-e22f-4685-9f65-039bef19f58c, deliveryTag: 1
DEBUG: Recieved
RoutingKey: '#'
CorrelationId: ''
ConsumerTag: 'a60b7760-e22f-4685-9f65-039bef19f58c'
DeliveryTag: 1
Redelivered: False
ERROR: Exception thrown by subscription callback.
Exchange: 'RabbitMon.BusManifestHolla:RabbitMon'
Routing Key: '#'
Redelivered: 'False'
Message:
{"Guid":"a6cf174d-9b77-4558-bbda-efe9d8451dff"}
BasicProperties:
ContentType=NULL, ContentEncoding=NULL, Headers=[], DeliveryMode=0, Priority=0, CorrelationId=NULL, ReplyTo=NULL, Expiration=NULL, MessageId=NULL, Timestamp=0, Type=NULL, UserId=NULL, AppId=NULL, ClusterId=
Exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at EasyNetQ.TypeNameSerializer.DeSerialize(String typeName)
at EasyNetQ.RabbitAdvancedBus.<>c__DisplayClass16.<Consume>b__15(Byte[] body, MessageProperties properties, MessageReceivedInfo messageRecievedInfo)
at EasyNetQ.Consumer.HandlerRunner.InvokeUserMessageHandler(ConsumerExecutionContext context)
Is there not a way to send an object across the bus? How do you integrate these two?
It's failing on the TypeNameSerializer.DeSerialize call. In your node code you'll need to populate BasicProperties.Type with the type that EasyNetQ should expect at the other end. This needs to be a fully qualified name including the assembly name. Just look at the name that EasyNetQ has given to your BusManifestHolla queue minus the HollaID value (and underscore).
Admittedly that error message isn't very helpful. It probably could be improved.

ServiceStack: SerializationException

I am testing the api created using ServiceStack using SoapUI and when I try to send the required DataMember thru headers, the api returns the correct values. When I try to send the required DataMember thru Body, I am getting the below error... Please help
Request sent through the body
<GetProductDetailsReq>
<AToken>ck0b0YYBPkrnVF/j6e16DUPzxLX2SMCXewoR4T</AToken>
</GetProductDetailsReq>
POST http://localhost/ServiceStackAPI/GetProductDetails HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: application/xml
Accept: application/xml
Content-Length: 777
Host: localhost
Proxy-Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
Response Status
Error Code
SerializationException Message
Could not deserialize 'application/xml' request using ServiceModel.DTO.GetProductDetailsReq' Error:
System.Runtime.Serialization.SerializationException: Error in line 1
position 66. Expecting element 'GetProductDetailsReq' from namespace
''.. Encountered 'Element' with name 'GetProductDetailsReq', namespace
'http://schemas.servicestack.net/types'. at
System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator
xmlReader, Boolean verifyObjectName, DataContractResolver
dataContractResolver) at
System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator
reader, Boolean verifyObjectName, DataContractResolver
dataContractResolver) at
System.Runtime.Serialization.XmlObjectSerializer.ReadObject(XmlDictionaryReader
reader) at
System.Runtime.Serialization.XmlObjectSerializer.ReadObject(Stream
stream) at ServiceStack.Text.XmlSerializer.DeserializeFromStream(Type
type, Stream stream) at
ServiceStack.WebHost.Endpoints.Support.EndpointHandlerBase.CreateContentTypeRequest(IHttpRequest
httpReq, Type requestType, String contentType) Stack Trace
at ServiceStack.WebHost.Endpoints.Support.EndpointHandlerBase.CreateContentTypeRequest(IHttpRequest
httpReq, Type requestType, String contentType) at
ServiceStack.WebHost.Endpoints.RestHandler.GetRequest(IHttpRequest
httpReq, IRestPath restPath) at
ServiceStack.WebHost.Endpoints.RestHandler.ProcessRequest(IHttpRequest
httpReq, IHttpResponse httpRes, String operationName)
Check the SOAP Limitations to ensure you're creating Services that can be sent in SOAP, i.e. If the Request DTO is called GetProductDetails then it must return a Response DTO called GetProductDetailsResponse. Also you need to ensure your DTO's have [DataContract] on all types and [DataMember] on all public properties and that your DTO's are in a single namespace.
The other issue with this request is that you're using the SOAP requests to wrong endpoint address. i.e. The SOAP endpoint is either /soap11 or /soap12. In the above example it's attempting to send it to the /GetProductDetails custom HTTP route, when for SOAP it needs to be either /soap11 or soap12.
If you did just want to just send XML over HTTP then you should just send the raw XML payload which you can find out what it looks like by serializing the Request DTO, e.g:
string requestXml = new GetProductDetails { ... }.ToXml();
Which you can easily send with HTTP Utils:
var responseXml = "http://localhost/ServiceStackAPI/GetProductDetails"
.PostXmlToUrl(xmlRequest);
var responseDto = responseXml.FromXml<GetProductDetailsResponse>();
Assuming you have a [Route("/GetProductDetails")] defined on the GetProductDetails Request DTO.

Resources