Requests to third party API from .NET Core app timeout in IIS - iis

I have a .NET Core Web API app running in IIS that can receive and respond to requests, but it needs to make requests to a third party API (Twilio). Those requests succeed when hosted locally but they time out when hosted in IIS.
The .NET Core Web API app is targeting v2.1 and running on Windows Server and IIS 7. I setup a dedicated app pool for the site following guidelines in this article.
App pool settings:
I'm using Postman to make GET and POST requests to the app.
GET and POST requests to a test controller work fine. All I'm doing with these test endpoints is returning the parameters that are received.
[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
[HttpGet]
public string Get(int id)
{
return $"response to GET; id:{id}";
}
[HttpPost]
public string Post([FromBody] int id)
{
return $"response to POST; foo:{id}";
}
}
The above endpoints work locally running debug mode in VS2019 using IISExpress, and they also work when deployed to the web server and hosted with IIS7.
The trouble starts when I try to call out to a third party API (Twilio). Again these calls succeed when the app runs locally. There's almost no delay. I post to the SMSController's post endpoint, the request is triggered, and I get a text message within seconds. However, GET and POST requests to these endpoints fails from the web server and IIS.
[Route("api/[controller]")]
[ApiController]
public class SMSController : ControllerBase
{
private string twilioSID = "xxxxxx";
private string twilioAuthToken = "xxxxxx";
private string twilioPhoneNumber = "+15555555555";
[HttpGet]
public string Get(string phoneNumber)
{
TwilioClient.Init(twilioSID, twilioAuthToken);
var message = MessageResource.Create(
body: $"TEST message via Twilio",
from: new Twilio.Types.PhoneNumber(twilioPhoneNumber),
to: new Twilio.Types.PhoneNumber(phoneNumber)
);
return message.Status.ToString();
}
[HttpPost]
public string Post([FromBody] GeoEvent geoEvent)
{
TwilioClient.Init(twilioSID, twilioAuthToken);
var message = MessageResource.Create(
body: $"TEST message via Twilio",
from: new Twilio.Types.PhoneNumber(twilioPhoneNumber),
to: new Twilio.Types.PhoneNumber(geoEvent.PhoneNumber)
);
return message.Status.ToString();
}
}
GET and POST requests to the above endpoints get a 500 response when hosted in IIS. The following exception is thrown:
System.Net.Http.HttpRequestException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond ---> System.Net.Sockets.SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
Lastly, here is the web.config that the SDK generated for me when I published the app in VS2019.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath=".\TwilioService.exe" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
</system.webServer>
</location>
</configuration>
Neither my local machine nor the web server running IIS are behind a corporate proxy.
Am I missing an IIS or web.config setting that will allow .NET Core to make requests through IIS to a third party?
UPDATE:
I have now reproduced the same issue on a different web server running Windows Server 2016. This machine is setup with .NET 5, IIS 10, and the hosting bundle for .NET 5. I also installed VS2019 and recreated the web api project to run locally on the server. Here are the results:
Running locally in IIS Express, the api controller can successfully
make a request to the Twilio api and gets a response. The SMS
message is sent successfully. No errors.
Running locally in IIS 10, the api controller calls Twilio but
fails to get a response. The following error occurs:
Twilio.Exceptions.ApiConnectionException: Connection Error: POSThttps://api.twilio.com/2010-04-01/Accounts//Messages.json
---> System.Net.Http.HttpRequestException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. (api.twilio.com:443)
To reiterate, both of the above scenarios are running on the same machine. The only difference is IIS Express vs IIS 10. Is this an SSL cert issue? Because IIS 10 works as a reverse proxy for .NET core apps, could there be a problem with a cert getting recognized or forwarded from the .net core app through IIS?

Try to ping the third-party service URL from your machine command prompt.
Make sure the firewall port is set to allow. check the antivirus is now blocking the connection.
try to add the defaultProxy section to my web.config in order to get my website to connect to a web service.
https://learn.microsoft.com/en-US/troubleshoot/dotnet/framework/cannot-consume-web-service-via-http-proxy

Solved. It turned out to be a corporate proxy after all. I had bad information that the web server was not behind a proxy but it was. Twilio has a solution for this exact scenario in their documentation involving the use of a custom proxied rest client class. It's a wrapper around System.Net.HttpClient. The service can now make calls to Twilio through the proxy using this class with the proxy server url set.

Related

LEGACY EDGE ONLY: HTTP request is unauthorized with client authentication scheme 'Negotiate'. .'

I have an application that calls a wcf service, the application uses windows authentication and anonymous access is disabled. I am still getting the error in accessing the service:
'The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'Negotiate,NTLM'.' '
The odd part is that it is working fine in IE11 and chrome (v83), this issue is appearing on Windows 10 1809 version only.
Attaching screenshots from my IIS below.
Can anyone suggest what could be the possible issue?
try to set the below setting in iis:
open iis, Select Website
Select ‘Configuration Editor’
Select
‘system.webServer/security/authentication/windowsAuthentication’
useAppPoolCredential to True
Make sure both WCF and the .net application URL using the same protocol.
set the application pool user to the domain account.
at client configuration set below code:
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
you could refer below link for more detail:L
401 Client 'Negotiate', Server 'Negotiate,NTLM' When Calling WCF Server to Server

