OWIN OpenIdConnect Middleware IDX10311 nonce cannot be validated - c#

I have an application using the OWIN middleware for OpenIdConnect. The startup.cs file uses the standard implementation of app.UseOpenIdConnectAuthentication. The cookie is set to the browser, but it errors with:
IDX10311: RequireNonce is 'true' (default) but validationContext.Nonce is null. A nonce cannot be validated. If you don't need to check the nonce, set OpenIdConnectProtocolValidator.RequireNonce to 'false'.
I've found that when running fiddler as I do for most debug projects this behavior happens. The error is returned, but if I go back to the site everything is working and my user is authenticated. Has anyone seen this behavior when running fiddler?
With fiddler:
SecurityTokenValidated notification in OpenIdConnect is executed twice.
After the second pass through the IDX10311 error is thrown
Browser contains the valid cookie, going back to the page I can view the valid User.Identity data.
Running without fiddler:
SecurityTokenValidated executes once in OpenIdConnect
No error thrown, proceeds to load up controller action for post authentication redirect Uri
Cookie also valid and User.Identity data correct.
Ideas? I can get around it without running fiddler, but when debugging it would be nice to also run fiddler to inspect traffic.

Maybe is this the cause?
Hello there, I think I found the root cause of this issue.
I'm summing up my discoveries:
The problem is in the OpenIdConnect.nonce.OpenIdConnect cookie
This cookie is set from the app (let's call this "ID Client") as soon as the OpenID Middleware init an authentication session
The cookie should be sent back from the browser to the "ID Client" as soon as the authentication has been completed. My assumption is that this cookie is needed to have a double check from the ID client point of view (i.e. did I really started an OpenID Connect authorization flow?)
A lot of confusion in me was caused by the "Nonce" term, used both in this cookie and in the OpenID Connect flow from the ID server.
The exception, in my case, was caused by the missing cookie (not the nonce of the ID Server), simply because it wasn't sent by the browser back to the "ID client"
So the main root, in my case, was this: OpenIdConnect.nonce.OpenIdConnect cookie was not sent back to the ID Client by the browser. In some cases (i.e. Chrome, Firefox and Edge) cookie was sent correctly, while in others (IE11, Safari) it wasn't.
After a lot of research, I discovered that the problem was on the Cookie restriction policy, defined on the browser. In my case, the "ID client" is embedded in an <iframe>. This cause the "ID Client" to be seen as a "third-party client", because the user didn't navigate to that URL directly in the main window. Because this is a third-party, for some browsers, it's cookies have to be blocked.
Indeed the same effect may be obtained on Chrome, by setting "Block third-party cookies".
So, I have to conclude that:
a) If iframe is a must (as in my case, because "ID Clients" are apps that must run inside the graphic content of the our main platform app), I think the only solution is to intercept the error, and handle it with a page, asking the user to enable third party cookies.
b) If iframe is not a must, it should suffice opening the "ID Client" in a new window.
Hope this helps somebody, because I got crazy!
Marco

I had the same problem but switching back the Microsoft.Owin.Security.OpenIdConnect to version 3.0.1 solved the issue

