Upload the file to CRM using C# and Web API - c#

I am trying to upload a PDF file to the file field in the CRM entity. I followed the following document: https://learn.microsoft.com/en-us/powerapps/developer/data-platform/file-attributes#upload-file-data
Implemented the code of OAuth to generate an Access token using a Client ID and Client Secret. But I am getting a Bad Request as a response.
Entity name: msnfp_request
File field name: bna_file
var fileStream = System.IO.File.OpenRead(<file path>);
var url = new Uri("https://mydev.crm.dynamics.com/api/data/v9.1/msnfp_request(a528f300-7b53-ec11-8c62-0022482a2e7a)/bna_file);
AuthenticationContext authContext = new AuthenticationContext("https://login.microsoftonline.com/<domain>");
ClientCredential credential = new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(<client id>,<client secret>);
AuthenticationResult result = authContext.AcquireToken("https://mydev.crm.dynamics.com/", credential);
using (var req = new HttpRequestMessage(new HttpMethod("PATCH"), url))
{
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
req.Content = new StreamContent(fileStream);
req.Content.Headers.Add("Content-Type", "application/octet-stream");
req.Content.Headers.Add("x-ms-file-name", "test.pdf");
HttpClient Client = new HttpClient();
using (var response = await Client.SendAsync(req))
{
response.EnsureSuccessStatusCode();
}
}
What am I doing wrong in the above code?

I will troubleshoot to see if you are able to find out issues step by step.
Make sure the authentication is working after passing the token, as you are not getting 401 it should be fine. But you can do a simple GET request to cross check the connectivity is working
The plural entity name could be a problem. You can find the status code text to see any inner exception like "The resource could not be found". Pls try in case entity plural name is msnfp_requests then it should be like https://mydev.crm.dynamics.com/api/data/v9.1/msnfp_requests(a528f300-7b53-ec11-8c62-0022482a2e7a)/bna_file (wild guess :))

Related

secure access to external web API .NET CORE

I have two web APIs applications developed in .Net core. I need to import Json data from the second application to the first. However,I have a security issue. I need to secure the access to the external API. How should I securely manage the connection between these two APIs.
For example, I need to secure the access to the URL in the code bellow => securely access to the covid API without another authentication.
PS: I'm using JWT token authentication in both applications
Best regards.
using (var client = new HttpClient())
{
string url = string.Format("https://covid19.mathdro.id/api");
var response = client.GetAsync(url).Result;
string responseAsString = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<CovidResult>(responseAsString);
}
If both APIs are protected by the same accessToken, then you can read the authorization header from the first request and pass it to the second request.
Something like this to read the header:
var authHeader = context.Request.Headers.Get("Authorization");
You should end up with authHeader equal to "Bearer ey...(a bunch of base64)"
Then add the auth header to the client:
var request = new HttpRequestMessage() {
RequestUri = new Uri("http://https://covid19.mathdro.id/api"),
Method = HttpMethod.Get,
};
...
request.Headers.Authorization.Add(new AuthenticationHeaderValue(authHeader));
var task = client.SendAsync(request)

How to diagnose a 401 error attempting to get an OAuth2 bearer token in c# .NET Core?

I have some limited skills in c++ and have recently moved in C# (asp.net) and azure Web services. As a PoC I'm trying to make REST calls into PayPal (which I'll need to be using professionally in 3 -6 months).
I've set up my personal PayPal account using the instructions here and I get a bearer token back using curl as described in the link. Awesome.
I'm now trying to do this from .NET Core C# and all I get is a 401 error. I've examined the request and it seems the same as the curl in terms of headers; the base64 encoded credentials I think I'm adding are the same as the ones in the verbose curl log (I examined the two base64 strings by eye) so it must be something I'm doing (or not doing) in the set up of the call. I'm looking for suggestions, pointers, or flat out laughter at the obvious mistake I've made.
I've set up what I believe to be a named client thus:
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("PayPal", c =>
{
c.BaseAddress = new Uri("https://api.sandbox.paypal.com/v1/");
c.DefaultRequestHeaders.Add("Accept", "application/json");
c.DefaultRequestHeaders.Add("Accept-Language", "en_US");
});
(with all the other stuff that comes free with VS under it omitted for brevity).
I attempt the call thus:
string clientCredString = CLIENTID + ":" + SECRET;
var clientCreds = System.Text.Encoding.UTF8.GetBytes(clientCredString);
var client = _clientFactory.CreateClient("PayPal");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", System.Convert.ToBase64String(clientCreds));
var messageBody = new Dictionary<string,string > ();
messageBody.Add("grant_type", "client_credientials");
var request = new HttpRequestMessage(HttpMethod.Get, "oauth2/token")
{
Content = new FormUrlEncodedContent(messageBody)
};
string token;
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
token = JsonConvert.DeserializeObject<string>(json);
}
else
{
throw new ApplicationException("Well that failed");
}
and get a 401 code for my trouble.
Suggestions for troubleshooting, better methods of doing this and laughter at my foolishness all welcomed.
Update:
I read the documentation, a couple of items stand out to me:
Requires a verb of post.
Uses FormUrlEncodedContent for client credentials.
Basic auth requires username and password (Client Id & Secret)
I believe the syntax should be:
var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "...");
request.Content = new Dictionary<string, string>() { "grant_type", "client_credentials" };
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", $"{Encoding.UTF8.GetBytes($"{id}:{secret}")}");
HttpResponseMEssage = response = await client.PostAsync(request);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
For the benefit of future readers:
It was, as suggested, an encoding problem. The line:
var clientCreds = System.Text.Encoding.UTF8.GetBytes(clientCredString);
needed to be
var clientCreds = System.Text.Encoding.ASCII.GetBytes(clientCredString);
It should also be noted that this particular operation requires a POST not a GET as I was using, but once I started sending properly encoded requests the errors started to make a lot more sense.

