ServiceStack New API - How to get raw request/response - c#

I'm using the new ServiceStack.Client to consume a ServiceStack API, and to make a simple prototype to a client, as they are using PHP, I would like to show the raw request and response that internally ServiceStack is using to make the request.
Is there anything I can hook up in the client side to get the raw URL and data that is been sent to the API as well the raw json that we're getting from the API call?
I'm simply using, as an example:
var service = new JsonServiceClient(gko_url);
var response = service.Post<Authenticate>("/auth", new Authenticate()
{
UserName = username,
Password = password,
RememberMe = true
});

If you are trying to inspect the raw HTTP request & response between the ServiceStack Client and service, the easiest way is to run the Fiddler proxy on the same PC as the client.
Then set the ServiceStack client to use fidder as a proxy (running on localhost port 8888 by default):
var client = new JsonServiceClient(gko_url);
IWebProxy webProxy = new WebProxy("http://localhost:8888");
client.Proxy = webProxy;
var response = client.Post<Authenticate>("/auth", new Authenticate()
{
UserName = username,
Password = password,
RememberMe = true
});
You can then inspect the raw HTTP Request and Response between the client and the server via the Fiddler UI. That will give you and others confidence the "over the wire" communication is pure HTTP+JSON , and language-independent.
This may be more effective to "show off", since you are not asking the ServiceStack client to give you the raw HTTP communication - it is coming from a completely different application (Fiddler web proxy).

Related

403 Forbidden with WebClient

The company I work for have a limited internet connection and we use the proxy (example: 10.10.10.10:8080) to access at some restricted connections.
I can use the API in Postman (putting the proxy in the Postman settings) but when putting in the C# WebClient code it gives me a 403-Forbidden error.
I only need the var sensorData field but I splitted in var data and var data2 to understand where was the problem. It gives me the error at the var data = ...
Uri uri = new Uri("https://XXXXXXXX/api/DatasourceData/DatasourceDataHistoryBySerialNumber/");
Token token = new Token();
token = GetToken(tokenAPI);
using (WebClient client = new WebClient())
{
try
{
client.Proxy = new WebProxy("10.10.10.10", 8080);
client.Headers.Add("Authorization", "Bearer " + token.AccessToken);
client.QueryString.Add("serialNumbersDatasource", "I2001258");
client.QueryString.Add("startDate", string.Format("{0:s}", "2019-12-01"));
client.QueryString.Add("endDate", string.Format("{0:s}", DateTime.Now));
client.QueryString.Add("isFilterDatesByDataDate", "false");
var data = client.DownloadData(uri);
var data2 = (Encoding.UTF8.GetString(data));
sensorData = (JsonConvert.DeserializeObject<List<Sensor>>(Encoding.UTF8.GetString(client.DownloadData(uri))))[0];
}
}
Seems the problem at this line
client.Headers.Add("Authorization", "Bearer " + "tokenTest");
here you wil add header Authorization with value Bearer tokenTest
so, 403 Forbidden returns by service which you are addressing, but not a proxy
change to
client.Headers.Add("Authorization", "Bearer " + tokenTest);
and check if tokenTest has valid value
Check to see if you need any additional properties on the proxy. You may possibly need to enable:
UseDefaultCredentials (Boolean) true if the default credentials are
used; otherwise, false. The default value is false
Also, check your full url and query string that you are producing - look at the outgoing request fabricated (in the debugger) or through Fiddler and make sure it all lines up, url, query string, headers, etc.
From the docs:
Address
Gets or sets the address of the proxy server.
BypassArrayList
Gets a list of addresses that do not use the proxy server.
BypassList
Gets or sets an array of addresses that do not use the proxy server.
BypassProxyOnLocal
Gets or sets a value that indicates whether to bypass the proxy server for local addresses.
Credentials
Gets or sets the credentials to submit to the proxy server for authentication.
UseDefaultCredentials
Gets or sets a Boolean value that controls whether the DefaultCredentials are sent with requests.
Probably a problem with authorization header.
Is the token valid? Does it work with the same token in Postman?
I bet the api can't validate the token and and gives you no authorization to the resources. This is what a 403 would mean (but don't know what the api programmer actually intended by giving you 403).
Do you have access to the api's source code?
The token is really a string "tokentest" and that works with Postman?
I would suggest you to go for xNet.dll instead of webclient Because xNet library are considered best for proxy and webrequest.
var request = new HttpRequest()
request.UserAgent = Http.ChromeUserAgent();
request.Proxy = Socks5ProxyClient.Parse("10.10.10.10:8080");//can use socks4/5 http
Based on this
Try adding User-Agent in the header
client.Headers.Add("User-Agent", "PostmanRuntime/7.26.1");
In my case i did not specify security protocol. Paste this line of code before running any WebClient requests.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
original answer: How to specify SSL protocol to use for WebClient class

