RESTful API call using C# - c#

I am building my URL to make an API call, using the key and secret that the provider has given me.
https://api.testurl.com/api/test/calldata?key=12345&secret=999999&query=hello
My question is I am appending the 'query' based on user input each time and performing the call with the 'key' and 'secret' every time - to me this doesn't seem that secure. Isn't the secret key exposed each time the call is made?
public async Task<List<APIResult.Data>> ApiAsync()
{
using (var client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(_apiUrlToCall);
if (!response.IsSuccessStatusCode) return null;
var result = await response.Content.ReadAsStringAsync();
var rootResult = JsonConvert.DeserializeObject<APIResult.Rootobject>
(result);
return rootResult.Data.ToList();
}
}

Normally you'd pass the identity data (in this case your key and secret) in a HTTP header rather than on the querystring. That way it doesn't get logged anywhere (e.g. IIS logs, browser history, slurped by google, facebook et al trackers).
If you're using HTTPS that should stop it being exposed anywhere else.
But yes since HTTP is stateless you have to send some sort of identifying data every time you make a request, be that a secret key, Kerberos token, session coookie, whatever it is.

You can pass the key & secret as Http header. Normally for rest api the Authorization Http Header is set with the authtoken. You could so something similar.

Related

Refreshing Bearer token with refresh token - problem with request and HttpClient

I am making a Web Application. I have a WebAPI which my application connects with via HTTPS.
On the start there is a user logging - WebAPI logs in user after getting data from WebApplication and sends BearerToken and RefreshToken.
Next, my WebApplication stores the tokens. BearerToken is added to every request to WebAPI. After 15 minutes bearer expires. Now, application should get new bearer token using refresh token. I made it in very easy way. All my requests go through this method:
public async Task<HttpResponseMessage> SendAsyncRequest(string uri, string content, HttpMethod method, bool tryReauthorizeOn401 = true)
{
try
{
HttpRequestMessage rm = new HttpRequestMessage(method, uri);
if (!string.IsNullOrWhiteSpace(content))
rm.Content = new StringContent(content, Encoding.UTF8, ContentType);
HttpResponseMessage response = await httpClient.SendAsync(rm);
if (response.StatusCode == HttpStatusCode.Unauthorized && tryReauthorizeOn401)
{
httpClient.CancelPendingRequests();
bool res = await OnReauthorizeUser();
if (!res)
return response;
return await SendAsyncRequest(uri, content, method, false);
}
return response;
}catch(Exception ex)
{
Debug.Assert(false, ex.Message);
throw;
}
}
This should work like that:
I send a request
If I get 401 response and tryReauthorizeOn401 == true, I call OnReauthorizeUser event
In this event handler (which is not shown here) I eventually call again SendAsyncRequest with reauthorization data
I expected it to work. But it's not. It turns out that when I call SendAsynRequest with reauthorization data, I get TaskCancelletionException. Without any timeout. Just when hitting this: httpClient.SendAsync(rm); So my code "returns" to place after OnReauthorizeUser (previous call) with res == false.
So, I thought that maybe in some way I distort WebApplication http request lifecycle. I recon that during one request (for example - user clicks link with protected resource) I try send two requests:
get protected resource
reauthorize before main request ends
Am I right? How can I avoid such problems? What should I do to:
when I get 401 response, system should automatically refresh the tokens and call request one more time?
In other words:
user clicks "Show my documents" link
Application sends request to WebAPI: /documents/user/user-id
I get response from WebAPI with 401 error
Application then reauthorizes the user by sending request to WebAPI: /tokens/refresh/
I get response from WebAPI with new tokens (we assume that I will get this)
Application then sends again request from step two: /documents/user/user-id
So I would like to achieve this scenario.
User clicks in the link ONCE and then sees list of his documents. Without necessity to relogin (we assume that he is logged in, bearer token has expired in natural way and refresh token is valid)

How do I get RestSharp not to add oauth_callback parameters to the Authentication header

