I have a c# .net Client with this Code:
using(WebClient client = new WebClient())
{
string serialisedData = "";
serialisedData = JsonConvert.SerializeObject(myData);
client.Credentials = new NetworkCredential(config.UserData.Username, config.UserData.Password);
byte[] responsebyte = client.UploadData(config.ServerAddress, System.Text.Encoding.UTF8.GetBytes(serialisedData));
}
That Client sends data to my nodejs Server.
Nodejs Code:
var http = require('http');
var _server = http.createServer(_listener);
_server.listen(1234);
console.log( 'started' );
function _listener(req, res) {
let data = []
req.on('data', chunk => {
data.push(chunk)
})
req.on('end', () => {
data = Buffer.concat(data);
var dataString = new Buffer.from(data).toString("utf-8");
const data = JSON.parse(dataString);
// data has all the data from the c# object "myData"
res.write('response')
res.end()
})
}
But how can I access the credentials of this connection?
This is how I can Access the credentials in c#:
HttpListener listener = new HttpListener();
listener.Prefixes.Add($"https://+:{Config.Port}/");
listener.AuthenticationSchemes = AuthenticationSchemes.Basic;
listener.Start();
for (; ; )
{
Console.WriteLine("Listening...");
IAsyncResult result = listener.BeginGetContext(new AsyncCallback(DoWork), listener);
result.AsyncWaitHandle.WaitOne();
result = null;
}
private void DoWork(IAsyncResult asyncResult)
{
HttpListener listener = (HttpListener)asyncResult.AsyncState;
HttpListenerContext context = listener.EndGetContext(asyncResult);
HttpListenerBasicIdentity identity = (HttpListenerBasicIdentity)context.User.Identity;
// identity has the credentials
}
Edit: I cant change the c# Code anymore. So only nodejs solutions are needed
Edit2: The headers also have no Auth or Authentification property…
Edit3: I cant even find if other location exists except the header for credentials/authentification. But this must be possible right? I mean c# can somehow read this stuff from somewhere…
Any Idea what I can try to find the credentials?
To make your C# client to send its networkCredentials as HTTP Basic Authentication to your Nodejs server; the server should return a response whose header contains a HTTP 401 Unauthorized status and a WWW-Authenticate field if the request does not contain the Authorization header. This will cause your C# client retry the POST with Authorization header.
This process it is called Authentication challenge in case you want to search for more info.
There are serveral packages that does that for you; like http-auth or you can code it by hand (it is not very hard as it is just a matter of checking the existence of the Authorization header in the request and, if there is none or incorrect credentials, make a 401 response with a WWW-Authenticate field)
i.e. from the top of my head:
var http = require('http');
var _server = http.createServer(listener);
_server.listen(1234);
console.log('started');
function listener(req, res) {
if (!req.headers.authorization) {
res.statusCode = 401;
res.statusMessage = 'Unauthorized';
res.setHeader('WWW-Authenticate', 'Basic');
res.end();
}
}
Related
I'm actually trying to expose some methods of an ASP.NET MVC specific controller, in order to secure sensitive calls.
The entire website doesn't have to be protected by a specific SSL certificate, but some requests are.
Here is my code (as simple as it is) to get "Data", as you can see, I first check the SSL certificate, then the process continues if the SSL Certificate is correct :
public string GetData()
{
try
{
var certificate = Request.ClientCertificate;
if (certificate == null || string.IsNullOrEmpty(certificate.Subject))
{
// certificate may not be here
throw new Exception("ERR_00_NO_SSL_CERTIFICATE");
}
if (!certificate.IsValid || !IsMyCertificateOK(certificate))
{
// certificate is not valid
throw new Exception("ERR_01_WRONG_SSL_CERTIFICATE");
}
// Actions here ...
}
catch (Exception)
{
Response.StatusCode = 400;
Response.StatusDescription = "Bad Request";
}
}
Here is my IIS configuration :
SSL Certificate is set to "Accept", thus, I hope I could get the client certificate in the Request.ClientCertificate property, but it's never the case, I never get the certificate set in my client.
Here is my client code (copied from generated Postman C# code) :
string PFX_PATH = #"C:\Test\test.pfx"; // set here path of the PFX file
string PFX_PASSWORD = "password"; // set here password of the PFX file
var client = new RestClient("https://mywebsite.com/GetData?input=test");
client.Timeout = -1;
client.ClientCertificates = new System.Security.Cryptography.X509Certificates.X509CertificateCollection()
{
new System.Security.Cryptography.X509Certificates.X509Certificate(PFX_PATH,
PFX_PASSWORD,
System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.Exportable)
};
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
The PFX file has a private key, and is accessible from client side.
Am I missing something regarding the IIS configuration, or should I update my web.config somehow ?
Context
I am currently working on a .net Core 3.1 API that has an authentication method that checks if a HTTP request is send from a specific IP addresses.
The requesters IP Address should match with ones that are stored in the database or localhost otherwise the client is denied.
Code
I Have the following code:
Controller
public async Task<IActionResult> AuthenticatePlanbord([FromBody] AuthPlanbordRequest request)
{
if (request.AuthType == AuthType.Planbord)
{
// Validate the IP address of the client to check if the request is made from the server of the planbord.
var ip = _accessor.HttpContext?.Connection?.RemoteIpAddress?.ToString();
var AuthResponse = await _authService.AuthenticatePlanbordAsync(ip, request.DatabaseName, request.UserId);
if (AuthResponse == null) return Unauthorized(new ServerResponse(false, "Unauthorized", HttpStatusCode.Unauthorized));
return Ok(new ServerResponse(TokenGenerator.GenerateJsonWebToken(AuthResponse)));
}
return BadRequest(new ServerResponse(false, _localizer["AuthTypeNotSupported"], HttpStatusCode.BadRequest));
}
Authentication service
public async Task<AuthEntityPlanbord> AuthenticatePlanbordAsync(string ip, string databaseName, Guid userId = default)
{
_unitOfWork.Init();
// Check if the request does not originate from localhost
if (ip != "::1")
{
var Ip = await _unitOfWork.Connection.ExecuteScalarAsync<string>("SELECT IpAdres FROM PlanbordAutorisaties WITH(NOLOCK) WHERE IpAdres = #Ip ", new { Ip = ip }, _unitOfWork.Transaction);
if (string.IsNullOrEmpty(Ip)) return null;
}
var userData = await _unitOfWork.AuthRepository.AuthenticatePlanbordAsync(userId);
userData.IPAdress = ip;
userData.DatabaseName = databaseName;
return userData;
}
Problem & Question
To fully test if the logic works I would like to write a integration test that sends a HTTP request from a different IP address than localhost. Would this be possible in .net Core? Or Should I just rely on Unit tests only?
The easy way (which works in any language), is to use a service like reqbin or similar to simulate a request. Being an online service it will have a different IP.
You can find other similar services doing that.
This one in particular has also an API examples available. So if you want to integrate it to your unit tests or someting like that, you will just have to simulate a POST request to their api, with the parameters pointing to your endpoint so you can simulate an external request from an IP not whitelisted.
Yes, if this is an API call you can create a request using HTTP client.
You can set HTTP client to use a proxy in the handler.
var httpclienthandler = new HttpClientHandler
{
// Set creds in here too if your proxy needs auth
Proxy = new WebProxy("proxyIp")
};
var httpClient = new HttpClient(httpclienthandler);
If this is a website action, you can set your browser to use a proxy, or simply connect to a VPN?
I'm in the process of extending my Vtiger (V7) class to include all supported methods. I'm stuck on getting the extendsession call to work. According to the website I have to POST to:
http://vtiger_url/webservice.php?operation=extendsession
Every try fails with:
{
"success": false,
"error":
{
"code": "AUTHENTICATION_REQUIRED",
"message": "Authencation required"
}
}
This is my current code:
private const string UrlSegmentOperation = "webservice.php?operation=";
private const string OperationExtendSession = "extendsession";
RestClient client = new RestClient(baseUrl + UrlSegmentOperation + OperationExtendSession);
RestRequest request = new RestRequest(Method.POST);
IRestResponse restResponse = client.Execute(request);
So far I have tried GET, POST, with sessionname, username and both but still I get the same result.
Is it something that I may not have rights for? I'm supposed to have an ADMIN user and all other calls work flawlessly.
According to linked docs.
Notice: if the user does a Extend Session then the session will be tied together, so logging out one(webservices or web client) will log the user out of the other as well. for extend session operation to work cookies must be enabled on the client browser.
(Emphasis mine)
Your new rest client would not be an already authenticated session as it does not include any cookies that were returned with the login response.
I would suggest have one client for the life time of the application for that endpoint, and also to have a shared cookie container to store any cookies that would have been returned from previous requests.
public static class Rest {
static Lazy<RestClient> client = new Lazy<RestClient>(() => {
var endpoint = "webservice.php";
var endPointUrl = baseUrl + endpoint;
var client = new RestClient(endPointUrl);
client.CookieContainer = new System.Net.CookieContainer();
return client
});
public static RestClient Client {
get {
return client.Value;
}
}
}
By doing so, any cookies set or unset in responses will be used in subsequent requests.
The above would be used to make all requests to the web service. The cookies will be set when logging into the web service.
Including when making the call to extend the session.
private const string OperationExtendSession = "extendsession";
var request = new RestRequest(Method.POST);
request.AddParameter("operation", OperationExtendSession);
IRestResponse restResponse = Rest.Client.Execute(request);
I am new to the Service Stack library and trying to use the Server Events Client. The server I'm working with has two URIs. One for receiving a connection token and one for listening for search requests using the token acquired in the previous call.
I use a regular JsonServiceClient with digest authentication to get the token like so:
public const string Baseurl = "http://serverIp:port";
var client = new JsonServiceClient(Baseurl)
{
UserName = "user",
Password = "password",
AlwaysSendBasicAuthHeader = false
};
//ConnectionData has a string token property
var connectionData = client.Get<ConnectionData>("someServices/connectToSomeService");
And then use this token to listen for server events. Like so:
var eventClient =
new ServerEventsClient($"{Baseurl}/differentUri/retrieveSearchRequests?token={connectionData.Token}")
{
OnConnect = Console.WriteLine,
OnMessage = message => Console.WriteLine(message.Json),
OnCommand = message => Console.WriteLine(message.Json),
OnException = WriteLine,
ServiceClient = client, //same JsonServiceClient from the previous snippet
EventStreamRequestFilter = request =>
{
request.PreAuthenticate = true;
request.Credentials = new CredentialCache
{
{
new Uri(Baseurl), "Digest", new NetworkCredential("user", "password")
}
};
}
};
Console.WriteLine(eventClient.EventStreamUri); // "/event-stream&channels=" is appended at the end
eventClient.Start();
The problem with the above code is that it automatically appends "/event-stream&channels=" at the end of my URI. How do I disable this behavior?
I have tried adding the following class
public class AppHost : AppSelfHostBase
{
public static void Start()
{
new AppHost().Init().Start(Baseurl);
}
public AppHost() : base(typeof(AppHost).Name, typeof(AppHost).Assembly)
{
}
public override void Configure(Container container)
{
Plugins.Add(new ServerEventsFeature
{
StreamPath = string.Empty
});
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[]
{
new DigestAuthProvider()
}));
}
}
and called Start on it, before calling the above code, but still no luck.
The ServerEventsClient is only for listening to ServiceStack SSE Stream and should only be populated with the BaseUrl of the remote ServiceStack instance, i.e. not the path to the /event-stream or a queryString.
See this previous answer for additional customization available, e.g. you can use ResolveStreamUrl to add a QueryString to the EventStream URL it connects to:
var client = new ServerEventsClient(BaseUrl) {
ResolveStreamUrl = url => url.AddQueryParam("token", token)
});
If you've modified ServerEventsFeature.StreamPath to point to a different path, e.g:
Plugins.Add(new ServerEventsFeature
{
StreamPath = "/custom-event-stream"
});
You can change the ServerEventsClient to subscribe to the custom path with:
client.EventStreamPath = client.BaseUri.CombineWith("custom-event-stream");
ResolveStreamUrl + EventStreamPath is available from v5.0.3 that's now available on MyGet.
How to send web header collection from rest Service to remoting service?
I have tried to send web headers using below code but its not working.
System.Net.WebRequest request = base.GetWebRequest(uri);
request.Headers.Add("myheader", "myheader_value");
You can try the below sample
public RemotingServiceClient serviceClient = new RemotingServiceClient();
public void Demo()
{
using (OperationContextScope scope = new OperationContextScope(serviceClient.InnerChannel))
{
MessageHeader<string> header = new MessageHeader<string>("HeaderValue1");
var v1 = header.GetUntypedHeader("HeaderName1", "RemotingService");
OperationContext.Current.OutgoingMessageHeaders.Add(v1);
header = new MessageHeader<string>("HeaderValue2");
var v2 = header.GetUntypedHeader("HeaderName2", "RemotingService");
OperationContext.Current.OutgoingMessageHeaders.Add(v2);
//IMP: To send headers make sure to call service in this block only.
//Keep unique uri name "RemotingService"
return serviceClient.MyRemotingServiceCall();
}
}
It's working for me as expected