Authentication error using token with c# httpclient - c#

I have a HttpClient that I am using to use a REST API. However I am having trouble setting up the Authorization header. I need to set the header to the token I received from signin method, but when I use it in another method I get an authentication error.
var invoiceObj = new InvoiceUploadObj { dataFile = file, credential = "", domain = "" };
var invoiceObjSerialized = JsonConvert.SerializeObject(invoiceObj);
var data = new StringContent(invoiceObjSerialized, Encoding.UTF8, "application/json");
HttpClient clientDemoWS = new HttpClient();
clientDemoWS.BaseAddress = new Uri("https://www.nameservice.com");
clientDemoWS.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
clientDemoWS.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Authorization", String.Format("Bearer {0}", bearer_token));
HttpResponseMessage responseUpload = clientDemoWS.PostAsync("/services/invoice/upload", data).Result;
The status code is 200, but when I deserialize the server response I get an authentication error as error description.
Checking with Fiddler I see that this is the header:
POST /services/invoice/upload HTTP/1.1
Accept: application/json
Authorization: Bearer eyJ0eXAi......
Content-Type: application/json; charset=utf-8
Host: www.nameservice.com
Content-Length: 4623
Expect: 100-continue
Where am I wrong?

Related

Salesforce Account Creation via REST POST API Is Always Failing With Error "Bad Request" Via C#

I'm trying to write a simple console based application with the help from C# to create Salesforce standard objects like Accounts, Contacts, Leads etc. As a starting point I tried to create account using following code but it is always failing with below reason.
Code Snippet -
// Here string requestbody = "{\"name\":\"Testrun Limited\", \"city\":\"Delhi\"}"
public static string SFPostCall2(string requestbody)
{
string responseresult = string.Empty;
using ( var client = new HttpClient())
{
// Headers
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", SalesforceClient.AuthToken);
client.DefaultRequestHeaders.Add("X-PrettyPrint", "1");
// Contents
Uri URL = new Uri("https://mysalesforce-env.salesforce.com/services/data/v55.0/sobjects/account");
var data = new StringContent(requestbody, Encoding.UTF8, "application/json");
var response = client.PostAsync(URL, data).Result;
response.EnsureSuccessStatusCode();
responseresult = response.Content.ReadAsStringAsync().Result;
}
return responseresult;
}
Error Details -
System.Net.Http.HttpRequestException: 'Response status code does not
indicate success: 400 (Bad Request).'
Response Body -
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{
Date: Wed, 29 Jun 2022 12:08:46 GMT
Set-Cookie: CookieConsentPolicy=0:1; path=/; expires=Thu, 29-Jun-2023 12:08:46 GMT; Max-Age=31536000
Set-Cookie: LSKey-c$CookieConsentPolicy=0:1; path=/; expires=Thu, 29-Jun-2023 12:08:46 GMT; Max-Age=31536000
Set-Cookie: BrowserId=OfVYGvekEeyahU8Wnegiow; domain=.salesforce.com; path=/; expires=Thu, 29-Jun-2023 12:08:46 GMT; Max-Age=31536000
Strict-Transport-Security: max-age=63072000; includeSubDomains
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Robots-Tag: none
Cache-Control: no-store, must-revalidate, no-cache, max-age=0, private
Sforce-Limit-Info: api-usage=16/15000
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8
}
}
Please Note :-
I have successfully got the Salesforce Token while using the GET request.
Same request with same parameters in POSTMAN is creating data successfully every time
I have also tried using
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls but still no
help
Same request via JavaScript is also creating data from IE browser { to avoid CORS error }.
I have also tried to change the content to JSON object using following code, but still got same error -
var dataobj = new
{
Name = "Testrun Limited",
City = "Delhi"
};
var jsondataobj = JsonConvert.SerializeObject(dataobj);
HttpContent c_content = new StringContent(jsondataobj, Encoding.UTF8, "application/json");
This style worked for me.
HttpRequestMessage requestUpdate = new HttpRequestMessage(HttpMethod.Post, url);
requestUpdate.Headers.Add("Authorization", "Bearer " + SFAuthToken);
requestUpdate.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
requestUpdate.Content = contentUpdate;
HttpClient client = new HttpClient();
HttpResponseMessage asyncResponse = client.SendAsync(requestUpdate).Result;
var content = asyncResponse.Content.ReadAsStringAsync();