I'm trying to connect to a finicky API using RestSharp. The API uses OAuth1.0 and on the initial Request Token requires oauth_callback parameter ONLY in the query and not in the Authentication Header (I have confirmed this with Postman).
When I construct the request this way:
var Authenticator = OAuth1Authenticator.ForRequestToken(mc_apiKey, mc_appsecret);
Authenticator.ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader;
Authenticator.SignatureMethod = OAuthSignatureMethod.PlainText;
client.Authenticator = Authenticator;
var request = new RestRequest(RequestToken, Method.POST);
string AuthorizationCallBackURL = string.Format(LoopbackCallback);
request.AddParameter(_oauthCallback, AuthorizationCallBackURL, ParameterType.QueryStringWithoutEncode);
and look at the logs on the server I see the query string in the Http call,
http://192.168.0.187:8080/xxxx/ws/oauth/initiate?oauth_callback=http://192.168.0.187:8080/provider_emailer/callback.jsp
but it is also in the Authentication header:
Headers:
{Accept=[application/json, text/json, text/x-json, text/javascript, application/xml, text/xml],
accept-encoding=[gzip, deflate],
Authorization=[OAuth oauth_callback="http://192.168.0.187:8080/provider_emailer/callback.jsp",
oauth_consumer_key="XXXXXXXXXXXX",
oauth_nonce="cei09xm04qetk2ce",
oauth_signature="XXXXXXXXXXXXXXXX",
oauth_signature_method="PLAINTEXT",
oauth_timestamp="1591197088",
oauth_version="1.0"],
Content-Length=[0], Content-Type=[null],
cookie=[JSESSIONID=C8C8DB501382F7D1E52FE436600094C0],
host=[192.168.0.187:8080], user-agent=[RestSharp/106.11.4.0]}
This causes a "NotAcceptable" response. The same request done with Postman without the callback parameter in the Authentication header works.
Am I doing something wrong? Is there a way to only get the callback in the query string?
That's tricky. I looked at the code and we don't set the callback URL to the workflow object when you use the overload without this parameter. So, you're doing it conceptually correct.
However, we must collect all the parameters (default and request parameters) plus OAuth parameters to build the OAuth signature. We then use the parameters collection and extract all of them that have a name starting with oauth_ or xauth_ to the request headers (in case you use HttpAuthorizationHeader) and by doing so, we put your query parameter to the header.
Apparently, that's not ideal and it looks like a bug, so I can suggest opening an issue in RestSharp repository. Should not be hard to fix.

Is there a way to retrieve the String the way it is actually uploaded to the server (as a whole)?

I am currently working on a OAuth2 implementation. However I am stuck on an Error 401. It seems like there is something wrong with my post request that is supposed to retrieve the access token from the Company the User logged in to. This is my code:
internal void RequestAccessToken(string code)
{
string requestBody = "grant_type="+ WebUtility.UrlEncode(GRANTTYPE)+ "&code=" + WebUtility.UrlEncode(code)+"&redirect_uri="+ WebUtility.UrlEncode(REDIRECT_URI);
WebClient client = new WebClient();
client.Headers.Add("Authorization",HeaderBase64Encode(CLIENT_ID, SECRETKEY));
var response = client.UploadString("https://thewebsiteiamcallingto.com/some/api", requestBody);
var responseString = client.OpenRead("https://thewebsiteiamcallingto.com/some/api");
}
My Questions are:
Is there anything wrong with the way I try to make the POST request ?
Is there a way to retrieve the whole string that is posted to the URI using UploadString?
P.S. I have seen this post regarding the POST creation. However I find the async part to be too complicated for my case.
Since we dont know the api documentation, I would suggest you to make a postman request and view the actual request sent and response received, and secondly make a request using your method and capture using a utility like wireshark and compare the difference.

How to send an access_token and id_token to an api using System.Net.Http