oauth2 authentication c# code is not working

I am trying to consume oauth2 api using auth code. In first step, i received auth by providing client id and secret, and now in 2nd step, i need access token using that auth code.
I tried below c# code
var client1 = new RestClient("https://ant.aliceblueonline.com/oauth2/token");
var request1 = new RestRequest(Method.POST);
request1.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request1.AddParameter("code", "xxxxxxxxxxxxxxxxx");
request1.AddParameter("grant_type", "authorization_code");
request1.AddParameter("redirect_uri", "https://ant.aliceblueonline.com/plugin/callback");
request1.AddParameter("client_id", "MM01418");
request1.AddParameter("client_secret", "xxxxxxxxxxxxxx");
IRestResponse response = client1.Execute(request1);
In response, I get
The OAuth 2.0 Client supports client authentication method "client_secret_basic", but method "client_secret_post" was requested. You must configure the OAuth 2.0 client's "token_endpoint_auth_method" value to accept "client_secret_post
Tried a lot but could not resolve it.
First of all as you're working with C# I would like to recommend you to use IdentityModel to interact with any OAuth2 authorization server or OpenId Connect Provider.
Let's start by the definition of a Client: A client is an application that is allowed to request acces tokens on behalf of the user. In your example your server runing the code you posted is the client.
To be able to use the token endpoint to request a new access_token a client must be able to prove its identity first, by providing a client_id and client_secret (like a user and password for clients).
There are two methods for providing this client credentials, from IdentityServer4 documentation about secrets:
Authentication using a shared secret
You can either send the client id/secret combination as part of the POST body:
POST /connect/token
client_id=client1& client_secret=secret& ...
..or as a basic authentication header:
POST /connect/token
Authorization: Basic xxxxx
In this case, the error response is saying that just one of those methods is allowed, which is Authentication header
So instead of passing your client_id and client_secret along your request body:
request1.AddParameter("client_id", "MM01418");
request1.AddParameter("client_secret", "xxxxxxxxxxxxxx");
you need to concat client_id and client_secret with a collon as separator like "MM01418:xxxxxxxxxxxxxxxx" and apply base64 codification. Then add it to your request as header, of the form Authorization: Basic TU0wMTQxODp4eHh4eHh4eHh4eHh4eHh4. You can do this in c# by using the following code:
var credentials = string.Format("{0}:{1}", clientId, clientSecret);
var headerValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(credentials));
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", headerValue);
or instead leave this low level details to a library like IdentityModel Nuget Docs that implements the correct requests for you depending on the case.

Using Azure Hybrid Connection to connect to internal Web API

Very new to Azure, and I have an internal web API on an internal address http://internal-server:182/api/policies. I have set up a Hybrid Connection internal-service.servicebus.windows.net. This is connected and working.
My struggle is getting the C# code working to connect and retrieve the data. After a number of days, I have reviewed various articles, videos etc and all seem more advanced than what I am trying to do, which is just call the Web API and read the JSON. I have tried to simplify the code but receive the error:
401 MalformedToken: Invalid authorization header: The request is missing WRAP authorization credentials.
At present I have the followed code:
using (var client = new HttpClient())
{
var url = "http://internal-service.servicebus.windows.net";
var tp = TokenProvider.CreateSharedAccessSignatureTokenProvider("RootManageSharedAccessKey", "<key goes here>");
var token = tp.GetWebTokenAsync(url, string.Empty, true, TimeSpan.FromHours(1))
.GetAwaiter()
.GetResult();
client.BaseAddress = new Uri(url);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("ServiceBusAuthorization", token);
var response = client.GetAsync("/api/policies").Result;
string res = "";
using (HttpContent content = response.Content)
{
// ... Read the string.
Task<string> result = content.ReadAsStringAsync();
res = result.Result;
Label1.Text = res;
}
}
Any help or direction would be much appreciated? Once this code is working the Web App will be published as an Azure Web App.
Seems that your are not sending the right header.
First suggestion: intercept the call with a proxy like fiddler, to do that add a proxy config to your call to localhost port 8888, after this you can try some request and see the raw http you are sending to the server, you can also modify it until it works, once you have this modify your code until it send the same raw http.
You can find more info about this here:
Microsoft Azure CreateQueue using Simple REST Client
https://github.com/ytechie/event-hubs-sas-generator

