How to set Httpwebrequest Authentication header with Authenticationresult - c#

I need to set the auth header for my http web request using the AuthenticationResult I get from AuthenticationContext:
AuthenticationContext authContext = new AuthenticationContext("http://blabla/token");
Task<AuthenticationResult> resultTask = authContext.AcquireTokenAsync(
"http://blabla/service",
"SomeGuid",
new Uri("http://authlogin"),
new Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters(PromptBehavior.Auto, false));
resultTask.Wait();
AuthenticationResult result = resultTask.Result;
HttpWebRequest request = WebRequest.CreateHttp("http://MyApi/method");
//Set headers for request
I need to pass the authentication result to the header of my request. I know I can do
request.Headers[HttpRequestHeader.Authorization] = //something
I just don't know what that something should be. Any help is appreciated. Thanks

Here is a Wiki article on basic HTTP Authentication which is passed in the header.
For convenience, here is some code that implements the basic Auth Token:
string auth = string.Format("{0}:{1}", userName, password);
string enc = Convert.ToBase64String(Encoding.ASCII.GetBytes(auth));
string cred = string.Format("{0} {1}", "Basic", enc);
reqest.Headers[HttpRequestHeader.Authorization] = cred;
Note that you can replace this by setting the request.Credential, instead of constructing your own header value. This makes the code cleaner, HOWEVER your request will not pass the Auth header UNLESS the server responds with an Auth failure (401), so this approach generates more network traffic on secured end points.

Related

Microsoft Graph API: HttpStatusCode 200, Content-Length -1?

I'm trying to use Ms Graph API to connect to outlook and download attachment. What I have written till now is
private static async Task<HttpWebRequest> createHttpRequestWithToken(Uri uri)
{
HttpWebRequest newRequest = (HttpWebRequest)HttpWebRequest.Create(uri);
string clientId = "myClientId";
string clientSecret = "myClientSecret";
ClientCredential creds = new ClientCredential(clientId, clientSecret);
AuthenticationContext authContext = new AuthenticationContext("https://login.windows.net/myAzureAD/oauth2/token");
AuthenticationResult authResult = await authContext.AcquireTokenAsync("https://graph.microsoft.com/", creds);
newRequest.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + authResult.AccessToken);
newRequest.ContentType = "application/json";
return newRequest;
}
And I'm using this to call the Graph APIs that I need. So to begin with, I tried calling this URL:
Uri uri = new Uri(("https://graph.microsoft.com/v1.0/users/myEmailId/messages"));
HttpWebRequest request = createHttpRequestWithToken(uri).Result;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
After running the code, I get a response with HttpStatusCode 200 but the Content-Length is -1. I'm currently stuck here. Could someone please help me with where I'm going wrong / how to debug this piece of code further.
Thanks in advance.
The API uses "Transfer-Encoding: chunked" and hence Content-Length header is not returned. This is why you see default value of -1 for response.ContentLength property. To read the response body, simply read response stream obtained by response.GetResponseStream() method.

Trouble downloadig an thumbnail image in O365 Video using C#

I am running into an odd issue with trying to access a thumbnail image stored on O365 video via C#. I can access the REST API with no issue at all, I just add the Authentication: Bearer <token> to the header and I am off an running. The trouble is with a basic image URL that I get back from a specific video.
https://<mytenant>.sharepoint.com/portals/Channel1/pVid/myvideo.mp4.PNG?VideoPreview=1
When I access that URL from a browser it works 100% of the time. When I try to access it via the httpclient object, I am getting a 401 Unauthorized error.
The best I can figure is that the authorization header token is not being honored when accessing a basic URL. Which makes me thing that I need something else like a cookie? However I cannot seem to figure out which one. Looking for any advice :)
Pass credentials instead and yes you need an authentication cookie. Here is a sample:
private static async Task<string>getWebTitle(string webUrl)
{
//Creating Password
const string PWD = "softjam.1";
const string USER = "bubu#zsis376.onmicrosoft.com";
const string RESTURL = "{0}/_api/web?$select=Title";
//Creating Credentials
var passWord = new SecureString();
foreach (var c in PWD) passWord.AppendChar(c);
var credential = new SharePointOnlineCredentials(USER, passWord);
//Creating Handler to allows the client to use credentials and cookie
using (var handler = new HttpClientHandler() { Credentials = credential })
{
//Getting authentication cookies
Uri uri = new Uri(webUrl);
handler.CookieContainer.SetCookies(uri, credential.GetAuthenticationCookie(uri));
//Invoking REST API
using (var client = new HttpClient(handler))
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(string.Format(RESTURL, webUrl)).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
string jsonData = await response.Content.ReadAsStringAsync();
return jsonData;
}
}
}

