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.
Related
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();
I have my own C# Web API service (targeting .NET 5.0) that is sitting on the same server where an on-premise SharePoint 2019 is located. My code calls the built-in SharePoint REST API in order to do searches and return results (the outside world will only be accessing my service).
I have no problem calling the SharePoint REST API from IE on that server.
Within my Web API service (https) I call the SharePoint Rest service (http) using the same url that worked in IE
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(sharePointSearchServiceBaseUrl);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes("account:password");
string val = System.Convert.ToBase64String(plainTextBytes);
client.DefaultRequestHeaders.Add("Authorization", "Basic " + val);
HttpResponseMessage response = await client.GetAsync("search/query?querytext='(oil*)'&rowlimit=100");
if (response.IsSuccessStatusCode)
{
var objResponse = response.Content.ReadAsStringAsync().Result;
return Content(objResponse);
}
Unfortunately, the results from the client.GetAsync are always as follows:
INFO 2021-01-07 01:24:18 StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{
Server: Microsoft-IIS/10.0
SPRequestGuid: 47049f9f-8244-1032-40ac-07df48a24632
request-id: 47049f9f-8244-1032-40ac-07df48a24632
X-Frame-Options: SAMEORIGIN
SPRequestDuration: 6
SPIisLatency: 2
WWW-Authenticate: NTLM
X-Powered-By: ASP.NET
MicrosoftSharePointTeamServices: 16.0.0.10368
X-Content-Type-Options: nosniff
X-MS-InvokeApp: 1; RequireReadOnly
Date: Thu, 07 Jan 2021 18:24:18 GMT
Content-Length: 0
}
I have tried passing all SharePoint accounts I have, and all of them have given me the same 401.
Any insight into resolving this would be much appreciated.
I was able to find a solution for this. The most important line is the NetworkCredential one. I tried many other approaches, but this is the only one that worked for me.
using (var handler = new HttpClientHandler())
{
handler.Credentials = new NetworkCredential(account, password, domain);
using (var client = new HttpClient(handler))
{
var requri = new Uri("http://localhost:30001/_api/search/query?querytext='(oil*)'&rowlimit=100");
HttpResponseMessage response = await client.GetAsync(requri);
if (response.IsSuccessStatusCode)
{
var objResponse = response.Content.ReadAsStringAsync().Result;
_logger.LogInformation("Results are: " + objResponse);
return Content(objResponse);
}
else
{
_logger.LogInformation("Sharepoint service call - was NOT successful");
_logger.LogInformation(response.ToString());
return null;
}
}
}
I have an application that requests the marketing cloud for an access token to use in subsequent requests to access the API. This was designed last year and working fine till last week. The same request has been failing since 02/22. Here is the sample code to request the token:
public static async Task<string> GetAuthorizationToken(string ClientId, string ClientSecret)
{
string strAuthorizationToken = string.Empty;
HttpClient client = new HttpClient();
var dictParams = new Dictionary<string, string>()
{
{ "clientId", ClientId }, {"clientSecret", ClientSecret }
};
var content = new FormUrlEncodedContent(dictParams);
var response = await client.PostAsync("https://auth.exacttargetapis.com/v1/requestToken", content);
if (response.IsSuccessStatusCode)
{
var strresponse = await response.Content.ReadAsStringAsync();
//dynamic objResult = JsonConvert.DeserializeObject<dynamic>(strresponse);
//strAuthorizationToken = objResult.accessToken;
}
return strAuthorizationToken;
}
GetAuthorizationToken("*********", "*******").GetAwaiter().GetResult();
Here is the error I am getting from the API:
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
X-Mashery-Responder: 02-26
Vary: Origin
X-Mashery-Message-ID: f4cec199-2e7e-49e2-88e0-6673ffe849ed
strict-transport-security: max-age=15552000; preload
Content-Security-Policy: upgrade-insecure-requests
x-xss-protection: 1; mode=block
x-frame-options: DENY
x-content-type-options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Connection: close
Cache-Control: no-store, must-revalidate, no-cache, max-age=0, private
Date: Tue, 26 Feb 2019 16:42:29 GMT
Server: Apache
Content-Length: 223
Content-Type: application/json; charset=UTF-8
}}
I need to know what has been changed or what I need to fix in this code to be able to access the API again. Please help how I can make this work again.
Thanks in advance.
I am posting the answer as it is resolved and might be helpful for someone else who has a similar issue with sending http requests.
I had to add the following line of code to specify the security protocol to use:
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12 | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls;
OK. So this is really starting to baffle me now. I can get this to work on one UWP app, but not this one.
I have this piece of code to post:
public async Task<string> SubmitDataWithTokenAsync(string url, string token)
{
var httpClient = new HttpClient();
HttpResponseMessage response;
try
{
var root = new
{
fields = new Dictionary<string, string>
{
//General Parameters...
//Inspection Parameters...
//Startup Parameters...
//Mechanical Parameters...
//Electrical Parameters...
//SCR Parameters...
//Shutdown Parameters...
}
};
var s = new JsonSerializerSettings { DateFormatHandling = DateFormatHandling.MicrosoftDateFormat };
var content = JsonConvert.SerializeObject(root, s);
var request = new HttpRequestMessage(HttpMethod.Post, url);
//Add the token in Authorization header
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent(content, Encoding.UTF8, "application/json");
response = await httpClient.SendAsync(request);
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
catch (Exception ex)
{
return ex.ToString();
}
}
}
the content comes back just fine, so it seems....
"{\"fields\":{\"Date\":\"8/16/2018 2:18:48 PM -04:00\",\"Maximo\":null,\"IBX\":\"DC4\",\"Generator\":\"Generator D\",\"AirQuality\":\"Red / Unhealthy\",\"Engineer\":\"Kassim Ganiyou\",\"MT1Level\":null,\"MT2Level\":null,\"StartDTLevel\":null,\"BC1V\":null,\"BC1A\":null,\"BC2V\":null,\"BC2A\":null,\"StartCoolantTEmp\":null,\"StartHours\":null,\"Reason\":null,\"InspectionNotes\":null,\"StartTime\":null,\"CrankV1\":null,\"CrankV2\":null,\"Emissions\":null,\"SCRStartTime\":null,\"OilPressure\":null,\"CoolantTemp\":null,\"BatteryVolt\":null,\"LeftExhTemp\":null,\"RightExhTemp\":null,\"ABVolts\":null,\"BCVolts\":null,\"CAVolts\":null,\"AAmps\":null,\"BAmps\":null,\"CAmps\":null,\"KW\":null,\"Frequency\":null,\"SCROutletTemp\":null,\"NOx\":null,\"UReaFLow\":null,\"Alarms\":null,\"SCRSTopTime\":null,\"StopTime\":null,\"StopHours\":null}}"
The request comes back:
{Method: POST, RequestUri: 'https://graph.microsoft.com/v1.0/sites/root/lists/A07CEC93-XXXX-XXXX-XXXX-0F756D2EF63A/items', Version: 2.0, Content: System.Net.Http.StringContent, Headers:
{
Authorization: Bearer eyJ0eX...PUQ
Content-Type: application/json; charset=utf-8
Content-Length: 603
}}
But then the response is:
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
client-request-id: bb26e6fe-4fae-41ae-921d-aeb39063bd8e
Strict-Transport-Security: max-age=31536000
request-id: bb26e6fe-4fae-41ae-921d-aeb39063bd8e
Transfer-Encoding: chunked
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"East US","Slice":"SliceC","Ring":"5","ScaleUnit":"001","Host":"AGSFE_IN_1","ADSiteName":"EUS"}}
Duration: 319.5961
Cache-Control: private
Date: Thu, 16 Aug 2018 20:03:10 GMT
Content-Type: application/json
}}
I have another UWP app going to the same sharepoint site and I get a Status 201 no problem. I am just not seeing where my issue is in this case.
This is why it is good to take a break from a project and come back to it later.
I found out that I had mistyped ONE letter on the SharePoint list. So today when I reviewed the response code it pointed it our to me.
The code WAS good.
Thanks all.
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);