InvalidSignatureException with AWS Kinesis Video Stream C#

I am attempting to execute my own HTTP signed request since there is no SDK in C# for the PutMedia API for the AWS Kinesis Video Stream, but I am getting the following error message:
StatusCode: 403, ReasonPhrase: 'Forbidden'
x-amzn-ErrorType: InvalidSignatureException:http://internal.amazon.com/coral/com.amazon.coral.service/
Here is a gist of what my code looks like:
var streamName = "audio-stream-test";
var service = "kinesisvideo";
var endpoint = GetPutMediaEndpoint(streamName);
var host = GetHostFromEndpoint(endpoint);
var region = GetRegionFromEndpoint(endpoint);
var t = DateTime.UtcNow;
var canonical_uri = $"{endpoint}/putMedia";
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, new Uri(canonical_uri));
httpRequestMessage.Headers.Add("connection", "keep-alive");
httpRequestMessage.Headers.Add("host", host);
httpRequestMessage.Headers.Add("Transfer-Encoding", "chunked");
httpRequestMessage.Headers.Add("user-agent", "AWS-SDK-KVS/2.0.2");
httpRequestMessage.Headers.Add("x-amzn-fragment-acknowledgment-required", "1");
httpRequestMessage.Headers.Add("x-amzn-fragment-timecode-type", "ABSOLUTE");
httpRequestMessage.Headers.Add("x-amzn-producer-start-timestamp", (t - DateTime.MinValue).TotalMilliseconds.ToString());
httpRequestMessage.Headers.Add("x-amzn-stream-name", streamName);
httpRequestMessage.Headers.Add("x-amz-security-token", sessionToken);
var byteArray = File.ReadAllBytes(filePath);
var content = new ByteArrayContent(byteArray);
httpRequestMessage.Content = content;
var httpClient = new HttpClient();
var aws4RequestSigner = new AWS4RequestSigner(accessKey, secretAccessKey);
var signedHttpRequestMessage = aws4RequestSigner.Sign(httpRequestMessage, service, region).Result;
var httpResponseMessage = httpClient.SendAsync(signedHttpRequestMessage);
Screenshot of Error
I am using the Aws4RequestSigner NuGet package to sign the request. Any ideas what I am doing wrong here? Has anyone tried to use the AWS Kinesis Video Stream with C#/.NET successfully?
Two potential issues with the pseudo-code.
If using session token then the request signing should include the session token as well not only access key/secret access key combination.
The body of the PutMedia is "endless" as it streams out as a realtime stream. As such, the data shouldn't be included in the signature calculation.
This is answer to your question "the actual "content" is not being added to the stream. I see the Put Connection from KVS but no data added".
After you get 200 by setting http headers properly for the signing with below code, you need to have your content set in signedHttpRequestMessage.
var httpResponseMessage = httpClient.SendAsync(signedHttpRequestMessage);

Trouble downloadig an thumbnail image in O365 Video using C#

I am running into an odd issue with trying to access a thumbnail image stored on O365 video via C#. I can access the REST API with no issue at all, I just add the Authentication: Bearer <token> to the header and I am off an running. The trouble is with a basic image URL that I get back from a specific video.
https://<mytenant>.sharepoint.com/portals/Channel1/pVid/myvideo.mp4.PNG?VideoPreview=1
When I access that URL from a browser it works 100% of the time. When I try to access it via the httpclient object, I am getting a 401 Unauthorized error.
The best I can figure is that the authorization header token is not being honored when accessing a basic URL. Which makes me thing that I need something else like a cookie? However I cannot seem to figure out which one. Looking for any advice :)
Pass credentials instead and yes you need an authentication cookie. Here is a sample:
private static async Task<string>getWebTitle(string webUrl)
{
//Creating Password
const string PWD = "softjam.1";
const string USER = "bubu#zsis376.onmicrosoft.com";
const string RESTURL = "{0}/_api/web?$select=Title";
//Creating Credentials
var passWord = new SecureString();
foreach (var c in PWD) passWord.AppendChar(c);
var credential = new SharePointOnlineCredentials(USER, passWord);
//Creating Handler to allows the client to use credentials and cookie
using (var handler = new HttpClientHandler() { Credentials = credential })
{
//Getting authentication cookies
Uri uri = new Uri(webUrl);
handler.CookieContainer.SetCookies(uri, credential.GetAuthenticationCookie(uri));
//Invoking REST API
using (var client = new HttpClient(handler))
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(string.Format(RESTURL, webUrl)).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
string jsonData = await response.Content.ReadAsStringAsync();
return jsonData;
}
}
}

Performing a put or patch request with token

I've got a chunk of code:
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(String.Format("{0}:{1}", "Username", "password"))));
var method = new HttpMethod("PATCH");
var reqmsg = new HttpRequestMessage(method, uri)
{
Content = new StringContent(request, Encoding.UTF8, "application/json")
};
HttpResponseMessage response = await client.SendAsync(reqmsg);
This works fine using Basic authentication. I want to use a token though, and if I change the Authorization to use a webtoken:
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Token", WebToken);
I now get a 403 Forbidden error. If I'm doing a Post or a Get, the token works, but not for Patch or Put. I'm guessing the token is somehow being stripped off. is there a way around this?
You're getting a 403 error because the encoding is incorrect.
-facepalm-
You're using UTF8 while the default is ANCI... Doesn't make sense.

Categories