Unauthorized error when posting data in Azure Eventhub - c#

I am doing a POC on sending data to Azure event hub using C#. The data will be posted over http unlike the Eventhub client. As per the requirements I cam creating a SAStoken to be embedded in http header. However when posting I get 501, Unauthorized access error. Not sure where I am going wrong. Here is the code that I have written
public static async Task<HttpResponseMessage> SendDataUsingHttp()
{
// Namespace info.
var serviceNamespace = "myeventhubs";
var hubName = "eventhub1";
var url = string.Format("{0}/publishers/{1}/messages", hubName, 1);
//var url = string.Format("{0}/messages", hubName);
var baseUri = new
Uri(string.Format("https://{0}.servicebus.windows.net/"
, serviceNamespace));
var SharedAccessKeyName = "All";
var SharedAccessKey = "<shared access key>";
var sasToken =
createSasToken(baseUri.ToString(),SharedAccessKeyName,
SharedAccessKey);
var evtData = new
{
Temperature = new Random().Next(20, 50)
};
var payload = JsonConvert.SerializeObject(evtData);
// Create client.
var httpClient = new HttpClient
{
BaseAddress = baseUri
};
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Authorization = new
System.Net.Http.Headers.AuthenticationHeaderValue
("SharedAccessSignature", sasToken);
var content = new StringContent(payload, Encoding.UTF8);
content.Headers.ContentType = new
System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
HttpResponseMessage result = null;
try
{
result = await httpClient.PostAsync(url, content);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return result;
}
private static string createSasToken(string baseUri, string keyName,
string key)
{
TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
var week = 60 * 60 * 24 * 7;
var expiration = Convert.ToString((int)sinceEpoch.TotalSeconds +
week);
string stringToSign = HttpUtility.UrlEncode(baseUri) + "\n" +
expiration;
HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key)); //--
var signature =
Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(
stringToSign)));
var sasToken = String.Format(CultureInfo.InvariantCulture,
"SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}",
HttpUtility.UrlEncode(baseUri), HttpUtility.UrlEncode(signature),
expiration, keyName);
return sasToken;
}
result
{StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1,
Content: System.Net.Http.StreamContent, Headers: {
Transfer-Encoding: chunked Strict- Transport-Security:
max-age=31536000 Date: Sun, 17 Jun 2018 08:35:43 GMT Server:
Microsoft-HTTPAPI/2.0 Content-Type: application/xml; charset=utf-8
}} Content: {System.Net.Http.StreamContent} Headers:
{Transfer-Encoding: chunked Strict-Transport-Security:
max-age=31536000 Date: Sun, 17 Jun 2018 08:35:43 GMT Server:
Microsoft-HTTPAPI/2.0

Based on your createSasToken method, it would generate the authorization header value with the following format:
Authorization: SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}
For your request, you also specify the following code:
System.Net.Http.Headers.AuthenticationHeaderValue("SharedAccessSignature", sasToken);
Using Fiddler to capture the network traces, you would find that your authorization header value in your sent request would look like this:
Authorization: SharedAccessSignature SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}
For your code, you need to adjust the format of your sasToken under the createSasToken method.
Moreover, the baseUri parameter of createSasToken method needs to be the full request path you are requesting. So you need to modify the related code under SendDataUsingHttp for generating the token as follows:
var sasToken =createSasToken(baseUri+url, SharedAccessKeyName, SharedAccessKey);

Related

C# REST Api always return 401 status code when i am calling API by HttpClient

I am working with Nasdaq Fund Network Data Service first time. i am calling their one of the API where passing user id,pwd and access key but always getting 401 status code. i am not able to figure out what is wrong in my http call. please some one have a look at the code and tell me where i made the mistake for which i am getting 401 status code instead of right response.
here is my sample code where i could not share actual credentials and access key.
giving the code
string url = "sample url";
Uri u = new Uri(url);
string username = "test1";
string password = "test2";
string accessKey = "myaccesskey";
var payload = new Dictionary<string, string>
{
{"username", username},
{"password", password},
{ "accessKey", accessKey}
};
string strPayload = JsonConvert.SerializeObject(payload);
//HttpContent c = new StringContent(strPayload, Encoding.UTF8, "application/json");
HttpContent c = new StringContent(strPayload, Encoding.UTF8, "application/x-www-form-urlencoded");
var response = string.Empty;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
using (var client = new HttpClient())
{
HttpRequestMessage request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = u,
Content = c
};
var result = client.SendAsync(request).Result;
if (result.IsSuccessStatusCode)
{
response = result.StatusCode.ToString();
}
}
This Error i am getting
{StatusCode: 401, ReasonPhrase: 'Check Username/Password or Access
Key', Version: 1.1, Content: System.Net.Http.StreamContent, Headers: {
Pragma: no-cache X-Frame-Options: SAMEORIGIN Cache-Control:
no-cache Date: Wed, 10 Aug 2022 11:55:36 GMT Content-Length: 0
Expires: -1 }}
Try the following in order to extract the JWT token you receive as a response.
var url = "https://nfn.nasdaq.com/servicecall/tempsession";
var formDataDictionary = new Dictionary<string, string>
{
{ "username", "test1"},
{ "password", "test2"},
{ "accessKey", "myaccesskey"}
};
var formData = new FormUrlEncodedContent(formDataDictionary);
using (var client = new HttpClient())
{
var response = await client.PostAsync(url, formData);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
var responseBody = JObject.Parse(result);
var accessToken = responseBody["data']"].Value<string>();
}

