HttpClient and Impersonation - c#

From my web service (A) usng impersonation i would like call a WebAPI service (B) using HttpClient. But the service B always gets the system user of service A even though i do impersonation there.
var baseUri = "http://service/api/"
var handler = new HttpClientHandler { UseDefaultCredentials = true };
var client = new HttpClient(handler) { BaseAddress = baseUri };
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("ContentType", new List<string> { "application/json"});
var dataDto = new DataDto();
var json = JsonConvert.SerializeObject(dataDto );
var content = new StringContent(json);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync(SubUrl, content);
I know that Kerberos and SPN are set up correctly because it works using WebClient.
I think the problem is, that HttpClient.PostAsync creates a new task and therefore a new thread running under the credentials of the appPool of service A.
Does anyone know how i could force the task to run under the imperonated credentials?
I do not have access to the aspnet_config.config so the solution proveded here does not work for me.
Thanks a lot!
Tschuege

Related

C# to pause, turn on ssas server, backup cube.... how to?

I'm building some function apps in C# (via REST API) to make refreshes of tabular cube located on an azure ssas server. So far, no problem. However, I can't find a way to pause/start the ssas server (I saw some doc in powershell but I'd like to stay in C# so as not to mix languages)
Has anyone ever created anything like this?
I tried to make a POST suspend but no solution for now.
See the ResumeAzureAS() method here:
protected async Task<bool> ResumeAzureAS()
{
HttpClient client = new HttpClient();
var apiURI = new Uri(string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.AnalysisServices/servers/{2}/resume?api-version=2016-05-16", subscriptionID, resourcegroup, server));
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
HttpResponseMessage response = await client.PostAsync(apiURI.ToString(), null);
response.EnsureSuccessStatusCode();
return true;
}
The rest of the API calls (such as suspend) are documented here.
private async Task<string> AASAcquireToken()
{
// Get auth token and add the access token to the authorization header of the request.
string authority = "https://login.windows.net/" + tenant + "/oauth/authorize";
AuthenticationContext ac = new AuthenticationContext(authority);
ClientCredential cred = new ClientCredential(clientID, keyID);
AuthenticationResult ar = await ac.AcquireTokenAsync(audience, cred);
return ar.AccessToken;
}
With audience set as "https://management.azure.com"
and for the "pause" itself :
I use as servername the complete name mention in the portal azure as "asazure://northeurope.asazure.windows...."
For the version of the api , well I don't know where to find it so I use one I found on the net.
var apiURI = new Uri(string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.AnalysisServices/servers/{2}/suspend?api-version=2016-05-16", subscription, ressourceID, servername));
audience = "https://management.azure.com";
myClient.BaseAddress = new Uri(location);
myClient.DefaultRequestHeaders.Accept.Clear();
myClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
myClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await AASAcquireToken());
HttpResponseMessage response = await myClient.PostAsync(apiURI.ToString(), null);
var output = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode();
The right audience was :
audience = "https://management.core.windows.net/";

.NET Core Httpclient works but .Net Framework 4.7.2 httpclient doesn't

Sorry for the awful title, I'm not really sure how to phrase my issue in a short title format.
I'm trying to communicate with an external API. I make a basic authentication request to that API and get an x-csrf-token and a session token from the api.
I then make another request to that API, now using the x-csrf-token as a header and attach the session token to the header as "cookie".
The team that maintains the API sent me an example project that handles all of the above, and it looks like this:
public static async Task<string> Send(string apiname, string value)
{
// Fetch the authorization tokens from SAP
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(basePath);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(user + ":" + password)));
client.DefaultRequestHeaders.Add("x-csrf-token", "Fetch");
string csrfToken = "";
string sessionCookie = "";
HttpResponseMessage response = await client.GetAsync(string.Empty);
IEnumerable<string> values;
if (response.Headers.TryGetValues("x-csrf-token", out values))
{
csrfToken = values.FirstOrDefault();
}
if (response.Headers.TryGetValues("set-cookie", out values))
{
sessionCookie = values.Where(s => s.StartsWith("SAP_SESSION")).FirstOrDefault();
}
// Reinstantiate the HttpClient, adding the tokens we just got from SAP
client = new HttpClient();
client.DefaultRequestHeaders.Add("x-csrf-token", csrfToken);
client.DefaultRequestHeaders.Add("cookie", sessionCookie);
client.BaseAddress = new Uri(basePath);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Have to parse the string this way otherwise it'll break the dates
JToken token;
using (var sr = new StringReader(value))
using (var jr = new JsonTextReader(sr) { DateParseHandling = DateParseHandling.None })
{
token = JToken.ReadFrom(jr);
}
HttpResponseMessage response2 = await client.PostAsJsonAsync(apiname, token);
string responseBody = await response2.Content.ReadAsStringAsync();
return responseBody;
}
This all works great as a .NET Core webAPI (and also as a .netcore console app).
Interestingly enough (in my opinion anyway), when I use the exact same code in a .net 4.7.2 project, it doesn't append the "cookie" header properly, and so I'm getting an unauthorized redirect back from the API.
To be absolutely sure that I didn't change any code, I started from scratch with a brand new .netcore 2.0 console app and a brand new .net 4.7.2 console app and copy-pasted the exact same code and installed the same nuget packages (Newtonsoft.JSON and Microsoft.WebApi.Client). I inspected my web traffic with fiddler (seen below) and you can see that in .netcore, the cookie appends properly and everything works, but in .net 4.7.2, the API returns a redirect to authenticate.
HttpClient will eat the custom cookie if you do not setUseCookies to false,
using (var handler = new HttpClientHandler { UseCookies = false })
using (client = new HttpClient(handler) { BaseAddress = new Uri(Path) }){
client.DefaultRequestHeaders.Add("cookie", cookieValue);
}
It will try to use the cookie container and at the same time ignore any custom cookie headers, very frustrating behavior if you ask me.
.Net Framework uses Cookie Container.
Also core, perhaps its a better implementation then what you are doing now and more supported.
Please see cookie container docs
Small example:
var cookieContainer = new CookieContainer();
this.handler = new HttpClientHandler
{
CookieContainer = cookieContainer,
UseCookies = true
};
client = new HttpClient(handler);

