We've upgraded our application from .NET 6 to .NET 7 and now the Kerberos authentication fails. I've created a simple console application to replicate the behavior and built it for both .NET 6 and .NET 7. The .NET 6 application does a successful authentication, but .NET 7 fails with the stacktrace below.
OS: Windows Server 2019 Standard
.NET Version: 7
Application: WPF and
Console App, same behavior.
using System.Configuration;
Console.WriteLine("Beep boop bop... starting application");
HttpClientHandler handler = new()
{
UseDefaultCredentials = true,
PreAuthenticate = true,
};
handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => {
return true;
};
HttpClient client = new(handler);
string url = ConfigurationManager.AppSettings["IdpUrl"];
Console.WriteLine($"Requesting: {url}");
HttpResponseMessage? response = await client.GetAsync(url);
Console.WriteLine($"Response status code: {response.StatusCode}");
Console.WriteLine($"Response: {response}");
Console.WriteLine("Beep boop bop... signing off");
Console.ReadLine();
The stacktrace:
System.Net.Http.HttpRequestException: Authentication validation failed with error - InvalidToken. at System.Net.Http.AuthenticationHelper.SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, Boolean async, ICredentials credentials, Boolean isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.AuthenticationHelper.SendWithAuthAsync(HttpRequestMessage request, Uri authUri, Boolean async, ICredentials credentials, Boolean preAuthenticate, Boolean isProxyAuth, Boolean doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken) at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpClient.g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
From a Kerberos point of view, this is the normal process:
Call happens to a URL
The server replies with met een 401 and WWW-Authenticate: Negotiate header
Client replies with a kerberos token
The server does the Kerberos validation and returns a 200 reply with in the body a valid JWT which is the case.
I've omitted the token(s) for privacy reasons and added some X's where other data use to be.
Fiddler results for the Kerberos authentication:
HTTP/1.1 401
Server: xxxxxxxx
Date: Mon, 06 Feb 2023 10:13:22 GMT
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 519
Connection: keep-alive
Set-Cookie: XX_JSESSIONID=XXXXXXXXXXXXXXXXXXXXXXXXXXXX; Path=/xx; Secure; HttpOnly; SameSite=None
Cache-Control: no-store
WWW-Authenticate: Negotiate
Pragma: no-cache
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000; includeSubdomains;
Cache-Control: private, max-age=0, no-cache, no-store
Pragma: no-cache
Proxy-Support: Session-Based-Authentication
HTTP/1.1 200
Server: xxxxxx
Date: Mon, 06 Feb 2023 10:13:22 GMT
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 1124
Connection: keep-alive
Set-Cookie: XX_JSESSIONID=XXXXXXXXXXXXXXXXXX; Path=/xx; Secure; HttpOnly; SameSite=None
WWW-Authenticate: Negotiate (A TOKEN)
Cache-Control: max-age=0
Pragma: no-cache
Expires: Mon, 06 Feb 2023 10:13:22 GMT
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000; includeSubdomains;
Cache-Control: private, max-age=0, no-cache, no-store
Pragma: no-cache
I was expecting the same behavior in .NET and a successful Kerberos authentication.
Related
I getting the stuck while post the Request json data with bearer token in c#.
EX - Jsondata
"{"data":{"POLICY_NO":"29991231030","POLICY_TYPE":"1","HEGICCardNo_Var":"HC1801724-01A"}}"
response:
{StatusCode: 403,
ReasonPhrase: 'Forbidden',
Version: 1.1,
Content: System.Net.Http.StreamContent,
Headers:
{
Pragma: no-cache
Access-Control-Allow-Origin: *
Cache-Control: no-cache
Date: Sat, 02 Mar 2019 06:21:40 GMT
Server: Microsoft-IIS/7.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Content-Length: 28
Content-Type: text/plain; charset=utf-8
Expires: -1
}}
While posting the Json data in web service Respective url with Bearer Token.
Then First Add the AuthenticationHeaderValue as Below C# Code :
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Authorization/Bearer", TokenValue);
HttpResponseMessage messge = client.PostAsync(Url,ContentObj).Result;
I have an web api wand I call it method in an ajax with POST method, sending the parameter as data . It gives me a 404 not found error.
But when I pass the data in the query string , it works fine. Please help me fixing the matter and get it worked when calling the method with data in the body, not the query string.
Note : The web application and Web-api are running on different ports
Sending a concrete type object in query string also does not work and received to web api as null
config
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type, contentType, data" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>
Controller
public class RiskController : ApiController
{
method
[HttpGet, HttpPost]
public DashboardContainerViewModel GetDashboardContainer(string token)
{
Ajax call
var postData = { token: priv.secToken };
$.ajax({
type: 'POST',
url: 'http://localhost:48060/api/Risk/GetDashboardContainer',
dataType: 'json',
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
data: postData,
success: function (data) {
pub.dashboardHeader(data.Name);
var defaultTab = _.find(data.Dashboards, function (tab) {
return tab.IsDefault == true;
});
And I get POST http://localhost:48060/api/Risk/GetDashboardContainer 404 (Not Found)
Fiddler request headers
POST http://localhost:48060/api/Risk/GetDashboardContainer HTTP/1.1
Host: localhost:48060
Connection: keep-alive
Content-Length: 142
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://localhost:10452
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost:10452/Authorised/DashBoard/DashBoard.aspx
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
token=ETvgg0YH4sx9w%2FiQL5560S20Ja4jGix%2FiBTOFGpQCliYZVilmtKiXPbk30d8FTYbhUWyFqz8%2FqT1pmI0oY6rzDGQ7krL6d2fDKDKwJhfCNFXZxnv%2BPCUj5ki5eizdWOM
Fiddler response headers
HTTP/1.1 404 Not Found
Cache-Control: no-cache, no-store
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcRGVmYXVsdENvbGxlY3Rpb25cQ0FNTVNfUHJvZHVjdHNcX1JlbGVhc2VcMTQxMExSLUFNXFdlYiBTZXJ2aWNlXENBTU1TLlNBQVMuV2ViQVBJXENBTU1TLlNBQVMuSVJNV2ViQVBJXGFwaVxSaXNrXEdldERhc2hib2FyZENvbnRhaW5lcg==?=
X-Powered-By: ASP.NET
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type, contentType, data
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Date: Fri, 29 Jun 2018 05:47:22 GMT
Content-Length: 215
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:48060/api/Risk/GetDashboardContainer'.","MessageDetail":"No action was found on the controller 'Risk' that matches the request."}
This is most lilkely due to a routing error in my opinion and experience, have you tried looking at RouteConfig.cs to make sure it sets the right routes for your API?
Something like this:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Your Controller method uses simple type parameter. WebApi will search for simple parameter in request URL. So if you want to POST parameter, you must tell WebApi to look at the request body.
public DashboardContainerViewModel GetDashboardContainer([FromBody] string token)
But you also need to change the format of data you are sending. Simple type data sent as application/x-www-form-urlencoded must be in format '=value' not 'token=value'
var postData = "=testData";
I'm making a simple c# program to establish if server side compression is available/enabled on various servers. This is my request code below.
using (var client = new HttpClient())
{
client.Timeout = new TimeSpan(0, 0, 5);
client.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.97 Safari/537.36");
client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate, sdch, br");
var httpClientResponse = client.GetAsync(websiteURL).Result;
string header = httpClientResponse.Headers.GetValues("Content-Encoding").First();
}
I can see by watching the request in fiddler that compression is enabled for this request however I can't seem to extract that information from the response headers in code.
These are the full headers for request and response.
GET https://www.dobbies.com/ HTTP/1.1
Accept: text/html, application/xhtml+xml, application/xml; q=0.9, image/webp, */*; q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.97 Safari/537.36
Accept-Encoding: gzip, deflate, sdch, br
Host: www.dobbies.com
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server:
Set-Cookie: ASP.NET_SessionId=hx1rb34ottgfritgt3rciql4; path=/; secure; HttpOnly
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000
X-Xss-Protection: 1; mode=block
Date: Fri, 07 Apr 2017 08:06:17 GMT
Content-Length: 16836
This is what I get when I use httpClientResponse.Headers
{
Vary: Accept-Encoding
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000
X-Xss-Protection: 1; mode=block
Cache-Control: private
Date: Fri, 07 Apr 2017 08:06:17 GMT
Set-Cookie: ASP.NET_SessionId=hx1rb34ottgfritgt3rciql4; path=/; secure; HttpOnly
Server:
}
As you can see header of Content-Encoding: gzip is missing in the response.
Why would this header be missing? Along with others. Give me back my headers!
Found the problem.
The HttpResponseMessage returned by HttpClient methods has two header properties:
HttpResponseMessage.Headers is an HttpResponseHeaders with generic response headers
HttpResponseMessage.Content.Headers is an HttpContentHeaders with content-specific headers like Content-Type
I use webapi when i hosted it on localhost and tried to invoke it it works fine but when i hosted it to a domain server it returns: "StatusCode: 404, ReasonPhrase: 'Not Found'"
client.BaseAddress = new Uri("http://xyz.website.com/API");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("api/Store").Result;
but the result is:
{StatusCode: 404, ReasonPhrase: 'Not Found', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Connection: Keep-Alive
Date: Sun, 07 Sep 2014 14:19:28 GMT
Server: Microsoft-IIS/8.0
X-Powered-By: ASP.NET
Content-Length: 1245
Content-Type: text/html
}}
i think there is a problem with the prefix name ('xyz') before website.com
I have a known working web service which is configured as a SOAP and REST binding. I can call it with a test harness and I can see the SOAP POST's going to and then the SOAP response all good.
However when I try and host the service in visual studio 2010 Cassini I get the following behaviour:
1) When calling endpoint as REST and using the WebChannelFactory class the service works.
2) When calling the endpoint as SOAP and using the ChannelFactory class the service throws an error and I get a 400 response. (The error is below).
3) When calling the endpoint as SOAP and using a c# SOAP reference the service works.
Confusing no?
Im wondering is it something to do with the fact that I have used a port number for the Cassini version?
just before people start telling me to host in IIS I really need to get this working in Cassini as its part of a tutorial on WCF
Here is the raw HTTP request that doesnt work (when called against a working IIS deployment and the faulty Visual Studio webserver.
IIS WORKING:
POST http://api.dev.videolibraryserver.com/VLSContentService.svc/soap/ HTTP/1.1
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tempuri.org/IVLSContentService/EchoWithGet"
Host: api.dev.videolibraryserver.com
Content-Length: 180
Expect: 100-continue
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><EchoWithGet xmlns="http://tempuri.org/"><message>Hello World!</message></EchoWithGet></s:Body></s:Envelope>
VISUAL STUDIO (CASSINI) - NOT WORKING:
POST http://127.0.0.1:52587/VLSContentService.svc//soap HTTP/1.1
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tempuri.org/IVLSContentService/EchoWithGet"
Host: 127.0.0.1:52587
Content-Length: 180
Expect: 100-continue
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><EchoWithGet xmlns="http://tempuri.org/"><message>Hello World!</message></EchoWithGet></s:Body></s:Envelope>
THE ERROR RESPONSE WHEN USING CASSINI
HTTP/1.1 400 Bad Request
Server: ASP.NET Development Server/10.0.0.0
Date: Thu, 23 Jun 2011 10:42:09 GMT
Content-Length: 1209
Content-type: text/html;charset=utf-8
Connection: Close
<html>
<head>
<title>Bad Request</title>
<style>
body {font-family:"Verdana";font-weight:normal;font-size: 8pt;color:black;}
p {font-family:"Verdana";font-weight:normal;color:black;margin-top: -5px}
b {font-family:"Verdana";font-weight:bold;color:black;margin-top: -5px}
h1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red }
h2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maroon }
pre {font-family:"Lucida Console";font-size: 8pt}
.marker {font-weight: bold; color: black;text-decoration: none;}
.version {color: gray;}
.error {margin-bottom: 10px;}
.expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:hand; }
</style>
</head>
<body bgcolor="white">
<span><h1>Server Error in '/' Application.<hr width=100% size=1 color=silver></h1>
<h2> <i>HTTP Error 400 - Bad Request.</i> </h2></span>
<hr width=100% size=1 color=silver>
<b>Version Information:</b> ASP.NET Development Server 10.0.0.0
</font>
</body>
</html>
THE .NET EXCEPTION (although irrelevant as no response returned from server)
C:\Users\Peter Heard\Desktop\TestClient\Debug>TestClient.exe "http://ipv4.fiddle
r:52587/VLSContentService.svc/" "Hello World!" 2
***************************
About to call SOAP web service interface TestSOAPUsingChannelFactory()
There was an errorSystem.ServiceModel.ProtocolException: The remote server retur
ned an unexpected response: (400) Bad Request. ---> System.Net.WebException: The
remote server returned an error: (400) Bad Request.
at System.Net.HttpWebRequest.GetResponse()
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpCha
nnelRequest.WaitForReply(TimeSpan timeout)
--- End of inner exception stack trace ---
Server stack trace:
at System.ServiceModel.Channels.HttpChannelUtilities.ValidateRequestReplyResp
onse(HttpWebRequest request, HttpWebResponse response, HttpChannelFactory factor
y, WebException responseException, ChannelBinding channelBinding)
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpCha
nnelRequest.WaitForReply(TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeS
pan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message messag
e, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean on
eway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan tim
eout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCall
Message methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage req
Msg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgDa
ta, Int32 type)
at IVLSContentService.EchoWithGet(String message)
at TestClient.Program.TestSOAPUsingChannelFactory() in C:\Repositories\VideoL
ibraryServer\trunk\prototypes\Hml_Api_Wcf\TestClient\Program.cs:line 96
at TestClient.Program.Main(String[] args) in C:\Repositories\VideoLibraryServ
er\trunk\prototypes\Hml_Api_Wcf\TestClient\Program.cs:line 47
*******Update*************
Whilst the answer to this question was correct. My testing wasnt. I actually tried to pass a '//' in the url for the IIS and Cassini and it didnt work for Cassini. Obviously IIS is a bit more robust when handling quotes. The comments below talk about this but I thought i would update the answer.
The first line of your "not working" request has an extra slash in the URL:
POST http://127.0.0.1:52587/VLSContentService.svc//soap HTTP/1.1
^