For anyone else who gets here in 2021, you'll likely get this issue if:
You're redirecting http -> https
Or you've changed your app's host domain.
Both of these aren't an issue with the middleware or your app, but it's about the combination of two issues:
The fact that your app is still hosted on the old old domain or protocol. You want to prevent browsers hitting that by implementing a redirect on the web server.
The redirect URI (sometimes know as reply URL) in Azure or whichever OpenIdConnect authorization server you're authenticating with. You want to get this updated to the new protocol or domain.
Our example: We had https://old.example.com/app/ that was now also hosted at https://new.example.com/app/. We wanted users' previous bookmarks to still work.
Our solution:
We updated the redirect URI (reply url) to point to the new domain for the app (https://new.example.com/app/signin-endpoint). Ideally, make sure there is only one URI listed for your app and that it's https.
We added the new domain binding to the site in IIS (we're old school, but do the same for your hosting of choice 😊)
We added an IIS redirect to the new domain (new.example.com) so that users' bookmarks still work. Again if you're not on IIS, implement a permanent redirect in the web server of your choice.
Until we had the final step above, we were seeing the error in the OP's post. It's the same process if you're forcing http -> https.
Here is the IIS re-write for those who are also "old school":
<rewrite>
<rules>
<rule name="Redirect old.example.com to new.example.com" enabled="true" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<conditions>
<add input="{HTTP_HOST}" pattern="old.example.com" />
</conditions>
<action type="Redirect" url="https://new.example.com{REQUEST_URI}" />
</rule>
</rules>
</rewrite>
It goes in the <system.webServer> section of your web.config file. Enjoy!

For me changing reply url in Azure active directory works.
This happens when you enable SSL because it changes only the sign on URL to the HTTPS URL while the reply URL remains the same HTTP URL.
When you try to access your app using the https URL, it sets a cookie with a unique number(nonce) in your browser and hits Azure AD for authentication. After authentication, the browser has to give access to that cookie. But since the sign on URL and reply URL are different the browser does not recognize your app and does not give access to that cookie and hence the application throws this error.

I know its an old post but I had this problem and nothing was working for me, after lose my mind behind a solution to make my enterprise application works I end up fixing it by setting the multi-tenanted option to yes in azure (in Azure select: app registration>settings>properties, set multi-tenanted to yes and click save).
hope it helps someone, couldn't see nobody mentioning it.

I noticed this error when running IIS Express in the background when I had switched to hosting in full IIS. When I disabled the IIS Express, my error went away.

A cookies rewrite rule in the web.config to ensure samesite cookies gave this cryptic exception. Disabling that rule solved it.

A temporary solution which worked for me for an app secured via Azure Active Directory was to signout (by going to the sites/Account/SignOut page) and then I was able to return to the home page and sign in ok. Hope this helps someone.

I know it's been a while on this one. My specific issue was with the IDX10311 error in relation to authenticating with IdentityServer while Fiddler (traffic inspector proxy) was running. I added a custom owin middleware to catch and absorb the IDX13011 in the case where the hostname contained "localhost". Ignoring this exception allowed us to use the site with fiddler as a workaround. I think it causes breaks in the authentication process though where we have to press enter in the browser address bar on the callbacks to get it going again, but this only affects development.
Here's the invoke method we used in the middleware to absorb the error. I should note though that we have seen this error in production occasionally as well. No explanation for a cause, but I have a feeling it is related to users on IE browsers.
public override async Task Invoke(IOwinContext context) {
try {
await Next.Invoke(context);
} catch (Exception ex) {
_errorHandling = new ErrorHandling();
if (ex.Message.Contains("IDX10803")) {
//do something here to alert your IT staff to a possible IdSvr outage
context.Response.Redirect("/Error/IdSvrDown?message=" + ex.Message);
} else if(ex.Message.Contains("IDX10311") && context.Request.Host.Value.Contains("localhost")) {
//absorb exception and allow middleware to continue
} else {
context.Response.Redirect("/Error/OwinMiddlewareError?exMsg=" + ex.Message + "&owinContextName=" + lastMiddlewareTypeName);
}
}
}

For me it was a different problem. My site was working with both of the urls below
https://www.example.com
and
https://example.com
But my redirect url was https://www.example.com.
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = ConfigurationManager.AppSettings["ClientId"].ToString(),
Authority = ConfigurationManager.AppSettings["Authority"].ToString(),
RedirectUri = ConfigurationManager.AppSettings["RedirectUri"].ToString();//https://www.example.com
}
Users who use https://example.com the mentioned exception occurs.
The cookie generated for www.example.com and example.com are different. So after the login when it redirects, the cookie doesn't contain the correct nonce to validate and the exception occurs.
The solution for the problem is to set the redirect URL dynamically
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = ConfigurationManager.AppSettings["ClientId"].ToString(),
Authority = ConfigurationManager.AppSettings["Authority"].ToString(),
RedirectUri = ConfigurationManager.AppSettings["RedirectUri"].ToString(),//https://www.example.com
,
// sample how to access token on form (when adding the token response type)
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = async n =>
{
var uri = n.Request.Uri; //From request URL determine the RedirctUri and set below
n.ProtocolMessage.RedirectUri =""//Set the url here
}
}
}
The same issue can happen with https://www.example.com and http://www.example.com