Google API Oauth2 Refresh token bad request 400

having an issue refreshing an existing token by using the Google API with Xamarin C#. I guess something is wrong with my code, but I cannot find what.
(Note: I added newline characters at the & to make the requests more readable. Hope everyone will be cool with that.)
Request Auth URI
https://accounts.google.com/o/oauth2/v2/auth?
scope=https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/calendar.events&
include_granted=true&
response_type=code&
redirect_uri=com.googleusercontent.apps.MYOAUTHCLIENTID:/oauth2redirect&
client_id=MYOAUTHCLIENTID.apps.googleusercontent.com&
access_type=offline
Redirect URI
com.googleusercontent.apps.MYOAUTHCLIENTID:/oauth2redirect
Response Authentication Code
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Request token URI
https://oauth2.googleapis.com/token?
code=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&
redirect_uri=com.googleusercontent.apps.MYOAUTHCLIENTID:/oauth2redirect&
client_id=MYOAUTHCLIENTID.apps.googleusercontent.com&
grant_type=authorization_code
Response token
{
"access_token": "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY",
"expires_in": 3599,
"refresh_token": "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
"scope": "https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/calendar.events",
"token_type": "Bearer"
}
Now I can use the "access_token" in my HTTP header requests and work with the calendar without problems. I also can revoke the token, but not refreshing it.
Try to refresh token
var dict = new Dictionary<string, string>();
dict.Add("Content-Type", "application/x-www-form-urlencoded");
var contentHeader = new FormUrlEncodedContent(dict);
HttpClient refreshTokenClient = new HttpClient();
// Doing this because someone wrote that this helped in his case. Did not help :/
System.Net.ServicePointManager.SecurityProtocol |=
SecurityProtocolType.Tls12 |
SecurityProtocolType.Tls11 |
SecurityProtocolType.Tls;
// uriTokenRequest has the type URI with this value
// {https://oauth2.googleapis.com/token?
// code=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&
// client_id=MYOAUTHCLIENTID.apps.googleusercontent.com&
// grant_type=refresh_token&
// refresh_token=ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ}
using (var result = await refreshTokenClient.PostAsync(uriTokenRequest, contentHeader))
if (result.IsSuccessStatusCode) // This will be FALSE
{
...
}
The response
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Accept-Ranges: none
Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
Cache-Control: no-store, must-revalidate, no-cache, max-age=0
Date: Sun, 11 Jul 2021 13:46:09 GMT
Pragma: no-cache
Server: scaffolding
Server: on
Server: HTTPServer2
Transfer-Encoding: chunked
Vary: X-Origin
Vary: Referer
Vary: Origin
Vary: Accept-Encoding
X-Android-Received-Millis: 1626011170088
X-Android-Response-Source: NETWORK 400
X-Android-Selected-Protocol: http/1.1
X-Android-Sent-Millis: 1626011170050
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 0
Content-Type: application/json; charset=utf-8
Expires: Mon, 01 Jan 1990 00:00:00 GMT
}}
The Google API documentation https://developers.google.com/identity/protocols/oauth2/native-app#offline does say about a "client_secret". But with the response_type "code" I never received such. I've read the documentation now so many times that I am blind to it.
Or does "code" not require a token refresh?
Any ideas?
EDIT: "code" does require a token refresh, since the token does expire, as the result property "expires_in" already tell.
EDIT 2: As by Cherry Bu - MSFT suggested I peeked at the Xamarin.Auth source code and found some differences, that I adapted. Unfortunately I am still not able to succeed. This is my latest try:
var queryValues = new Dictionary<string, string>();
//queryValues.Add("Content-Type", "application/x-www-form-urlencoded");
queryValues.Add("client_id", Constants.OAuthClientId);
queryValues.Add("code", _authenticationCode);
queryValues.Add("refresh_token", _refresh_token);
queryValues.Add("grant_type", "refresh_token");
var httpContent = new FormUrlEncodedContent(queryValues);
HttpClient refreshTokenClient = new HttpClient();
//if (!string.IsNullOrEmpty(_accessToken) && !string.IsNullOrEmpty(_accessTokenType))
//{
// refreshTokenClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(_accessTokenType, _accessToken);
//}
using (var response = await refreshTokenClient.PostAsync("https://accounts.google.com/o/oauth2/token", httpContent).ConfigureAwait(false))
{
if (response.IsSuccessStatusCode) --> This will be false
{
...
}
}
The correct http end point to refresh an access token can be found below
HTTP POST https://accounts.google.com/o/oauth2/token
client_id={ClientId}&client_secret={ClientSecret}&refresh_token=1/ffYmfI0sjR54Ft9oupubLzrJhD1hZS5tWQcyAvNECCA&grant_type=refresh_token
I actually have an example of using the google .net client library with Xamarin i had to adapt the authorization to it. I will have a look around see if i can find the code.
Fixed it. 2 issues:
1 The DefaultRequestHeader did not work.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(GoogleOAuth.AccessToken, GoogleOAuth.AccessTokenType);
Even when the object itself did look alright it did not work. I replaced it with that:
HttpClient client = new HttpClient();
var requestMessage = new HttpRequestMessage(HttpMethod.Post, calenderUri);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(GoogleOAuth.AccessTokenType, GoogleOAuth.AccessToken); // both are strings. ("Bearer", "yaa...")
requestMessage.Content = data;
var response = await client.SendAsync(requestMessage);
if (!response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
throw new Exception(content);
}
After that I only had to do one more thing
2 Removed "code" and "client_secret" from the request data. The result:
using (HttpClient client = new HttpClient())
{
var googleData = new Dictionary<string, string>();
googleData.Add("client_id", GoogleOAuth.ClientId);
googleData.Add("refresh_token", GoogleOAuth.RefreshToken);
googleData.Add("grant_type", "refresh_token");
var data = new FormUrlEncodedContent(googleData);
var requestMessage = new HttpRequestMessage(HttpMethod.Post, Constants.GoogleTokenUri);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(GoogleOAuth.AccessTokenType, GoogleOAuth.AccessToken);
requestMessage.Content = data;
var response = await client.SendAsync(requestMessage);
if (response.IsSuccessStatusCode)
{
// do something
}
}
For weeks I tried to get it running and was close to insanity. Hope this saves someones nerves.

