Can't authenticate correctly with headers (HttpClient) - c#

I've checking many forums but I can't make it work. I'm trying to authenticate with headers to an url that will return a JSON string if authentication were successful. In Postman I simply used Get method with username and password in header to get the JSON data. What changes do I need to make my following C# code achieve same thing? I think I even failed to add username and password into headers.
public async Task<string> LogMeIn(string username, string password)
{
var client = new HttpClient {
BaseAddress = new Uri("http://x.com")
};
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var content = new FormUrlEncodedContent(new[] {
new KeyValuePair<string,string>("grant_type","password"),
new KeyValuePair<string,string>("Username ", username),
new KeyValuePair<string,string>("Password", password)
});
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync("/login", content); //should it be GetAsync?
var jsonResp = await response.Content.ReadAsStringAsync();
var jsonResult = JsonConvert.DeserializeObject<JsonResult>(jsonResp); //JsonResult = class for json
return jsonResult.token;
}
}

Related

How To Pass Username and Password in the Body for Wordpress JWT Auth API

I have Wordpress and JWT Auth installed and I want to validate a username and password.
I simply want to pass the username and password through the JWT Token API using my C# code.
Here's the API link that's working in Postman:
https://xxxx/wp-json/jwt-auth/v1/token
I can only pass the username and password in the "Body" "Form-Data" fields with the key values "username" and "password" because parameters don't work with JWTAuth.
How do I pass these values as a "Body" "Form-Data" value using C#?
Here's my code:
string JWTAPI = "https://xxxx/wp-json/jwt-auth/v1/token";
var client = new HttpClient();
var uri = JWTAPI;
var jsonContent = "{ \"username\": \"test\", \"password\": \"test\"}";
string json = JsonConvert.SerializeObject(jsonContent);
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
var response_status = await client.PostAsync(uri, content);
var response_result = await response_status.Content.ReadAsStringAsync();
Never mind. I got it to work!
Just needed to remove:
string json = JsonConvert.SerializeObject(jsonContent);
TokenRequest Token;
public async Task TakeToken()
{
client = new HttpClient();
var values = new Dictionary<string, string>
{
{ "username", "root" },
{ "password", "1111" }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync($"{BaseUrl}/wp-json/jwt-auth/v1/token", content);
var responseString = await response.Content.ReadAsStringAsync();
Token = Newtonsoft.Json.JsonConvert.DeserializeObject<TokenRequest>(responseString);
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + Token.token);
}
public class TokenRequest
{
public string token;
public string user_email;
public string user_nicename;
public string user_display_name;
}

How to add Authentication token in HttpRequest in Adaptive dialog?

I am using Botframework adaptive dialog template (c#). I already obtained a token from a HttpRequest and saved it as a conversation state property conversation.token, now I am trying to use this token to make another API call with HttpRequest. But from the official document of HttpRequest Class, it seems there is no options to add the authentication token. I tried to add the token in the Headers, but did not work, it showed 401 Unauthorized error. How should the authorization be handled in HttpRequest in adaptive dialog?
new HttpRequest()
{
Url = "http://example.com/json",
ResultProperty = "conversation.httpResponse",
Method = HttpRequest.HttpMethod.GET,
ResponseType = HttpRequest.ResponseTypes.Json,
Headers = new Dictionary<string, AdaptiveExpressions.Properties.StringExpression>()
{
{"Authorization", "Bearer ${conversation.token.content.token}"},
},
},
new SendActivity("${conversation.httpResponse}"),
Instead of using HttpRequest, I made the API call inside CodeAction with custom code.
First make a POST request to get the token, then make a GET request to call the main API. In the GET request, the authorization can be added in this way: client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);.
new CodeAction(async (dc, options) =>
{
var my_jsondata = new
{
Username = "username",
Password = "password"
};
var json = JsonConvert.SerializeObject(my_jsondata);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var Tokenurl = "https://example.com/token?HTTP/1.1";
using var Tokenclient = new HttpClient();
var Tokenresponse = await Tokenclient.PostAsync(Tokenurl, data);
string Toeknresult = Tokenresponse.Content.ReadAsStringAsync().Result;
var Tokenjo = JObject.Parse(Tokenresult);
using var client = new HttpClient();
var url = "https://example.com/mainapi?HTTP/1.1";
var accessToken = Tokenjo["token"];
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
var response = await client.GetAsync(url);
string result = response.Content.ReadAsStringAsync().Result;
dc.State.SetValue("conversation.httpresponse", response);
dc.State.SetValue("conversation.result", result);
return await dc.EndDialogAsync();
}),