Users where getting this issue when Edge was set to IE compatibility mode, removed it from IE compatibility and it solved the issue. Setting / list of sites is controlled under edge://compat.

I end up allowing the Owin to skip to the next Middleware on AuthentificationFaild callback function. I'm checking if the error message contains nonce error Id and call SkipToNextMiddleware function from the context. With that, I'm restarting the sign-in process so if the user cookies were not set there will be a second call that will set the cookie.
The code is written in vb.net
Dim oidcAuthOpt= New OpenIdConnectAuthenticationOptions()
oidcAuthOpt.Notifications = New OpenIdConnectAuthenticationNotifications With {
.AuthenticationFailed = Function(n)
If (n.Exception.Message.StartsWith("OICE_20004") Or n.Exception.Message.Contains("IDX10311")) Then
n.SkipToNextMiddleware()
Return Task.FromResult(0)
End If
Return Task.FromResult(0)
End Function
}

Related

Blazor WASM authentication in Chrome Incognito

I do have a Blazor WASM (.NET 6) application that is using an Identity Server hosted on different URL for authentication.
I'm configuring the authentication like this:
builder.Services.AddOidcAuthentication(options =>
{
options.ProviderOptions.Authority = configuration.OidcAuthority;
options.ProviderOptions.ClientId = configuration.OidcClientId;
options.ProviderOptions.ResponseType = configuration.OidcResponseType;
options.ProviderOptions.PostLogoutRedirectUri = configuration.OidcPostLogoutRedirectUri;
options.ProviderOptions.RedirectUri = configuration.OidcRedirectUri;
options.ProviderOptions.DefaultScopes = configuration.DefaultScopes;
});
Everything works great locally and in production as well.
The problem occurs when I want to open the page in Chrome Incognito window with Third party cookies disabled.
I see in the browser that during authorization process I receive cookies with SameSite=None and Secure=true, but this cookie is not send with
https://login.myidserver.net/connect/authorize?client_id=myclientid&redirect_uri=h...
as oppose to normal Chrome window.
Because of that I receive back
https://localhost:12345/authentication/login-callback?error=login_required&state=....
and access_token is not saved in session storage as it should be.
Is there any way to overcome this and force the cookie to be used in this case?
Or am I misunderstanding something?
All the solutions I've seen on the internet seems not to work for me.
Edit:
Ok, why is authorize called right after sucessful authentication and receiving of the token?. I understand I cannot force to send this cookie but can I somehow avoid do this authorize call when I'm right after successful login?

How to force Federated signout redirect to login page?