use HttpClient to set the Content-Type to "application/json" and add object to the body

I'm trying to create the following post using HttpClient, using postman its working correctly but I cant seem to replicate it in code. I need to set the header Content-Type to application/json and have an object in the body of the post.
POST https://mycompanyurl.com/authenticate
HEADERS
Key: Content-Type, Value: application/json
BODY
{
"username": "someusername",
"password": "somepassword"
}
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://companyurl.com");
var serializedObject = JsonConvert.SerializeObject(
new {username = "username", password = "password" });
var request = new HttpRequestMessage(HttpMethod.Post, "authenticate");
request.Content = new StringContent(serializedObject, Encoding.UTF8,"application/json");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
}
Using the reverse proxy in fidder I can capture the raw call from postman which works, the rest api returns a result as expected:
POST http://127.0.0.1:8888/v1/authenticate HTTP/1.1 Content-Type: application/json;charset=UTF-8 cache-control: no-cache Postman-Token: 4db8f2dd-cbf0-413c-ad5b-20af0543a31d User-Agent: PostmanRuntime/7.6.0 Accept: / Host: 127.0.0.1:8888 accept-encoding: gzip, deflate content-length: 87 Connection: keep-alive
{"username":"username","password":"password"}
My call from HttpClient and using fiddler is below, This does not work, returns 200 but its not working correctly, data is not being returned, I cant see anything differences in the payload that will make the rest api not respond as expected.
POST http://127.0.0.1:8888/v1/authenticate HTTP/1.1 Content-Type: application/json; charset=utf-8 Host: 127.0.0.1:8888 Content-Length: 87 Expect: 100-continue Connection: Keep-Alive
{"username":"username","password":"password"}
The logic below should generate the same working request signature provided in your example (which was posted as an Answer, please edit your Question instead), and therefore should work:
var clientHandler = new HttpClientHandler
{
AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate,
AllowAutoRedirect = false
};
using (var client = new HttpClient(clientHandler, true))
{
client.BaseAddress = new Uri("http://127.0.0.1:8888/v1/");
client.DefaultRequestHeaders.Add("cache-control", "no-cache");
client.DefaultRequestHeaders.Add("Postman-Token", "db8f2dd-cbf0-413c-ad5b-20af0543a31d");
client.DefaultRequestHeaders.Add("User-Agent", "PostmanRuntime/7.6.0");
client.DefaultRequestHeaders.Add("Accept", "*/*");
client.DefaultRequestHeaders.ExpectContinue = false;
var serializedObject = JsonConvert.SerializeObject(
new { username = "username", password = "password" }
);
var request = new HttpRequestMessage(HttpMethod.Post, "authenticate")
{
Content = new StringContent(serializedObject, Encoding.UTF8, "application/json")
};
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
}
It will create the following request:
POST http://127.0.0.1:8888/v1/authenticate HTTP/1.1
cache-control: no-cache
Postman-Token: db8f2dd-cbf0-413c-ad5b-20af0543a31d
User-Agent: PostmanRuntime/7.6.0
Accept: */*
Content-Type: application/json; charset=utf-8
Host: 127.0.0.1:8888
Content-Length: 45
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
{"username":"username","password":"password"}
Hope this helps.

Azure graph API / c# patch URL

I am attempting to write a password reset application c# and the graph API. I have set permissions for the application within Azure, receive a valid token, and can request information.
I am receiving a 400 Bad Request response when attempting to perform the reset. I believe I am forming the URL incorrectly. Here is the response I receive followed by my code.
Thanks in advance!
Response: StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Transfer-Encoding: chunked
request-id: omitted
client-request-id: omitted
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"North Central US","Slice":"SliceA","Ring":"3","ScaleUnit":"002","Host":"AGSFE_IN_29","ADSiteName":"CHI"}}
Duration: 43.0949
Strict-Transport-Security: max-age=31536000
Cache-Control: private
Date: Mon, 02 Apr 2018 18:06:06 GMT
Content-Type: application/json
}
private static async Task ResetPasswordAsync(HttpClient client, string UPN)
{
var payload = new
{
accountEnabled = true,
passwordProfile = new
{
forceChangePasswordNextSignIn = true,
password = "Password!"
}
};
var payloadJSON = JsonConvert.SerializeObject(payload);
Console.WriteLine(payloadJSON);
HttpMethod method = new HttpMethod("PATCH");
string requestUrl = $"https://graph.microsoft.com/v1.0/users/{UPN}?api-version=1.6";
var request = new HttpRequestMessage(method, requestUrl)
{
Content = new StringContent($"{payloadJSON}", Encoding.UTF8, "application/json")
};
var response = await client.SendAsync(request);
Console.WriteLine("Response: " + response);
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException(response.ReasonPhrase);
}
}
According to 400 Bad Request, we could know that there is something wrong with http request. In your case, you could use Fiddler to catch the htt prequest, we could get Query parameter api-version not allowed. So you could remove the api version from the requesturl.
We could get more information about update use from Graph Update user API
PATCH /users/{id | userPrincipalName}
We also need to add the Authorization in the request header.
string requestUrl = $"https://graph.microsoft.com/v1.0/users/{UPN}";
var token ="Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFCSGg0...."
...
request.Headers.Add("Authorization", token);
var response = await client.SendAsync(request);

How to add access-control-allow-methods to method in C# POST

I am trying to sending a POST to a java web-service with my windows phone app using this c# code:
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new
MediaTypeWithQualityHeaderValue("application/json"));
var requestContent = new StringContent(json);
requestContent.Headers.ContentType = new
MediaTypeWithQualityHeaderValue("application/json");
var response = await client.PostAsync(requestUri, requestContent);
//...
}
but I am getting a 400 Bad Request and sending this header:
POST [myreq] HTTP/1.1
Content-Type: application/json
Content-Length: 340
Accept-Encoding: identity
Accept: application/json
User-Agent: NativeHost
Host: [myhost]
Connection: Keep-Alive
Pragma: no-cache
and the only difference that I see from a valid similar (to the same web service) android java request is this line in my header:
access-control-allow-methods=[POST]
How to include this access-control-allow-methods with C#?
for future help
client.DefaultRequestHeaders.Add("Access-Control-Allow-Methods", "POST");

Categories