HTTP request from a C# desktop application to a Siteminder-protected server

I have developed a C# desktop application which makes HTTPS requests to the customers' servers (usually Documentum/SharePoint/Alfresco/NemakiWare/etc HTTPS-based servers).
Several customers have asked us to support their servers which are protected by CA SSO (new name of Siteminder).
QUESTION: What do I need to do to allow my application to send HTTPS requests (and receive responses) with CA SSO-protected servers?
I have developed NTLM-SSO support for our C# desktop application and it works well, but I am not sure about how to proceed for CA SSO.
I have asked the same question on the CA forum, but like most questions there it remains unanswered.
To authenticate with CA SSO and then connect to the desired URL we need to access a protected resource on a web server configured to use CA SSO authentication:
Requests a resource on the server, using an HTTP request.
The request is received by the web server and is intercepted by the CA SSO web agent.
The web agent determines whether or not the resource is protected, and if so, gathers the user’s credentials and passes them to the Policy server.
The Policy server authenticates the user and verifies whether or not the authenticated user is authorized for the requested resource, based on rules and policies contained in the Policy store.
After the user is authenticated and authorized, the Policy server grants access to the protected resources.
This is accomplished with the following steps:
Open a connection (HTTP request in this case) to the URI of the protected resource. Since the request has not yet been authenticated, the CA SSO agent will issue a redirect to a login page. In the code, AllowAutoRedirect is set to false. This is important as the redirect URL will be required for the subsequent POST of login data in step 3 below. If AllowAutoRedirect were True, the response would not include a Location header and the subsequent POST would be made to the original URL, which would then redirect to the login page again. However, a POST occurs between a client and the server, any POST data carried in the payload of the request of step 3 will be lost during the redirect.
Dim request As HttpWebRequest
Dim response As HttpWebResponse
Dim url As String = PROTECTED_URL
request = WebRequest.Create(url)
request.AllowAutoRedirect = False
response = request.GetResponse
' make sure we have a valid response
If response.StatusCode <> HttpStatusCode.Found Then
Throw New InvalidProgramException
End If
' get the login page
url = response.Headers("Location")
request = WebRequest.Create(url)
request.AllowAutoRedirect = False
response = request.GetResponse
The next step involves creating an HTTPS request that POSTs all the form data, including userid and password, back to the server. The purpose of an authentication agent is to verify a user’s identity by validating their userid and password. Thus, their URLs naturally use SSL (secure sockets layer) and are encrypted for us, so we do not required further encryption in our program. However, the formatting of the POST data is interesting in as much as there are two alternatives. The sample program uses the simpler approach of setting the content type to application/x-www-form-urlencoded. Here the POST data is formatted similar to a query string and sent as part of the next request.
Dim postData As String
postData = ""
For Each inputName As String In tags.Keys
If inputName.Substring(0, 2).ToLower = "sm" Then
postData &= inputName & "=" & _
HttpUtility.UrlEncode(tags(inputName)) & "&"
End If
Next
postData += "postpreservationdata=&"
postData += "USER=" + HttpUtility.UrlEncode(USERNAME) & "&"
postData += "PASSWORD=" + HttpUtility.UrlEncode(PASSWORD)
request = WebRequest.Create(url)
cookies = New CookieContainer
request.CookieContainer = cookies
request.ContentType = FORM_CONTENT_TYPE
request.ContentLength = postData.Length
request.Method = POST_METHOD
request.AllowAutoRedirect = False ' Important
Dim sw As StreamWriter = New StreamWriter(request.GetRequestStream())
sw.Write(postData)
sw.Flush()
sw.Close()
response = request.GetResponse
Same idea as Mohit's answer, but it can be done with a much simpler code:
//Make initial request for SM to give you some cookies and the authentication URI
RestClient client = new RestClient("http://theResourceDomain/myApp");
client.CookieContainer = new CookieContainer();
IRestResponse response = client.Get(new RestRequest("someProduct/orders"));
//Now add credentials.
client.Authenticator = new HttpBasicAuthenticator("username", "password");
//Get resource from the SiteMinder URI which will redirect back to the API URI upon authentication.
response = client.Get(new RestRequest(response.ResponseUri));
Although this uses RestSharp, it can be easily replicated using HttpClient or even HttpWebRequest.