Access Etsy API oauth using c# RestSharp

I set up a developer acct under our shop, to access our sales receipts. I decided to use RestSharp to make my requests. I have proved it works for none Oauth required calls. I have successfully received my accessToken and accessTokenSecret. So i use those along with the customerKey and customerSecret to make a ForProtectedResource call, for a oauth request as follows but always receive "This method requires authentication".
I'm hoping its something simple I'm missing. I thought, all I need to make any call are those four items correct? Once I have those four items I don't have to request or access token anymore, correct? Thanks
var access_token = "#########################";
var access_token_secret = "########";
var baseUrl = "https://openapi.etsy.com/v2";
var client = new RestClient(baseUrl);
client.Authenticator = OAuth1Authenticator.ForProtectedResource(consumerKey,
consumerSecret,
access_token,
access_token_secret);
var request = new RestRequest("shops/########/receipts");
request.Method = Method.GET;
request.AddParameter("api_key", consumerKey);
client.ExecuteAsync(request, response =>
{
var r = response;
});
After some trial and error I finally wrapped my head around OAuth and the way Etsy implements it. The api_key parameter is only to be used when you're calling a none OAuth required method. Otherwise you have to send it all the required OAuth params. Below is working code. I leveraged RestSharp, as well as this OAuth base I found here. Hope this help some poor sap from staring at crappy code for 3 days (like yours truly).
var restClient = new RestClient(baseUrl);
OAuthBase oAuth = new OAuthBase();
string nonce = oAuth.GenerateNonce();
string timeStamp = oAuth.GenerateTimeStamp();
string normalizedUrl;
string normalizedRequestParameters;
string sig = oAuth.GenerateSignature(new Uri(baseUrl + MethodLocation), consumerKey, consumerSecret, Accesstoken, AccessTokenSecret, "GET", timeStamp, nonce, out normalizedUrl, out normalizedRequestParameters);
// sig = HttpUtility.UrlEncode(sig);
var request = new RestRequest(MethodLocation);
request.Resource = string.Format(MethodLocation);
request.Method = Method.GET;
// request.AddParameter("api_key", consumerKey);
request.AddParameter("oauth_consumer_key", consumerKey);
request.AddParameter("oauth_token", Accesstoken);
request.AddParameter("oauth_nonce", nonce);
request.AddParameter("oauth_timestamp", timeStamp);
request.AddParameter("oauth_signature_method", "HMAC-SHA1");
request.AddParameter("oauth_version", "1.0");
request.AddParameter("oauth_signature", sig);
restClient.ExecuteAsync(request, response =>
{
var r = response;
});

Azure ACS get token using RestSharp 415 error

