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

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.

Related

Example of OAuth authenticated request using .NET framework

The Node.JS code below sends 0-legged OAuth authenticated request to the API:
'use strict';
var OAuth = require('OAuth');
var express = require('express');
var app = express();
var oauth = new OAuth.OAuth(
'http://example.com/oauth/request_token',
'http://example.com/oauth/access_token',
'mykey',
'none',
'1.0',
null,
'HMAC-SHA1'
);
app.get('/', function (req, res) {
oauth.get(
'http://example.com/api',
'token123',
'tokensecret123',
function (error, data, response){
data = JSON.parse(data);
res.json(data);
});
});
I need to convert this code to C# or VB.NET. Any sample of OAuth authenticated request in .Net will help too.
I do it with the library RestSharp which helps to deal with REST API.
The code below send a request to get a token from the OAuth:
var restClient = new RestClient();
restClient.BaseUrl = new Uri("theApiBaseUrl");
string encodedCredentials = Convert.ToBase64String(Encoding.Default.GetBytes($"yourAppId:yourSecret"));
// change the request below per the API requirement
RestRequest request = new RestRequest("theApiUrlForAuthentication", Method.POST);
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", $"Basic {encodedCredentials}");
request.AddQueryParameter("grant_type", "client_credentials");
request.AddQueryParameter("scope", "api");
IRestResponse response = restClient.Execute(request);
// the token should be in the JSON string response.Content
// now you'll want to deserialize the JSON to get the token
var jsonWithToken = MyFunctionToGetToken(response.Content);
Now you have the token in order to do authenticated calls to the API:
var restClient = new RestClient();
restClient.BaseUrl = new Uri("theApiBaseUrl");
RestRequest request = new RestRequest("theApiEndpoint", Method.GET);
request.AddHeader("Accept", "application/hal+json");
request.AddHeader("profile", "https://api.slimpay.net/alps/v1");
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", $"Bearer {token}");
RestClient.Execute(request);
Each API is different, so you'll surely have to modify my code (add or remove headers, encoding the credentials, ...) so that it works for you.
Thank you #Guillaume Sasdy for steering me towards RestSharp. Here is a working solution that works the same way as the node.js code in my question.
Since API I'm accessing is using 0-legged OAuth, the Access Token and Access Secret are known upfront and make things much easier.
const string consumerKey = "mykey";
const string consumerSecret = "none";
var baseUrl = "https://example.com";
var client = new RestClient(baseUrl);
var request = new RestRequest("/api");
client.Authenticator = OAuth1Authenticator.ForProtectedResource(
consumerKey, consumerSecret, "token123", "tokensecret123"
);
var response = client.Execute(request);

I want to get Stex.com API access token

I am developing C# WinForms application to trade on Stex.com.
They upgraded their api to api3.
It uses google authentication app to login.
That's why there's no way to get access token without man's behavior.
Finally, I determined to use postman to get access token and I want to refresh token when the token is expired.
I think it the best way.
So I got the access token and refresh token via postman.
https://help.stex.com/en/articles/2740368-how-to-connect-to-the-stex-api-v3-using-postman .
now it's the turn to refresh my token.
so this is what I wrote.
string refresh_token = "def50200b03974080...";
string client_id = "502";
string client_secret = "SeTs50aFxV1RoMFBW1b4RVNQhh2wEdICaYQrpE3s";
string AccessToken = "eyJ0eXAiOiJKV1QiLCJhbGciO...";
string url = #"https://api3.stex.com/oauth/token";
var request = HttpWebRequest.Create(url);
request.Method = "POST";
request.Headers.Add("Authorization", "Bearer " + AccessToken);
request.ContentType = "application/x-www-form-urlencoded";
NameValueCollection outgoingQueryString = HttpUtility.ParseQueryString(String.Empty);
outgoingQueryString.Add("grant_type", "refresh_token");
outgoingQueryString.Add("refresh_token", refresh_token);
outgoingQueryString.Add("client_id", client_id);
outgoingQueryString.Add("client_secret", client_secret);
outgoingQueryString.Add("scope", "trade profile reports");
outgoingQueryString.Add("redirect_uri", #"https://www.getpostman.com/oauth2/callback");
byte[] postBytes = new ASCIIEncoding().GetBytes(outgoingQueryString.ToString());
Stream postStream = request.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Flush();
postStream.Close();
using (WebResponse response = request.GetResponse())
{
using (StreamReader streamReader = new StreamReader(response.GetResponseStream()))
{
dynamic jsonResponseText = streamReader.ReadToEnd();
}
}
It shows 401(Unauthorized) error.
And when I remove ContentType, it shows 400(Bad Request) error.
If anyone did this, please help me.
guys!
Finally, I found the issue.
The issue was due to my ignorance.
Calm down and have a relax when you get issue.
:)
I created 2 api3 clients and so client_secret was different.
Thank you.

How to set Httpwebrequest Authentication header with Authenticationresult

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.

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.

Categories