How do you use Basic Authentication with System.Net.Http.HttpClient?

I'm trying to implement a rest client in c# .net core that needs to first do Basic Authentication, then leverage a Bearer token in subsequent requests.
When I try to do Basic Authentication in combination with client.PostAsync with a FormUrlEncodedContent object, I'm getting an exception:
System.InvalidOperationException occurred in System.Net.Http.dll: 'Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.'
//setup reusable http client
HttpClient client = new HttpClient();
Uri baseUri = new Uri(url);
client.BaseAddress = baseUri;
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.ConnectionClose = true;
//Post body content
var values = new List<KeyValuePair<string,string>>();
values.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
var content = new FormUrlEncodedContent(values);
//Basic Authentication
var authenticationString = $"{clientId}:{clientSecret}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(authenticationString));
content.Headers.Add("Authorization", $"Basic {base64EncodedAuthenticationString}");
//make the request
var task = client.PostAsync("/oauth2/token",content);
var response = task.Result;
response.EnsureSuccessStatusCode();
string responseBody = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseBody);
Exception has occurred: CLR/System.InvalidOperationException
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Net.Http.dll: 'Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.'
at System.Net.Http.Headers.HttpHeaders.GetHeaderDescriptor(String name)
at System.Net.Http.Headers.HttpHeaders.Add(String name, String value)
It looks like you can't use PostAsync and have access to mess with the Headers for authentication. I had to use an HttpRequestMessage and SendAsync.
//setup reusable http client
HttpClient client = new HttpClient();
Uri baseUri = new Uri(url);
client.BaseAddress = baseUri;
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.ConnectionClose = true;
//Post body content
var values = new List<KeyValuePair<string, string>>();
values.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
var content = new FormUrlEncodedContent(values);
var authenticationString = $"{clientId}:{clientSecret}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(authenticationString));
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/oauth2/token");
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", base64EncodedAuthenticationString);
requestMessage.Content = content;
//make the request
var task = client.SendAsync(requestMessage);
var response = task.Result;
response.EnsureSuccessStatusCode();
string responseBody = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseBody);
It's not a good practice to create HttpClients explicitly from your calling code.
Please use HttpClientFactory that simplifies a lot of things.
However, if you want to use basic authentication, just create an HttpRequestMessage and add the following header:
var request = new HttpRequestMessage(HttpMethod.Post, getPath)
{
Content = new FormUrlEncodedContent(values)
};
request.Headers.Authorization = new BasicAuthenticationHeaderValue("username", "password");
// other settings
If you decide to use a recommended IHttpClientFactory it's even simpler:
serviceCollection.AddHttpClient(c =>
{
c.BaseAddress = new Uri("your base url");
c.SetBasicAuthentication("username", "password");
})
Don't encode the whole authentication string - encode the "Username:Password" expression and append the result to the "Basic " prefix.
var authenticationString = $"{clientId}:{clientSecret}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.UTF8.GetBytes(authenticationString));
content.Headers.Add("Authorization", "Basic " + base64EncodedAuthenticationString);
Also, consider using just ASCII encoding - the UTF8 may not be understood by the server unless you add a charset declaration to the header.
Wikipedia seems to cover this quite well.
The specific problem is this line (below)
content.Headers.Add("Authorization", $"Basic {base64EncodedAuthenticationString}");
This fails because HttpContent.Headers (System.Net.Http.Headers.HttpContentHeaders) is only for headers that are content-specific, such as Content-Type, Content-Length, and so on.
You've stated that you can't use DefaultRequestHeaders because you only need it for a single request - but you also can't use it with PostAsync - only SendAsync provided you construct the HttpRequestMessage yourself, as per your own answer and #NeilMoss' answer - but you could use an extension-method in future.
But for the benefit of other readers, another alternative is to add a new extension method based on the existing PostAsync, which is actually really simple (only 3 lines!):
public Task<HttpResponseMessage> PostAsync( this HttpClient httpClient, Uri requestUri, HttpContent content, String basicUserName, String basicPassword, String? challengeCharSet = null, CancellationToken cancellationToken = default )
{
if( basicUserName.IndexOf(':') > -1 ) throw new ArgumentException( message: "RFC 7617 states that usernames cannot contain colons.", paramName: nameof(basicUserName) );
HttpRequestMessage httpRequestMessage = new HttpRequestMessage( HttpMethod.Post, requestUri );
httpRequestMessage.Content = content;
//
Encoding encoding = Encoding.ASCII;
if( challengeCharSet != null )
{
try
{
encoding = Encoding.GetEncoding( challengeCharSet );
}
catch
{
encoding = Encoding.ASCII;
}
}
httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue(
scheme : "Basic",
parameter: Convert.ToBase64String( encoding.GetBytes( userName + ":" + password ) )
);
return SendAsync( httpRequestMessage, cancellationToken );
}
Usage:
HttpClient httpClient = ...
using( HttpResponseMessage response = await httpClient.PostAsync( uri, content, basicUserName: "AzureDiamond", basicPassword: "hunter2" ).ConfigureAwait(false) )
{
// ...
}
Just something to add that I struggled with, which I only experienced with Basic authentication endpoints. If you add Json as StringContent then it adds a charset=utf-8, this often return a BadRequest 400.
Here is the code I got to fix this: reference:
https://dzone.com/articles/httpclient-how-to-remove-charset-from-content-type
using (var client = new HttpClient())
using (var content = new StringContent(ParseJSON(data), Encoding.Default, "application/json"))
{
//Remove UTF-8 Charset causing BadRequest 400
content.Headers.ContentType.CharSet = "";
var clientId = "client";
var clientSecret = "secret";
var authenticationString = $"{clientId}:{clientSecret}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.UTF8.GetBytes(authenticationString));
client.DefaultRequestHeaders.TryAddWithoutValidation(authHeader, authorization);
var response = await client.PostAsync(url, content);
return response;
}
I have resolve this by using below code, that serve my purpose also. Added Code for both Get/Post, this will help you. Moreover I have added one more Header key. So to pass extra data to header. Hope that will resolve your issue.
class Program {
private static readonly string Username = "test";
private static readonly string Password = "test#123";
static void Main(string[] args) {
var response = Login();
}
public static async Task Login()
{
var anotherKey ="test";
HttpClient httpClient = new HttpClient
{
BaseAddress = new Uri("https://google.com/")
};
httpClient.DefaultRequestHeaders.Add($"Authorization", $"Basic {Base64Encode($"{Username}:{Password}")}");
httpClient.DefaultRequestHeaders.Add($"anotherKey", $"{anotherKey}");
HttpResponseMessage httpResponseMessage = await httpClient.GetAsync("user/123").ConfigureAwait(false);
// For Get Method
var response= await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
// For Post Method
User user = new User (1,"ABC");
HttpResponseMessage httpResponseMessage = await httpClient.PostAsJsonAsync("/post", user).ConfigureAwait(false);
UserDetail userDetail = await httpResponseMessage.Content.ReadAsAsync<UserDetail>().ConfigureAwait(false);
}
}
Using .NET 6, I use the HttpClient.DefaultRequestHeaders.Authorization property to set the Authorization header.
// This example will send a signing request to the RightSignature API
var api = "https://api.rightsignature.com/public/v2/sending_requests";
// requestJson is the serialized JSON request body
var contentData = new StringContent(requestJson, Encoding.UTF8, "application/json");
// Instantiate client (for testing), use Microsoft's guidelines in production
var client = new HttpClient();
// Use basic auth, the token has already been converted to base64
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", tokenB64);
try
{
var response = await client.PostAsync(api, contentData);
}
...
Good luck!