Is it possible to use an HTTPClient with Windows authentication within an AWS Lambda function?

Is it possible to specify Windows credentials to HttpClient within an AWS Lambda function?
For example:
var handler = new HttpClientHandler {
Credentials = new NetworkCredential("username", "password", "domain")
};
var httpClient = new HttpClient(handler);
var content = new StringContent("<test>test</test>", Encoding.UTF8, "application/xml");
var response = await httpClient.PostAsync("http://someurl", "content");
The target url returns 401 Unauthorized and it looks like the NetworkCredentials haven't been passed through.
The Lambda has been granted the role AWSLambdaFullAccess.
When executed locally, this code with the same credentials works as expected.

Using DefaultRequestHeaders sends requests twice?

I have a WebAPI that sends BASIC authorization information as following.
var client = new HttlpClient();
client.BaseAddress = new Uri(GlobalConstants.LdapUri);
var contentType = new MediaTypeWithQualityHeaderValue("application/json");
client.DefaultRequestHeaders.Accept.Add(contentType);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", userName, password))));
Task<HttpResponseMessage> results = client.GetAsync(GlobalConstants.FortressAPIUriDev);
var response = await results;
I've built this API using MVC Core 1.x and the receiving API is built using MVC5.
The problem is that this GetAsync sends two requests at the same time, and I have no clue how to resolve this. I've done some Googling myself to see if I can find a fix for this but so far no luck. Did anyone experience this problem and know how to resolve it?
Thank you very much in advance.
Long story short, found a solution as follows:
using (var client = new HttpClient())
{
var requestMessage = new HttpRequestMessage(HttpMethod.Get, GlobalConstants.LdapUri + GlobalConstants.FortressAPIUriDev);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", userName, password))));
var response = await client.SendAsync(requestMessage);
}
After replacing with this code, it is sending one request at a time.
Found a hint at :
Adding headers when using httpClient.GetAsync

Set proxy server specific to a service request HTTP client+REST

using (var client = new System.Net.Http.HttpClient())
{
var response = client.GetAsync(fullUrl).Result;
}
I am creating HTTP client as above to consume a RESTfull service.
I should be able to set proxy for this service request.
How can I set proxy server specific to this service request only?
System.Net.Http.HttpClient don't have TransportSettings propert and about Microsoft.Http assembly is unknown.
uSING Microsoft.Http I can
HttpClient client = new HttpClient();
/*Set Credentials to authenticate proxy*/
client.TransportSettings.Proxy = new WebProxy(proxyAddress);
client.TransportSettings.Proxy.Credentials = CredentialCache.DefaultCredentials;
client.TransportSettings.Credentials = CredentialCache.DefaultCredentials;
client.BaseAddress = new Uri(this.baseUrl);
var response = client.Get(fullUrl);
var jsonResponce = response.Content.ReadAsJsonDataContract<mYResponseoBJECT>();
public static T ReadAsJsonDataContract<T>(this HttpContent content)
{
return (T)content.ReadAsJsonDataContract<T>(new DataContractJsonSerializer(typeof(T)));
}

Categories