I need to connect to a server we have here at the library so that I can use some internal API features.
This sever uses OWIN to authenticate, and I need to use cookies to help with the authentication. I've looked through a ton of examples on SO on how to use HttpClient with cookies, but I'm obviously still doing something wrong.
I set up this little test app, to try it, but I keep on getting an Interal Server Error:
{StatusCode: 500, ReasonPhrase: 'Internal Server Error', Version: 1.1,
Content: System.Net.Http.StreamContent, Headers:{
Content-Security-Policy: script-src 'unsafe-eval' 'self'
cdnjs.cloudflare.com X-SourceFiles:
=?UTF-8?B?kjsdf83r3748ryefhkr4389r=?= X-UA-Compatible: IE=edge X-Frame-Options: SAMEORIGIN Date: Mon, 15 Aug 2016 14:37:28 GMT
Server: Microsoft-IIS/10.0 Content-Length: 3312 Content-Type:
application/json; charset=utf-8}}
Here is my little test app:
static void Main(string[] args)
{
Console.WriteLine("Connecting to Spock Library System...");
GenerateBookLoan();
}
private static void GenerateBookLoan()
{
DateTime dueDate_Begin = DateTime.Today;
DateTime dueDate_End = DateTime.Today.AddDays(13);
var dueDate_Range = new Dictionary<string, string>
{
{"BeginDueDate", dueDate_Begin.ToString()},
{"EndDueDate", dueDate_End.ToString()}
};
var json = JsonConvert.SerializeObject(dueDate_Range);
var credentials = new NetworkCredential("library_API", "xxxxxxxxxxxxxx");
var handler = new HttpClientHandler { Credentials = credentials, UseCookies = true };
var cookies = new CookieContainer();
handler.CookieContainer = cookies;
using (var http = new HttpClient(handler))
{
try
{
http.BaseAddress = new Uri("http://localhost:63432/");
IEnumerable<Cookie> responseCookies = cookies.GetCookies(http.BaseAddress).Cast<Cookie>();
HttpResponseMessage response = http.PostAsync("api/books/loans/add",
new StringContent(json, Encoding.UTF8, "application/json")).Result;
Console.WriteLine(response);
//do stuff//
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
Console.WriteLine("Done.");
Console.ReadLine();
}
I need some advice on where to go next. I've read through a ton of answers on here, but I can't seem to find one that fits my needs.
Thanks!
Related
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.
I have developed a C# application that calls a REST service existing in some PC in the network.
This is the code to make a request:
public async Task<bool> OpenDoorAsync(string name, int delay)
{
var data = await CallApiAsync("api/door/remoteOpenByName", new Dictionary<string, string> { { "doorName", name }, { "interval", delay.ToString() } });
return data.IsSuccess;
}
private async Task<ResponseData> CallApiAsync(string endPoint, Dictionary<string, string> parameters)
{
try
{
using (HttpClient client = new HttpClient())
{
client.Timeout = TimeSpan.FromSeconds(30);
client.DefaultRequestHeaders.Connection.ParseAdd("keep-alive");
var content = new StringContent(string.Empty, Encoding.UTF8, "application/json");
string fullUri = "http://192.168.0.122:8088/api/door/remoteOpenByName?doorName=10.185.85.237-1&interval=5&access_token=1234";
HttpResponseMessage response = await client.PostAsync(fullUri, content);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<ResponseData>(responseBody);
}
}
catch (Exception ex)
{
OnError("Existió un error al realizar la llamada.", ex);
return new ResponseData()
{
message = "failed"
};
}
}
Entry point is OpenDoorAsync, called this way, from a Winform form:
await _device.OpenDoorAsync(TxtNombrePuerta.Text.Trim(), IntInterval.Value);
Well, after the execution of PostAsync method, a HTTP 500 error is returned:
{StatusCode: 500, ReasonPhrase: 'Internal Server Error', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Connection: close
Date: Thu, 28 Jan 2021 21:06:35 GMT
Set-Cookie: JSESSIONID=4062B932CDB44B4CA3FCCC275937AC15; Path=/; HttpOnly
Server: Apache-Coyote/1.1
Content-Length: 2580
Content-Language: en
Content-Type: text/html; charset=utf-8
}}
However, if I make the same request using Google Chrome RESTED extension, it works perfectly:
Just in case, I analyzed Google Chrome developer tools after the RESTED call and I have not found anything weird. I thought maybe I missed to send something else in the headers.
Does anybody know what is happening with the call from the C# application? Clearly, I am not doing something that RESTED is.
I don't really know why it does not work when using HttpClient class, however, I solved the problem installling an using RestSharp NuGet package.
Finally, the code was reduced to this:
private ResponseData CallApi(string endPoint, Dictionary<string, string> parameters)
{
try
{
string fullUri = $"http://{GetServerIp()}:{((MainWindow)MainWindow).ServerPort}/{endPoint}?{GetQueryParameters(parameters)}";
var client = new RestClient(fullUri);
var request = new RestRequest(Method.POST);
var response = client.Execute(request);
return JsonConvert.DeserializeObject<ResponseData>(response.Content);
}
catch (Exception ex)
{
OnError("Existió un error al realizar la llamada.", ex);
return new ResponseData()
{
message = "failed"
};
}
}
Your working example is passing cookies, which may be required for the API you're calling.
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;
}
}
}
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);