How to call web api with multiple parameter in c# xamarin forms

I know this is a repeated question. I have tried many examples showed on stackoverflow. But the API is still not calling and it shows 404 not found error. I have tried the below examples,
Example 1:
using (var loginData = new MultipartFormDataContent())
{
loginData.Add(new StringContent(JsonConvert.SerializeObject(login.Email)), "emailId");
loginData.Add(new StringContent(JsonConvert.SerializeObject(this.Password)), "password");
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await httpClient.PostAsync("http://6cxsfera.ngrok.io/api/users/authenticate", loginData);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
Application.Current.MainPage = new AppShell();
}
}
Example 2:
JObject loginData = new JObject();
loginData.Add("emailId", login.Email);
loginData.Add("password", this.Password);
var httpClient = new HttpClient();
var httpContent = new StringContent(loginData.ToString(), Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync("http://6cxsfera.ngrok.io/api/users/authenticate", httpContent);
My API:
[Route("authenticate")]
[HttpPost]
public HttpResponseMessage Login(HttpRequestMessage request, string emailId, string password)
{
}
Please suggest me to pass multiple parameters to web API in c# xamarin forms.
You can generate code by postman. Then check hat you are missing. you can even use same code generated by postman. https://learning.getpostman.com/docs/postman/sending_api_requests/generate_code_snippets/
just install restSharp nuget package.. it will work with postman generated code.
Instead of creating JObject create a class which holds your emailId and password property like below.
public class UserModel
{
public string emailId {get;set;}
public string password {get;set;}
}
now later instantiate UserModel and assign values like below
UserModel user = new UserModel
{
emailId = "emailId",
password = "password"
};
Serialize UserModel like below
string serializedModel = await Task.Run(() => JsonConvert.SerializeObject(user));
var contents = new StringContent(serializedModel);
contents.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
Make a Post Request
var response = await httpClient.PostAsync("http://6cxsfera.ngrok.io/api/users/authenticate", contents);
If the above solution doesn't work try changing your Web Api method route like below
Route["api/users/authenticate"]
Hope this will solve your issue... :)
you could try this:
using (var httpclient = new HttpClient())
{
var formcontent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string,string>("emailId",emailId),
new KeyValuePair<string, string>("password","password")
});
var request = await httpclient .PostAsync("http://6cxsfera.ngrok.io/api/users/authenticate", formcontent);
request.EnsureSuccessStatusCode();
var response = await request.Content.ReadAsStringAsync();
}