The following is the code sample provided by msdn for obtaining an SWT token from azure ACS (Access Control Service):
private static string GetTokenFromACS(string scope)
{
string wrapPassword = pwd;
string wrapUsername = uid;
// request a token from ACS
WebClient client = new WebClient();
client.BaseAddress = string.Format(
"https://{0}.{1}", serviceNamespace, acsHostUrl);
NameValueCollection values = new NameValueCollection();
values.Add("wrap_name", wrapUsername);
values.Add("wrap_password", wrapPassword);
values.Add("wrap_scope", scope);
byte[] responseBytes = client.UploadValues("WRAPv0.9/", "POST", values);
string response = Encoding.UTF8.GetString(responseBytes);
Console.WriteLine("\nreceived token from ACS: {0}\n", response);
return HttpUtility.UrlDecode(
response
.Split('&')
.Single(value => value.StartsWith("wrap_access_token=", StringComparison.OrdinalIgnoreCase))
.Split('=')[1]);
}
I am trying to replicate the code using RestSharp:
var request = new RestRequest("WRAPv0.9", Method.POST);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("wrap_name", uid, ParameterType.RequestBody);
request.AddParameter("wrap_password", pwd, ParameterType.RequestBody);
request.AddParameter("wrap_scope", realm, ParameterType.RequestBody);
RestClient client = new RestClient(
string.Format(#"https://{0}.{1}", serviceNamespace, acsHostUrl));
client.ExecuteAsync(request, Callback);
I tried other variations of the above code but to no avail. I keep recieving a 415 error stating that:
415 Unsupported Media Type T8000 Content-Type 'text/plain' is not
supported. The request content type must be
'application/x-www-form-urlencoded'.
I am not a Fiddler expert but with my limited experience with it I was not able to inspect my outgoing http request because it is encrypted.
I would appreciate advice on solving the issue.
You can try to leave out the AddHeader method call and instead set the Content-Type as the first AddParameter.
The issue is described here.

HttpClient authentication header not getting sent

I'm trying to use an HttpClient for a third-party service that requires basic HTTP authentication. I am using the AuthenticationHeaderValue. Here is what I've come up with so far:
HttpRequestMessage<RequestType> request =
new HttpRequestMessage<RequestType>(
new RequestType("third-party-vendor-action"),
MediaTypeHeaderValue.Parse("application/xml"));
request.Headers.Authorization = new AuthenticationHeaderValue(
"Basic", Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", "username", "password"))));
var task = client.PostAsync(Uri, request.Content);
ResponseType response = task.ContinueWith(
t =>
{
return t.Result.Content.ReadAsAsync<ResponseType>();
}).Unwrap().Result;
It looks like the POST action works fine, but I don't get back the data I expect. Through some trial and error, and ultimately using Fiddler to sniff the raw traffic, I discovered the authorization header isn't being sent.
I've seen this, but I think I've got the authentication scheme specified as a part of the AuthenticationHeaderValue constructor.
Is there something I've missed?
Your code looks like it should work - I remember running into a similar problem setting the Authorization headers and solved by doing a Headers.Add() instead of setting it:
request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", "username", "password"))));
UPDATE:
It looks like when you do a request.Content, not all headers are being reflected in the content object. You can see this by inspecting request.Headers vs request.Content.Headers. One thing you might want to try is to use SendAsync instead of PostAsync. For example:
HttpRequestMessage<RequestType> request =
new HttpRequestMessage<RequestType>(
new RequestType("third-party-vendor-action"),
MediaTypeHeaderValue.Parse("application/xml"));
request.Headers.Authorization =
new AuthenticationHeaderValue(
"Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", "username", "password"))));
request.Method = HttpMethod.Post;
request.RequestUri = Uri;
var task = client.SendAsync(request);
ResponseType response = task.ContinueWith(
t =>
{ return t.Result.Content.ReadAsAsync<ResponseType>(); })
.Unwrap().Result;
This would also work and you wouldn't have to deal with the base64 string conversions:
var handler = new HttpClientHandler();
handler.Credentials = new System.Net.NetworkCredential("username", "password");
var client = new HttpClient(handler);
...
Try setting the header on the client:
DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(String.Format("{0}:{1}", userName, password))));
This works for me.
Also, consider that Redirect-Handler will clear the Authorization header if your request gets redirected.
So if you call an HTTP endpoint and it redirected to the HTTPS one, you will lose your authorization header.
request.Headers.Authorization = null;
Framework: .NET v6.0
Actually your problem is with PostAsync- you should use SendAsync. In your code - client.PostAsync(Uri, request.Content); sends only the content the request message headers are not included.
The proper way is:
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, url)
{
Content = content
};
message.Headers.Authorization = new AuthenticationHeaderValue("Basic", credentials);
httpClient.SendAsync(message);

Categories