how can you send both the access_token and id_token to your api using System.Net.Http? when i was testing my api with postman it seemed to send both tokens and returned the individual user information I needed (a list of products the user is selling). I am unsure how I can do this in my Xamarin app and have being stuck on this for quite some time. I am able to send the access_token as shown below but anything I have tried when sending both tokens has returned a 404 not found. (unauthorized is corrected to a 401 so the access_token is still working)
public async Task<string> GetResponseJsonString(string url)
{
string responseJsonString = null;
var access_token = CrossSecureStorage.Current.GetValue("access_token");
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + access_token);
HttpResponseMessage response = httpClient.GetAsync(url).Result;
responseJsonString = await response.Content.ReadAsStringAsync();
}
return responseJsonString;
}
Note: I am aware the id_token should contain the user information and it should be decoded rather than sending requests for user information. I looked at this and have been unable to find a library that works in a xamarin PCL. I looked at JosePCL.Jwt but was unable to get it to work. I figure since any time I need user information it is returning information from my database that it made sense to send both tokens with the request and let my api get the user information.
This is entirely dependent on the API you're calling. I've never seen an API that needs something more than the access_token it's provided back to you. It's possible you have the nomenclature incorrect here.
Do you mean "access key & secret"? Or are you certain you have an access_token?
In the former case, normally API's will expect things as followed:
Append the key & secret together separated by a ":"
Base64 Encode
Set the Authorization Bearer|Basic header with the result
It's also worth asking if you've tried passing in the id_token as the Authorization header?
It's also also worth asking if you can provide us with a screen capture of the successful response from postman (make sure you obfuscate the sensitive data).
It's also also also worth pointing out an optimization tweak for your code. Since you're using async, it seems you probably are somewhat concerned about performance. Have a look at this article, discussing the disposability of HttpClient. As a better alternative, use HttpRequestMessage as follows:
public async Task<string> GetResponseJsonString(string url)
{
string responseJsonString = null;
var req = new HttpRequestMessage(HttpMethod.Get, "/your/api/url");
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", access_token);
using (var resp = await client.SendAsync(req))
using (var s = await resp.Content.ReadAsStreamAsync())
using (var sr = new StreamReader(s))
{
if (resp.IsSuccessStatusCode)
{
responseJsonString = await sr.ReadToEndAsync();
}
else
{
string errorMessage = await sr.ReadToEndAsync();
int statusCode = (int)resp.StatusCode;
//log your error
}
}
return responseJsonString;
}
Where client is a reference to a statically shared instance of HttpClient. My preferred way to do all this, is to wrap my API calls, usually one-file-per-service. I inject this service as a singleton, which will broker it's own static instance of HttpClient. This setup is even more straightforward if you're using .NET Core.

User authentication when consuming a REST webservice with ServiceStack

The ServiceStack docs are full of examples on how to use server side implementation of authentication of a user. But how does one set the user credentials on the client side?
I use ServiceStack to consume a JSON REST service like this:
var restClient = new JsonServiceClient (baseUri);
var response = restClient.Get<MyResponse> ("/some/service");
How can I add any form of authentication to the request? The webservice I want to consume uses OAuth 1.0, but I am interested in adding custom authentication, too.
In my code, I have previously performed OAuth token exchange successfully, so I already own a valid access token and need to sign every REST request now using this access token and its token_secret.
ServiceStack's AuthTests shows different ways of authenticating when using the ServiceStack Service Clients. By default BasicAuth and DigestAuth is built into the clients, e.g:
var client = new JsonServiceClient(baseUri) {
UserName = UserName,
Password = Password,
};
var request = new Secured { Name = "test" };
var response = client.Send<SecureResponse>(request);
Behind the scenes ServiceStack will attempt to send the request normally but when the request is rejected and challenged by the Server the clients will automatically retry the same request but this time with the Basic/Digest Auth headers.
To skip the extra hop when you know you're accessing a secure service, you can tell the clients to always send the BasicAuth header with:
client.AlwaysSendBasicAuthHeader = true;
The alternative way to Authenticate is to make an explicit call to the Auth service (this requires CredentialsAuthProvider enabled) e.g:
var authResponse = client.Send<AuthResponse>(new Auth {
provider = CredentialsAuthProvider.Name,
UserName = "user",
Password = "p#55word",
RememberMe = true, //important tell client to retain permanent cookies
});
var request = new Secured { Name = "test" };
var response = client.Send<SecureResponse>(request);
After a successful call to the Auth service the client is Authenticated and if RememberMe is set, the client will retain the Session Cookies added by the Server on subsequent requests which is what enables future requests from that client to be authenticated.
Answering myself, as I've found a nice way to do it using the LocalHttpWebRequestFilter hook in the JsonServiceClient:
For securing a web service with OAuth 1.0a, every http request has to send a special Authorization: header. Within this header field, a hash (signature) must be send that uses some characteristics of the request as input data, like the hostname, request url and others.
Now it seems the LocalHttpWebRequestFilter is called by ServiceStack right before the http request is made, and exposes the underlying HttpWebRequest object, where one can add extra headers and access the required fields of the request.
So my solution is now basically:
var client = new JsonServiceClient (baseUri);
client.LocalHttpWebRequestFilter += (request) => {
// compute signature using request and a previously obtained
// access token
string authorization_header = CalculateSignature (request, access_token);
request.Headers.Add ("Authorization", authorization_header);
};
var response = client.Get<MySecuredResponse> ("/my/service");
Note that I use the Devdefined.OAuth library to do all the heavy stuff in CalculateSignature(). The creation of request token, obtaining user authorization, and exchanging the request token for access token as required by OAuth is done outside of ServiceStack, before the above service calls.

Categories