I'm using WSFederationAuthentication module for authentication. I want this: after user press logout button, he signs out (delete all cookies) and redirect to login page.
I have this code for logout button:
var ls = new LoginStatus();
ls.LogoutAction = LogoutAction.Redirect;
ls.LogoutPageUrl = {some URL, where I have sign out code}
Signout part:
Microsoft.IdentityModel.Web.WSFederationAuthenticationModule authModule = FederatedAuthentication.WSFederationAuthenticationModule;
String signoutURL = WSFederationAuthenticationModule.GetFederationPassiveSignOutUrl(
authModule.Issuer,
{login Url},
null);
WSFederationAuthenticationModule.FederatedSignOut(
new Uri(signoutURL),
new Uri(authModule.Realm));
This code really do signout and delete cookies, but does not redirect to login page. Still, url, that users sees contains this part:
&wreply={loginUrl}
As I understand wreply parameter does not always is used.
Instead of using FederatedSignOut() method I tried this one:
System.Net.WebRequest req = System.Net.WebRequest.Create(signoutURL);
System.Net.WebResponse resp = req.GetResponse();
Redirect(LoginUrl);
But, this doesn't really do sign out. When user tries to log in next time, he doesn't need to enter any credentials and is signed in automatically. My guess, not all cookies are deleted.
So, there is my question, how can I do sign out and force redirect to login page?
P.S. I also delete FedAuth cookies by myself.
I think you may have misunderstood how federated sign on and sign out work (I say this as you have neglected to mention what I would consider is the most important thing.)
When you attempt to login to an application that uses WS-Federation you are actually redirected to an identity provider (Idp) and you login to this Idp. Once logged into the Idp you will be redirected back to your site with a security token and you will then be logged in to your site also.
At this stage you are logged in to two applications in effect:
The Idp
Your web site
What are you trying to achieve?
Logging out of your site only
Federated Sign out (i.e. Signing out
of the Idp and your site and any other relying partys)
If we are in case 1 then that is simple. Delete your cookies and you will be fine however you will still be logged into the Idp and so when a user navigates back to your site and gets redirected to the Idp they will likely be redirected back to your site with a security token without being prompted for credentials and will then be logged into your site again, which seems a bit pointless.
Given this, I think you are after scenario 2. Well in that case, the functionality you want actually depends on what software you are using for your Idp, something you have neglected to mention in the question.
Unfortunately I don't believe there is a generic way to do what you want with all Idp's and even more some Idp's won't directly support it.
I think it may be best raising a question about how your Idp works and how to get this to work. Also some very good things to include in the question would possibly be the URL's of your site and Idp (not for the purposes of "checking them out" but because other indirect solutions may be possible if they come under the same domain name). Also anything about your infrastructure of your Idp and site would also be helpful as again specific setups could give indirect solutions.
Seems to be, that redirect Url can be only Url, that is written in Idp config:
<passiveEndpoints>
<endpoint endpointType="WsFed" location="{this url}" binding="Post" />
</passiveEndpoints>
If wreply parameter value is any other url, it won't work.
"If a wreply parameter was specified in the ACSSignOut cookie, the
JavaScript redirect to the address indicated by the wreply value to
complete the sign-out. Otherwise, the JavaScript redirects to the
Return URL of the relying party, as specified in the ACS Management
Portal."
Source

Owin WebApi 2 Hostname IntegratedAuth Failing - 400 error and bad credentials

I have an intranet application using OWIN self hosting to serve a SPA/WebAPI 2 type site. Everything works great as localhost:port, and IP:port, but hostname:port fails for IE and Chrome. Yet the application works with FireFox.
I believe this is due to the integrated authentication. Chrome returns ERR_INVALID_AUTH_CREDENTIALS, and IE returns 400 bad request. Fiddler doesn't show much difference in the headers of the request/response, except that the Authentication comes through and I get a 200 in FF, a 400 in IE and a 401 in Chrome.
Most of my code was "borrowed" from SO articles or MSDN tutorials.Here's my Program.cs:
string baseAddress = "http://*:9000/";
using (WebApp.Start<Startup>(new StartOptions(baseAddress) { ServerFactory = "Microsoft.Owin.Host.HttpListener" }))
{
// Keep server operational till stopped
Console.WriteLine("Started, Press any key to stop.");
Console.ReadKey();
Console.WriteLine("Stopped");
}
Here's my startup.cs:
public void Configuration(IAppBuilder appBuilder)
{
var listener = (HttpListener)appBuilder.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
appBuilder.UseWebApi(config);
appBuilder.UseFileServer(new FileServerOptions
{
RequestPath = new PathString(string.Empty),
// path to static files.
FileSystem = new PhysicalFileSystem("./public"),
EnableDirectoryBrowsing = false
});
}
I also made sure to set the url reservation using netsh:
netsh http add urlacl url=http://*:9000/ user=Everyone
Both IE and Chrome attempt the intranet model of Integrated Authentication and attempt to send the credentials immediately in all of our intranet applications. FF always prompts for credentials and never auto-authenticates. This has me leaning towards authentication as the problem.
Does anyone have an idea for what I can check to validate the authentication side of things? Fiddler didn't illuminate the request very well. Breakpoints never seem to get touched in the app when requesting against the host.
I think there could be a group/local policy that is getting in the way of proper authentication, but I'm not certain where to start looking.
*edit - updated tags
*update
Some further testing indicates that forcing auth to NTLM (versus integrated) allows Chrome to auto-logon, FF to work the same as it always did, and IE in a weird place.
IE will autologon, but then fail to load the JS on the page, or Prompt for a user and then login properly depending on FQDN vs shortname. I believe this is due to the default IE behaviors and trusted/intranet site settings.
I now have a work around, but it'd still be nice to figure out kerberos if anyone has any ideas.
Change the code to
HttpListener listener = (HttpListener)app.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemes = AuthenticationSchemes.Ntlm;
In Self Host Web API, it solved the issue.