IDX10803 and IDX10804 error with Azure AD

I have created a Web API in ASP.NET Core 2 and it is secured with Azure AD. The requests work fine in my dev environment. However when I publish the API to IIS and pass in a Authorization header in request I am getting these error:
fail: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[3]
Exception occurred while processing message.
System.InvalidOperationException: IDX10803: Unable to obtain configuration from: 'https://login.microsoftonline.com/xxxx/.well-known/openid-configuration'. ---> System.IO.IOException: IDX10804: Unable to retrieve document from: 'https://login.microsoftonline.com/xxxx/.well-known/openid-configuration'. ---> System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.Http.WinHttpException: A connection with the server could not be established
What am I doing wrong? I can browse the url in browser successfully.
I figured it out. It was because of a proxy setting to access the internet. Since ASP.NET Core does not pick up proxy setting from system.net area of web.config, the ideal solution was to create a middle ware to act as proxy.
I was running short of time. So I created a new user and logged on server with that new user and configured the proxy settings in Internet Explorer options. Then I configured the App Pool in IIS to run under that user's identity. Problem solved!
The correct answer is to add a proxy to the metadata request that ADAL makes using BackchannelHttpHandler. You can do it like this:
public void Configure(string name, JwtBearerOptions options)
{
options.BackchannelHttpHandler = new HttpClientHandler
{
UseProxy = true,
Proxy = new WebProxy
{
Address = new Uri($"{appSettings.InternetProxyUrl}:{appSettings.InternetProxyPort}"),
UseDefaultCredentials = true
}
};
}

MVC 5 SQL Network Interfaces, error: 26 - Error, with [Authorize]

I'm having trouble setting up Authorization in MVC 5. I have a brand new MVC 5 website with Individual User Accounts and an SQL Server database with the five aspNet... tables.
I can login locally, it all works fine on my local server and I can even login the production database from my local website when I point my local connection string at the production server but I can't login from the website on the production server,
I get the following error:
A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)
The error only happens when I use [Authorize] on an Action where I then have to login, if I remove [Authorize] the Action and database work fine.
Any ideas
Edit
Just for clarity, the SQL Server is on one of Arvixe servers, and the website is at Godaddy here is my connection string
<add name="mysiteEntities"
connectionString="metadata =
res://*/Models.mysite.csdl|res://*/Models.mysite.ssdl|res://*/Models.mysite.msl;
provider = System.Data.SqlClient;
provider connection string ='
data source = xxxxx.arvixe.com;
Initial Catalog = mysite;
User ID = xxxxxx;
Password = xxxxxxx'"
providerName="System.Data.EntityClient" />
I am unable to login from Godaddy with this string but if I use this string on my local computer I can login.
I also have a number of websites set up with the same configuration in MVC 4 which work fine.
I had a similar issue; however, I wrongly assumed it to be that I was not using EF and SQL Server, but Telerik Data Access around Oracle 11g. What fixed it for me was to remove the RoleManager module in the config file, as per this article (http://blog.luppes.com/2013/12/08/mvc5-authentication-roles-failure/)
So, to paraphrase that link for posterity, it was:
<system.webServer>
<modules>
<remove name="RoleManager" />
</modules>
</system.webServer>
I worked this out but it let me to a new question that I wave a work around for. For this question I had the wrong base connection referenced in here.
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
The problem now is I need two connection strings to connect. One for Authorisation as MVC5 wants a Code First connection string but the rest of my code is Database First

signalR giving 403 when authenticated

I have a MVC app that uses signalR and Forms authentication and published to Azure Service - Was working fine with MVC4, SignalR 1.1.2
We upgraded to VS 2013 and the latest and greatest of all the packages, MVC 5.5.2 and signalR 2.1.2, and made the required changes for mapping SignalR via OWIN
Now when I am not logged in, signalR works as expected and connects using WebSockets. But when I do login and refresh the same page, signalR no longer works.
I get an 403 error on connecting via websockets
It then tries server sent events with comes back as (cancelled)
and then Long Polling also fails with a 403 - Forbidden error
Note that when running locally it works fine, it just when publish to azure service that I get this problem
I must be something simple I'm missing. Anybody done a similar upgrade?
Its a large app, so can't really share much code, but in essence all I'm doing is connecting to the signalR hub
<script type="text/javascript" src="/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
// SignalR - Initialise
var dashboardHub = $.connection.miHub;
// start signalR hub - this will connect the user
$.connection.hub.start();
});
</script>
and hub class
public class MiHub : Hub
{
public override Task OnConnected()
{
ConnectUser();
return null;
}
....
}
When I am not logged in to my app, dev tools shows the following:
wss://myservice.cloudapp.net/signalr/connect?transport=webSockets&clientProtocol=1.4&connectionToken=...
response 101
wss://myservice.cloudapp.net/signalr/connect?transport=webSockets&clientProtocol=1.4&connectionToken=...
response 200
{ "Response": "started" }
When I logged in and hit F5 on the same page I get
wss://myservice.cloudapp.net/signalr/connect?transport=webSockets&clientProtocol=1.4&connectionToken=...
response 403