Generates the token but does not execute the azure url

I need to get this service from c # https://learn.microsoft.com/en-us/rest/api/power-bi-embedded/capacities/resume.
For this I use the GetAccessToken () function to obtain the authentication token.
public string GetAccessToken()
{
try
{
string tenantId = "ae4e3a81-xxxxxxxxxxxxxxxxxx";
string clientId = "ca8393b2-xxxxxxxxxxxxxxxxxxxxxxxxx";
string clientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxx";
string authContextURL = "https://login.windows.net/" + tenantId;
var authenticationContext = new AuthenticationContext(authContextURL);
var credential = new ClientCredential(clientId: clientId, clientSecret: clientSecret);
var result = authenticationContext.AcquireTokenAsync(resource: "https://management.azure.com/", clientCredential: credential).Result;
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
string token = result.AccessToken.ToString();
return token;
}
catch (Exception ex)
{
return ex.ToString();
}
}
Then I use the DoWork () method to use the token and be able to consume the azure ulr.
public void DoWork()
{
string Subscription = "afdbf38c-c33c-45ea-8e9b-===========";
string token = GetAccessToken();
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
client.BaseAddress = new Uri("https://management.azure.com/");
// Now you can party with your HttpClient!
using (var response = client.PostAsync($"/subscriptions/{Subscription}/resourceGroups/Reportes/providers/Microsoft.PowerBIDedicated/capacities/powerbiembeddedjre1/resume?api-version=2017-10-01", null).Result)
{
response.EnsureSuccessStatusCode();
}
}
}
But when I go to check the response it generates an error message:
{StatusCode: 403, ReasonPhrase: 'Forbidden', Version: 1.1, Content:
System.Net.Http.StreamContent, Headers: {Pragma: no-cache
x-ms-failure-cause: gateway x-ms-request-id:
02d9df62-0529-4a7f-b492-36ce8584aad6 x-ms-correlation-request-id:
02d9df62-0529-4a7f-b492-36ce8584aad6 x-ms-routing-request-id:
CANADACENTRAL: 20191114T163756Z: 02d9df62-052949-7 36ce8584aad6
Strict-Transport-Security: max-age = 31536000; includeSubDomains
X-Content-Type-Options: nosniff Connection: close Cache-Control:
no-cache Date: Thu, 14 Nov 2019 16:37:55 GMT Content-Length: 503
Content-Type: application / json; charset = utf-8 Expires: -1}}
What do I need to solve this problem?
looks like an issue with the bearer token, try client.SetBearerToken(token);
alternatively it's correctly sending back 403 because you do not meet the requirements for the claims (tenet/client issue).

Uploading files to GLPI server POST_MAX_SIZE