Basic Authentication redirects to login page instead of returning an auth challenge

I needed to support, in addition to standard out-of-the-box cookies/forms authentication, also Basic Authentication over HTTPS for SignalR. SignalR runs in the context of a mixed MVC/WebApi site.
After putting the pieces together how to implement this, I used ThinkTecture.IdentityModel.Owin.BasicAuthentication library for this on the server, like this:
app.Map("/basicauth", map =>
{
map.UseBasicAuthentication("realm", ValidateUser);
map.MapSignalR<AuthenticatedEchoConnection>("/echo");
map.MapSignalR();
});
But instead of returning a challenge, I always get a HTTP 302 response that redirects to the Login page of the MVC site. To better debug this, I quickly rolled my own simple OWIN middleware for basic authentication and got the same result. Further testing with a simple mapping like this:
app.Map("/test", map =>
{
map.Use((context, next) =>
{
// context.Response.StatusCode = 401;
context.Response.Write("Hello World!");
return Task.FromResult(0);
});
revealed that a simple "Hello World" response is returned normally. But when I comment out the the line that sets the response code to 401, I get the redirect to the Login page again. I do not understand this behavior ... why does my MVC site gets involved here and not the 401 response is returned? How can I prevent this?
For OWIN, besides the special /basicauth map above, I had only the top level SignalR mapping in the startup method defined that should continue to work for all cookie authenticated calls:
app.MapSignalR();
Nothing else had been configured by me for OWIN.
Can anybody help me with this?
Ok, I found a way to work around this issue. The first part is to throw out map.MapSignalR<AuthenticatedEchoConnection>("/echo"); from the first sample - it is not necessary and for some reason I not found out it prevented SignalR from working properly.
The second part, and the workaround for the actual problem, is that in the client I also send the credentials with the first request and do not wait for a challenge. Thus, a 401 never happens and so no redirect to login page. And that's good enough for me.
So the workaround is, in the client, do not use NetworkCredential like this:
connection.Credentials = new NetworkCredential(username, password);
Instead, add the Authorization header yourself so it is included already in the first request:
string creds = string.format("{0}:{1}", username, password);
string encodedCreds = Convert.ToBase64String(Encoding.Utf8.GetBytes(creds));
connection.Headers.Add("Authorization", "Basic " + encodedCreds);
In addition, what I found out is that with OWIN, this issue seems to be a more general one and not only related when used with SignalR or even Basic Auth:
http://brockallen.com/2013/10/27/using-cookie-authentication-middleware-with-web-api-and-401-response-codes/
Thus, with some modifications to the basic authentication module, it should also be possible to prevent this redirection. I might look into this and will update this post when done.

Using a "cross-domain" to front a login for another domain

I want to use the domain: aaaa.com to have a login form for the site at domain: cccc.com.
Note, I have full control of the server at cccc.com and have setup CORS on the server at cccc.com. I essentially have full control of the server at aaaa.com as well.
I am using jquery's $.ajax to send a POST to the cccc.com asp.net mvc 3 server. It looks like I get the right response back and I see the ASP.NET_SessionId and .ASPXAUTH cookies in the response. When I get the correct response in javascript with no login errors, I want to redirect to cccc.com/Home/Index using window.location. Everything seems to be working up to this point. Authentication, getting a correct response, etc. However when javascript redirects, cccc.com still wants me to login again. Why is this happening?
Is it because the authentication cookies belong to aaa.com? How can I work around this?
Thanks
Yes, the authentication cookies will belong to the other site, and are not shared.
If you had a subdomain of cccc.com instead of a completely separate domain, it would work if you set a domain-wide cookie.
As it is though, you will have to copy the cookie upon login, logout, and any other authentication methods that modify how the cookie is stored. If you're on a different server, you would also lose your ability to do sessions unless you have a session state server.
You could try copying the auth cookies with javascript after your POST to log in completes.

Categories

Resources