Unable to authenticate to ASP.NET Web Api service with HttpClient

I have an ASP.NET Web API service that runs on a web server with Windows Authentication enabled.
I have a client site built on MVC4 that runs in a different site on the same web server that uses the HttpClient to pull data from the service. This client site runs with identity impersonation enabled and also uses windows authentication.
The web server is Windows Server 2008 R2 with IIS 7.5.
The challenge I am having is getting the HttpClient to pass the current windows user as part of its authentication process. I have configured the HttpClient in this manner:
var clientHandler = new HttpClientHandler();
clientHandler.UseDefaultCredentials = true;
clientHandler.PreAuthenticate = true;
clientHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
var httpClient = new HttpClient(clientHandler);
My understanding is that running the site with identity impersonation enabled and then building the client in this manner should result in the client authenticating to the service using the impersonated identity of the currently logged in user.
This is not happening. In fact, the client doesn't seem to be authenticating at all.
The service is configured to use windows authentication and this seems to work perfectly. I can go to http://server/api/shippers in my web browser and be prompted for windows authentication, once entered I receive the data requested.
In the IIS logs I see the API requests being received with no authentication and receiving a 401 challenge response.
Documentation on this one seems to be sparse.
I need some insight into what could be wrong or another way to use windows authentication with this application.
Thank You,
Craig
I have investigated the source code of HttpClientHandler (the latest version I was able to get my hands on) and this is what can be found in SendAsync method:
// BeginGetResponse/BeginGetRequestStream have a lot of setup work to do before becoming async
// (proxy, dns, connection pooling, etc). Run these on a separate thread.
// Do not provide a cancellation token; if this helper task could be canceled before starting then
// nobody would complete the tcs.
Task.Factory.StartNew(startRequest, state);
Now if you check within your code the value of SecurityContext.IsWindowsIdentityFlowSuppressed() you will most probably get true. In result the StartRequest method is executed in new thread with the credentials of the asp.net process (not the credentials of the impersonated user).
There are two possible ways out of this. If you have access to yours server aspnet_config.config, you should set following settings (setting those in web.config seems to have no effect):
<legacyImpersonationPolicy enabled="false"/>
<alwaysFlowImpersonationPolicy enabled="true"/>
If you can't change the aspnet_config.config you will have to create your own HttpClientHandler to support this scenario.
UPDATE REGARDING THE USAGE OF FQDN
The issue you have hit here is a feature in Windows that is designed to protect against "reflection attacks". To work around this you need to whitelist the domain you are trying to access on the machine that is trying to access the server. Follow below steps:
Go to Start --> Run --> regedit
Locate HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0 registry key.
Right-click on it, choose New and then Multi-String Value.
Type BackConnectionHostNames (ENTER).
Right-click just created value and choose Modify.
Put the host name(s) for the site(s) that are on the local computer in the value box and click OK (each host name/FQDN needs to be on it's own line, no wildcards, the name must be exact match).
Save everything and restart the machine
You can read full KB article regarding the issue here.
I was also having this same problem. Thanks to the research done by #tpeczek, I developed the following solution: instead of using the HttpClient (which creates threads and sends requests async,) I used the WebClient class which issues requests on the same thread. Doing so enables me to pass on the user's identity to WebAPI from another ASP.NET application.
The obvious downside is that this will not work async.
var wi = (WindowsIdentity)HttpContext.User.Identity;
var wic = wi.Impersonate();
try
{
var data = JsonConvert.SerializeObject(new
{
Property1 = 1,
Property2 = "blah"
});
using (var client = new WebClient { UseDefaultCredentials = true })
{
client.Headers.Add(HttpRequestHeader.ContentType, "application/json; charset=utf-8");
client.UploadData("http://url/api/controller", "POST", Encoding.UTF8.GetBytes(data));
}
}
catch (Exception exc)
{
// handle exception
}
finally
{
wic.Undo();
}
Note: Requires NuGet package: Newtonsoft.Json, which is the same JSON serializer WebAPI uses.
The reason why this is not working is because you need double hop authentication.
The first hop is the web server, getting impersonation with Windows authentication to work there is no problem. But when using HttpClient or WebClient to authenticate you to another server, the web server needs to run on an account that has permission to do the necessary delegation.
See the following for more details:
http://blogs.technet.com/b/askds/archive/2008/06/13/understanding-kerberos-double-hop.aspx
Fix using the "setspn" command:
http://www.phishthis.com/2009/10/24/how-to-configure-ad-sql-and-iis-for-two-hop-kerberos-authentication-2/
(You will need sufficient access rights to perform these operations.)
Just consider what would happen if any server was allowed to forward your credentials as it pleases... To avoid this security issue, the domain controller needs to know which accounts are allowed to perform the delegation.
To impersonate the original (authenticated) user, use the following configuration in the Web.config file:
<authentication mode="Windows" />
<identity impersonate="true" />
With this configuration, ASP.NET always impersonates the authenticated user, and all resource access is performed using the authenticated user's security context.

Resources