I'm trying to POST a Document (any file type) to a GLPI server through API REST.
Here is what I'm doing:
private void button11_Click(object sender, EventArgs e)
{
using (var client = new HttpClient())
{
using (var content = new MultipartFormDataContent())
{
var rcontent = string.Empty;
// HEADERS (URL + Access Tokens)
//string _ContentType = "multipart/form-data";
string _Uri = Properties.Settings.Default.GLPI_URL + "/Document/";
client.BaseAddress = new Uri(_Uri);
//client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(_ContentType));
client.DefaultRequestHeaders.Add("Session-Token", Properties.Settings.Default.GLPI_SESSION_TOKEN);
client.DefaultRequestHeaders.Add("App-Token", Properties.Settings.Default.GLPI_APP_TOKEN);
// JSON Content (input string array with file uploaded informations)
JSON_C.DocumentAdder JSONContent = new JSON_C.DocumentAdder();
JSONContent.name = "sth";
JSONContent._filename = filebytes;
HttpContent _JSONContent = new StringContent("uploadManifest={\"input\": " + JsonConvert.SerializeObject(JSONContent).ToString() + "}", Encoding.UTF8, "application/json");
content.Add(_JSONContent);
// File Content in bytes
var fileContent = new ByteArrayContent(filebytes);
fileContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("_filename") { FileName = filepath };
//fileContent.ReadAsByteArrayAsync();
content.Add(fileContent);
// Request
HttpResponseMessage reponse;
var _Method = new HttpMethod("POST");
reponse = client.PostAsync(_Uri, content).Result;
// Request response
rcontent = reponse.Content.ReadAsStringAsync().Result;
textBox2.Text = reponse.ToString() + Environment.NewLine + rcontent.ToString();
}
}
}
But this is what I got in response:
StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Connection: close
Cache-Control: no-store, must-revalidate, no-cache
Date: Mon, 26 Nov 2018 12:50:09 GMT
Server: Apache/2.4.29
Server: (Ubuntu)
Content-Length: 61
Content-Type: application/json; charset=UTF-8
Expires: Mon, 26 Jul 1997 05:00:00 GM
}
With:
["ERROR_UPLOAD_FILE_TOO_BIG_POST_MAX_SIZE","The file seems too big"]
The file I'm trying to upload is 592bytes! Max overall limit in one request is 2Mo. And post_max_size in php.ini is "8M", the same result after I changed it to "0" (for no limit at all). And then set it to 20M to match upload_max_filesize (/etc/php/7.2/apache2/php.ini).
upload_max_filesize_.. is also "20M"
If anyone finding this post and needs help, here is how i managed to succeed :
After separatly creating a "Session-Token", and using "RestSharp".
// Upload
var RSClient = new RestClient(Properties.Settings.Default.GLPI_URL);
var request = new RestRequest("Document", Method.POST);
request.AddHeader("Session-Token", Properties.Settings.Default.GLPI_SESSION_TOKEN);
request.AddHeader("App-Token", Properties.Settings.Default.GLPI_APP_TOKEN);
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "multipart/form-data");
request.AddQueryParameter("uploadManifest", "{\"input\": {\"name\": \"UploadFileTest\", \"_filename\": \"GiletsJaunes.jpg\"}}");
request.AddFile("test", #"C:\path\to\File.jpg");
IRestResponse response = RSClient.Execute(request);
var content = response.Content;
textBox2.Text = textBox2.Text + Environment.NewLine + content;
Details :
I couldn't use RestSharp.Authenticator = new SimpleAuthenticator for some reasons, so i added these Auth params with AddHeader.
I couldn't use a Serialised Json string in a new StringContent, because of AddQueryParameter, so i wrote it manually.
Alleluyah.

Forbidden access when calling twitter api geo/search.json

I'm new in using twitter API, I've successfully called:
https://api.twitter.com/1.1/statuses/user_timeline.json
api.twitter.com/1.1/followers/list.json
but when I call:
https://api.twitter.com/1.1/geo/search.json?query=Pakistan
I get Forbidden access.
Following is my request:
Method: GET, RequestUri: 'https://api.twitter.com/1.1/geo/search.json?query=Pakistan', Version: 1.1, Content: , Headers:
{
Authorization: Bearer xxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzz%aaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbb
}
And the response that I get is:
StatusCode: 403, ReasonPhrase: 'Forbidden', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
strict-transport-security: max-age=631138519
x-connection-hash: 3a7f405036803861a700cef30f7b1e7f
x-response-time: 107
Date: Fri, 05 May 2017 03:18:15 GMT
Set-Cookie: guest_id=v1%3A149395429589966721; Domain=.twitter.com; Path=/; Expires=Sun, 05-May-2019 03:18:15 UTC
Server: tsa_o
Content-Length: 91
Content-Type: application/json; charset=utf-8
}
If you're interested in looking at my C# code that I'm using, here you go:
public async Task<IEnumerable<string>> GetTweetsByLatLong(double latitude, double longitude, int count, string accessToken = null)
{
if (accessToken == null)
{
accessToken = await GetAccessToken();
}
var requestUserTimeline = new HttpRequestMessage(HttpMethod.Get, string.Format("https://api.twitter.com/1.1/geo/search.json?query=Pakistan"));
requestUserTimeline.Headers.Add("Authorization", "Bearer " + accessToken);
var httpClient = new HttpClient();
HttpResponseMessage responseUserTimeLine = await httpClient.SendAsync(requestUserTimeline);
if (responseUserTimeLine.IsSuccessStatusCode)
{
var serializer = new JavaScriptSerializer();
dynamic json = ((serializer.Deserialize<object>(await responseUserTimeLine.Content.ReadAsStringAsync())) as Dictionary<string, object>).Values.ElementAt(0);
//new System.Collections.Generic.Mscorlib_DictionaryValueCollectionDebugView<string, object>((json as Dictionary<string, object>).Values).Items[0]
var enumerableTwitts = (json as IEnumerable<dynamic>);
if (enumerableTwitts == null)
{
return null;
}
return enumerableTwitts.Select(t => (string)(t["name"].ToString()));
}
else
{
return new string[] { responseUserTimeLine.ToString() };
}
}
public async Task<string> GetAccessToken()
{
var httpClient = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.twitter.com/oauth2/token ");
var customerInfo = Convert.ToBase64String(new UTF8Encoding().GetBytes(OAuthConsumerKey + ":" + OAuthConsumerSecret));
request.Headers.Add("Authorization", "Basic " + customerInfo);
request.Content = new StringContent("grant_type=client_credentials", Encoding.UTF8, "application/x-www-form-urlencoded");
HttpResponseMessage response = await httpClient.SendAsync(request);
string json = await response.Content.ReadAsStringAsync();
var serializer = new JavaScriptSerializer();
dynamic item = serializer.Deserialize<object>(json);
return item["access_token"];
}
I believe it is because you are using Application-only authentication by providing a Bearer token.
See "Requires Authentication" in both
https://dev.twitter.com/rest/reference/get/geo/search
and
https://dev.twitter.com/rest/reference/get/statuses/user_timeline
And read https://dev.twitter.com/oauth

