In my requests to Spotify's "get token" endpoint using .NET's HttpRequestMessage, I'm getting a 406 (Unacceptable) response.
Here's my post method (fyi, this is not optimized code -- more of a proof of concept):
public static async Task<string> PostForm(HttpClient httpClient, string url, List<KeyValuePair<string, string>> nameValueList)
{
using (var httpRequest = new HttpRequestMessage(HttpMethod.Post, url))
{
httpRequest.Content = new FormUrlEncodedContent(nameValueList);
using (var response = await httpClient.SendAsync(httpRequest))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
}
I'm calling that method with this:
public Token GetSpotifyToken()
{
var httpClient= new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
httpClient.DefaultRequestHeaders.Add("Authorization", "Basic MyApiCredsBase64EncodedYesIveConfirmedTheseWork=");
var url = "https://accounts.spotify.com/api/token";
var grantType = new KeyValuePair<string, string>("grant_type", "client_credentials");
return JsonConvert.DeserializeObject<Token>
(ApiRequest.PostForm(postClient, url,
new List<KeyValuePair<string, string>>() { grantType } )
.GetAwaiter()
.GetResult());
}
I know that the issue is related to how grant_type=client_credentials is getting added to the request. This method, which uses RestSharp, works fine:
public Token GetSpotifyToken()
{
var client = new RestClient("https://accounts.spotify.com/api/token");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", "Basic MyApiCredentialsBase64Encoded=");
request.AddParameter("undefined", "grant_type=client_credentials", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
return JsonConvert.DeserializeObject<Token>(response.Content);
}
But I've tried adding grant_type=client_credentials to the HttpRequestMessage object in a few different ways, without success.
Figured it out, right after I posted this.
In the above code, the line...
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
...was causing the problem. Omitting it allowed the code to work.
Related
Basically, I've been trying to authenticate through oauth2 on c# using restsharp, but I am receiving a bad request response, I'm not sure if it's something related to the API configuration or if it's something I'm missing In my code.
public string getToken(string email, string password)
{
var restclient = new RestClient(loginUrl);
RestRequest request = new RestRequest("request/oauth") { Method = Method.GET };
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("email", HttpUtility.UrlPathEncode(email));
request.AddParameter("password", HttpUtility.UrlPathEncode(password));
request.AddParameter("grant_type", HttpUtility.UrlPathEncode("password"));
var tResponse = restclient.Execute(request);
var responseJson = tResponse.Content;
string token = JsonConvert.DeserializeObject<Dictionary<string, object>>(responseJson)["access_token"].ToString();
return token;
}
this is the response when I execute that code
An this is the postman execution
Thanks!
I think there problem with adding parameters the way you are adding.
latest restsharp support this,
Also,avoid encoding of params by setting to false
var request = new RestRequest("resource", Method.GET);
request.AddQueryParameter("email", "test#test.com",false);
var restclient = new RestClient(loginUrl); I think you need to check your url.
Try this.. you OAuth is password grantype are your sure your not missing any credentials like client_id, scope and client_secret.
public static string getAccessToken(string usern, string pswd)
{
RestClient client = new RestClient(ConfigurationManager.AppSettings["TokenUrl"]);
RestRequest request = new RestRequest() { Method = Method.GET};
request.AddParameter("grant_type", "password", ParameterType.GetOrPost);
request.AddParameter("username", usern, ParameterType.GetOrPost);
request.AddParameter("password", pswd, ParameterType.GetOrPost);
IRestResponse response = client.Execute(request);
var responseJson = response.Content;
var token = JsonConvert.DeserializeObject<Dictionary<string, object>>(responseJson)["access_token"].ToString();
return token;
}
I make a login request to a nextcloud installation installed locally on my server.
I pass 3 parameters: requesttoken, password, shareToken. The request works perfectly fine in postman but fails in my C# code.
I get the following reponse
with a 412 status code.
"{\"message\":\"CSRF check failed\"}"
I copy pasted the c# code derived from my postman request which is like below. Any clues?
var client = new RestClient("https://nextcloud.xxxx.org/index.php/s/Sryt58drbe34/authenticate/showShare");
var request = new RestRequest(Method.POST);
request.AddHeader("Cache-Control", "no-cache");
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("undefined", "requesttoken=oq%2Fqo4Nx0eNvYyty5QNeJqPbjZxMCtn3bFvUfEi%2Fwcg%3D%3AlOWrxPECsptWMBwGnTYMQ%2FG3yPENe7iQQwz7EQOLmYA%3D&password=bb9AYoqWSmAxiCgvFusW&sharingToken=Sryt58drbe34", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
Your post should be something like this
var uri = "https://nextcloud.xxxx.org/index.php/s/Sryt58drbe34/authenticate/showShare"
var requestToken= "oq/qo4Nx0eNvYyty5QNeJqPbjZxMCtn3bFvUfEi/wcg=:lOWrxPECsptWMBwGnTYMQ/G3yPENe7iQQwz7EQOLmYA=";
var password = "bb9AYoqWSmAxiCgvFusW";
var shareToken = "Sryt58drbe34";
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("requesttoken", requestToken),
new KeyValuePair<string, string>("password", password ),
new KeyValuePair<string, string>("sharetoken ", shareToken )
});
var myHttpClient = new HttpClient();
var response = await myHttpClient.PostAsync(uri.ToString(), formContent);
I'm on Xamarin and would like to execute an HTML request to collect a token from a server I manage.
I can successfully execute the following Request on PostMan.
But once I try to code it it does not work.
I tried two different ways.
Firstly by using HTTPClient the following way:
HttpClient client = new HttpClient();
string url = AppConfiguration.CloudServer_URI + "/token";
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); // define the expected return format
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "/token");
request.Content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", "admin#example.com"),
new KeyValuePair<string, string>("password", "Admin#123465")
});
var response = await client.SendAsync(request);
This gives me well a response but with 400 - Bad request.
I cannot find in the HTTPResponse where I can get the details on the response body. But I strongly guest it is an "error": "unsupported_grant_type".
Then I tried another way with RestSharp and got the following implementation:
string URL = AppConfiguration.CloudServer_URI + "/token";
var client = new RestClient(URL);
var request = new RestRequest(Method.GET);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter("grant_type", "password", ParameterType.RequestBody);
request.AddParameter("username", "admin#example.com", ParameterType.RequestBody);
request.AddParameter("password", "Admin#123465", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
But again the result is the same - 400 - Bad request. And here I can see the response body which is "error": "unsupported_grant_type".
I have the feeling to miss something obvious but I don't see what.
I am using oAuth to authenticate my app. I managed to get a code, access_token and refresh_token. So the next step would be trying to get info about the current user.
public async void GetCurrentUser()
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", AccessToken);
var response = await client.GetAsync("https://oauth.reddit.com/api/v1/me");
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(json);
}
}
}
This is the method I am using to do that. However the response is always an 403 (Forbidden) error code. Any idea what could be wrong? The access_token is what I got when I made a request to https://oauth.reddit.com/api/v1/access_token
I think the token is correct because when I create the same request with Fiddler it works.
ANSWER:
Fixed it by adding a custom user-agent
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, _endpointUri + "me");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
request.Headers.Add("User-Agent", Uri.EscapeDataString("android:com.arnvanhoutte.redder:v1.2.3 (by /u/nerdiator)"));
var response = await client.SendAsync(request);
I have to send a delete command to a REST API service with JSON content using the HttpClient class and can't make this working.
API call:
DELETE /xxx/current
{
"authentication_token": ""
}
because I can't add any content into below statement:
HttpResponseMessage response = client.DeleteAsync(requestUri).Result;
I know how to make this work with RestSharp:
var request = new RestRequest {
Resource = "/xxx/current",
Method = Method.DELETE,
RequestFormat = DataFormat.Json
};
var jsonPayload = JsonConvert.SerializeObject(cancelDto, Formatting.Indented);
request.Parameters.Clear();
request.AddHeader("Content-type", "application/json");
request.AddHeader ("Accept", "application/json");
request.AddParameter ("application/json", jsonPayload, ParameterType.RequestBody);
var response = await client.ExecuteTaskAsync (request);
but I have get it done without RestSharp.
Although it might be late to answer this question but
I've faced a similar problem and the following code worked for me.
HttpRequestMessage request = new HttpRequestMessage
{
Content = new StringContent("[YOUR JSON GOES HERE]", Encoding.UTF8, "application/json"),
Method = HttpMethod.Delete,
RequestUri = new Uri("[YOUR URL GOES HERE]")
};
await httpClient.SendAsync(request);
UPDATE on .NET 5
.NET 5 introduced JsonContent. Here is an extension method using JsonContent:
public static async Task<HttpResponseMessage> DeleteAsJsonAsync<TValue>(this HttpClient httpClient, string requestUri, TValue value)
{
HttpRequestMessage request = new HttpRequestMessage
{
Content = JsonContent.Create(value),
Method = HttpMethod.Delete,
RequestUri = new Uri(requestUri, UriKind.Relative)
};
return await httpClient.SendAsync(request);
}
You can use these extension methods:
public static class HttpClientExtensions
{
public static Task<HttpResponseMessage> DeleteAsJsonAsync<T>(this HttpClient httpClient, string requestUri, T data)
=> httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Delete, requestUri) { Content = Serialize(data) });
public static Task<HttpResponseMessage> DeleteAsJsonAsync<T>(this HttpClient httpClient, string requestUri, T data, CancellationToken cancellationToken)
=> httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Delete, requestUri) { Content = Serialize(data) }, cancellationToken);
public static Task<HttpResponseMessage> DeleteAsJsonAsync<T>(this HttpClient httpClient, Uri requestUri, T data)
=> httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Delete, requestUri) { Content = Serialize(data) });
public static Task<HttpResponseMessage> DeleteAsJsonAsync<T>(this HttpClient httpClient, Uri requestUri, T data, CancellationToken cancellationToken)
=> httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Delete, requestUri) { Content = Serialize(data) }, cancellationToken);
private static HttpContent Serialize(object data) => new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
}
The answer from Farzan Hajian still didn't work for me, I could set the request content but it wasn't actually sent to the server.
As an alternative you could look at using the X-HTTP-Method-Override header. This tells the server that you want it to treat the request as if you sent a different verb than the one that you actually sent. You will have to ensure that the server handles this header correctly, but if it does you can just POST the request and add: X-HTTP-Method-Override:DELETE to the headers and it will be the equivalent of a DELETE request with a body.
Try with
Edited
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.your.url");
request.Method = "DELETE";
request.ContentType = "application/json";
request.Accept = "application/json";
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
string json = "{\"key\":\"value\"}";
streamWriter.Write(json);
streamWriter.Flush();
}
using (var httpResponse = (HttpWebResponse)request.GetResponse())
{
// do something with response
}
Here you can find very similar problem.
Edited
I am not sure that passing request body for DELETE request is good approach. Especially when this is only for your authentication purposes. I will prefer to put authentication_token to Headers. It is because in my solution I will not have to parse the whole request body to check that current request is correctly authenticated. What about other request types? Do you always pass authentication_token in request body?