Make a post Request with azure new rest api (ressource manager)

I want to start my VM using the post Uri as described here https://msdn.microsoft.com/en-us/library/azure/mt163628.aspx
Since i don't have body in my request i get 403 frobidden. I can make a get Request without problem. Here is my code
public void StartVM()
{
string subscriptionid = ConfigurationManager.AppSettings["SubscriptionID"];
string resssourcegroup = ConfigurationManager.AppSettings["ressourgroupename"];
string vmname = ConfigurationManager.AppSettings["VMName"];
string apiversion = ConfigurationManager.AppSettings["apiversion"];
var reqstring = string.Format(ConfigurationManager.AppSettings["apirestcall"] + "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/virtualMachines/{2}/start?api-version={3}", subscriptionid, resssourcegroup, vmname, apiversion);
string result = PostRequest(reqstring);
}
public string PostRequest(string url)
{
string content = null;
using (HttpClient client = new HttpClient())
{
StringContent stringcontent = new StringContent(string.Empty);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string token = GetAccessToken();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
HttpResponseMessage response = client.PostAsync(url, stringcontent).Result;
if (response.IsSuccessStatusCode)
{
content = response.Content.ReadAsStringAsync().Result;
}
}
return content;
}
i've also tried this in the PostRequest
var values = new Dictionary<string, string>
{
{ "api-version", ConfigurationManager.AppSettings["apiversion"] }
};
var posteddata = new FormUrlEncodedContent(values);
HttpResponseMessage response = client.PostAsync(url, posteddata).Result;
with url=string.Format(ConfigurationManager.AppSettings["apirestcall"] + "subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/virtualMachines/{2}/start", subscriptionid, resssourcegroup, vmname);
I Get 400 Bad request
I found the solution. Needed to add role in Azure to allow starting/stopping the VM. That is why i received 4.3 forbidden.
Thank you

Categories