Passing querystring parameters in OpenPaths.cc api call with OAuth not working

I am trying to call the OpenPaths.cc rest service API which requires 2-legged OAuth. For this I use the DevDefined.OAuth library (I tried the nuget package and the latest from github). It works when I don't pass parameters in the querystring but returns 400 NOT AUTHORIZED when I do pass parameters.
Working sample with no parameters:
public class OpenPathsRequest
{
private const string accessKey = "your personal access key";
private const string secretKey = "your personal secret";
private const string url = "https://openpaths.cc/api/1";
private OAuthSession session;
public OpenPathsRequest()
{
var consumerContext = new OAuthConsumerContext
{
ConsumerKey = accessKey,
ConsumerSecret = secretKey,
SignatureMethod = SignatureMethod.HmacSha1,
UseHeaderForOAuthParameters = true
};
session = new OAuthSession(consumerContext, url, url, url);
}
private string GetWebResponseAsString(HttpWebResponse response)
{
Encoding enc = System.Text.Encoding.GetEncoding(1252);
StreamReader loResponseStream = new StreamReader(response.GetResponseStream(), enc);
return loResponseStream.ReadToEnd();
}
public string GetResponse()
{
HttpWebResponse response = session.Request().Get().ForUrl(url).ToWebResponse();
var result = GetWebResponseAsString(response);
return result;
}
}
class Program
{
static void Main(string[] args)
{
// create new OpenPathsRequest and get result
var request = new OpenPathsRequest();
var response = request.GetResponse();
Console.WriteLine(response);
Console.WriteLine("Press any key...");
Console.ReadKey();
}
}
But when I change the GetResponse method and pass in 2 parameters (start_time and end_time) like so:
public string GetResponse()
{
HttpWebResponse response = session.Request().Get().ForUrl(url).WithQueryParameters(
new { start_time = 1364962612, end_time = 1364991412 }).ToWebResponse();
var result = GetWebResponseAsString(response);
return result;
}
Which results in the following HTTP request (consumer key omitted):
GET https://openpaths.cc/api/1?start_time=1364962612&end_time=1364991412 HTTP/1.1
Authorization: OAuth oauth_nonce="7b5da37a-6227-4ded-ae8b-a695e789ef90",oauth_consumer_key="**********",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1365058952",oauth_version="1.0",oauth_signature="tAk4KMj%2FsiG6BTLSmvDNKXbBpNs%3D"
Host: openpaths.cc
Connection: Keep-Alive
I get the an error response:
HTTP/1.1 400 Bad Request
Date: Thu, 04 Apr 2013 07:03:26 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Content-Length: 19
Server: TornadoServer/2.0
Set-Cookie: _xsrf=bf20487382b64eeb8646d31b0770db85; Path=/
Set-Cookie: session_id=ZTk2Mjk1MzIzNWNiMmRjMTY1ZmY5Y2ExNWUwMGY5ZTAxZmY1NGIyODljZGJiNzRlMmIyMWI4NTA3YzUwYWJlYg==|1365059006|7459a7ff95039279e9686ceb76b58918fd9f3e48; expires=Thu, 04 Apr 2013 07:18:26 GMT; Path=/
400: NOT AUTHORIZED
All help would be appreciated very much. Thanks in advance.
Harmen
Solved! Apparently OpenPaths.cc api requires signing WITHOUT the additional query parameters in the signature base string. I believe this is not according to OAuth specs, is it? Anyway, with DevDefined.OAuth i can easily solve this by calling SignWithoutToken before calling WithQueryParameters like so:
public string GetResponse()
{
HttpWebResponse response = session.Request().Get().ForUrl(url).
SignWithoutToken().
WithQueryParameters(new { start_time = 1364962612, end_time = 1364991412 }).
ToWebResponse();
var result = GetWebResponseAsString(response);
return result;
}

Categories