HTTPClient getting two 401s before success (sending wrong token)

I'm trying to communicate with a self-hosted WebAPI client using HttpClient. The client is created with the following code:
HttpClientHandler clientHandler = new HttpClientHandler()
{
UseDefaultCredentials = true,
PreAuthenticate = true
};
var client = new HttpClient(clientHandler);
on the server side we set:
HttpListener listener = (HttpListener)app.Properties[typeof(HttpListener).FullName];
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
in the Startup file.
The problem is that I get two (or one after preauthentication) 401 errors before the request is processed.
In fiddler the sequence looks like this:
First request:
Authorization: Negotiate TlRMTVNTUAABAAAAl7II4gcABwAxAAAACQAJACgAAAAGAbEdAAAAD1dTMTEzLTEyMFNXVC0xMTM=
Answer:
WWW-Authenticate: Negotiate TlRMTVNTUAACAAAADgAOADgAAAAVwonisrQOBMTKHhKwCkgCAAAAAJoAmgBGAAAABgGxHQAAAA9TAFcAVAAtADEAMQAzAAIADgBTAFcAVAAtADEAMQAzAAEAEgBXAFMAMQAxADMALQAxADIAMAAEABYAcwB3AHQALQAxADEAMwAuAGwAbwBjAAMAKgBXAFMAMQAxADMALQAxADIAMAAuAHMAdwB0AC0AMQAxADMALgBsAG8AYwAFABYAQQBMAEQASQA5ADkAOQAuAGwAbwBjAAcACACkGh0XVY3QAQAAAAA=
Second request (succeeds):
Authorization: Negotiate TlRMTVNTUAADAAAAAAAAAFgAAAAAAAAAWAAAAAAAAABYAAAAAAAAAFgAAAAAAAAAWAAAAAAAAABYAAAAFcKI4gYBsR0AAAAPfJafWSuLL0sAXYtWCynOqg==
So why isn't my client sending the correct authorization token the first time around but always needs this two-time approach?
What you are experiencing is normal, this is how the NTLM authentication scheme works.
1: C --> S GET ...
2: C <-- S 401 Unauthorized
WWW-Authenticate: NTLM
3: C --> S GET ...
Authorization: NTLM <base64-encoded type-1-message>
4: C <-- S 401 Unauthorized
WWW-Authenticate: NTLM <base64-encoded type-2-message>
5: C --> S GET ...
Authorization: NTLM <base64-encoded type-3-message>
6: C <-- S 200 Ok
The client sends a GET request to the server.
Since you need to be authenticated to access the requested resource, the server sends back a 401 Unathorized response and notifies the client in the WWW-Authenticate header that it supports NTLM authentication. So this is where you get your first 401 response code.
The client sends the domain name and the username to the server in the Authorization header. Note that based solely on these information the client cannot be authenticated yet.
The server sends a challenge to the client. It's a randomly generated number called a nonce. This is where you get your second 401 response code.
The client sends back a response to the server's challenge, using its password's hash to encrypt the random number.
The server sends the client's username, the challenge sent to the client and the response received from the client to the domain controller. Using the username the domain controller retrieves the hash of the user's password and encrypts the challenge with it. If the result matches the response sent by the client, the client is authenticated and the server sends back a 200 response code and the requested resource to the client.
Faced a similar issue, and after going through a lot of answers, where none of those worked. The following worked, and it doesn't come up with two 401's:
var credential = new NetworkCredential("username", "password", "domainname");
var myCache = new CredentialCache();
// Add the target Uri to the CredentialCache with credential object
myCache.Add(new Uri("http://targeturi/"), "NTLM", credential);
// Create an HttpClientHandler to add some settings
var handler = new HttpClientHandler();
handler.AllowAutoRedirect = true;
handler.Credentials = myCache;
// Create an HttpClient with the handler object
httpClient = new HttpClient(handler);
// Wait to get the reponse, and you can use the reponse in your code
HttpResponseMessage response = await httpClient.GetAsync(resourceUri);
Source
the default behavior where it only sends credentials after receiving an HTTP 401 Not Authorized response.
Manually adding the credentials header seems to be the best solution available.
More details in this post

Categories

Resources