On an application that has form based authentication, I have a standard ASP.NET Login control with the following Authenticate event handler.
void Login_Authenticate(object sender, AuthenticateEventArgs e)
{
if (Security.AuthenticateUser(Login.UserName, Login.Password))
{
e.Authenticated = true;
RedirectFromLoginPage(Login.UserName);
}
else
{
e.Authenticated = false;
}
}
The RedirectFromLoginPage function goes like this :
private void RedirectFromLoginPage(String username)
{
String returnUrl = GetReturnUrl();
FormsAuthentication.SetAuthCookie(username, true, "/");
Response.Redirect(returnUrl, true);
}
This works fine in 99% of cases. However, I sometimes get support calls from people who can't log in. They will enter their credentials, get redirected back to the home page (which is what happens when everything works fine) but they won't be logged in.
Figuring it might be a cookie problem, I tried to reproduce the problem in my environment by setting my privacy options to "Block All Cookies" and I was able to reproduce the problem. The SetAuthCookie function is called, but on the next page load HttpContext.Current.User.Identity.IsAuthenticated returns false.
In my web.config, the authentication is set like so :
<authentication mode="Forms">
<forms loginUrl="..." timeout="180" cookieless="AutoDetect"/>
</authentication>
Reading the documentation on MSDN about AutoDetect and SetAuthCookie, I got that :
AutoDetect Specifies that cookies are
used, if the device profile supports
cookies; otherwise, cookies are not
used.For desktop browsers that are
known to support cookies, a probing
mechanism will be used to try to use
cookies, when enabled. If a device
does not support cookies, no probing
mechanism will be used.
FormsAuthentication.SetAuthCookie :
Creates an authentication ticket for
the supplied user name and adds it to
the cookies collection of the
response, using the supplied cookie
path, or using the URL if you are
using cookieless authentication.
I would of thought that in my scenario, cookieless authentication would of been used but it isn't (I don't see anything in the QueryString after the redirect anyway).
If I set a breakpoint in the RedirectFromLoginPage function and test some values I get :
bool cookieSupport = Request.Browser.Cookies; //"true"
bool redirectWithCookies = Request.Browser.SupportsRedirectWithCookie; //"true"
HttpCookieMode currentMode = FormsAuthentication.CookieMode; //"AutoDetect"
I'm not sure if the Request.Browser.Cookies is meant to be true or not here. The browser does support cookies, but they are all blocked...
Anyway, I got to remote for a few minutes on a machine where the problem was happening. The privacy settings were set to medium so it should of been able to accept cookies. It was a standard Win7 / IE8 setup. I tried adding the website to the user's trusted zone, to login via https but it didn't work. Other problem setups were similar (nothing really stands out with the machines and the users tell me they have no problems on other websites)
So what am I doing wrong here?
Do you specify the domain for the forms authentication cookie in the web.config file? And does it match the domain for the website?
I believe Medium security settings in IE block third party cookies so the problem could be IE thinking your authentication cookie is a third party cookie.
A similar problem occured for me. But it was only for Internet Explorer 8. After some research, I figured that IE8 runs on cookiless mode by default. So, I changed this line in web.config:
<forms loginUrl="..." timeout="180" cookieless="AutoDetect"/> to
<forms loginUrl="..." timeout="180" cookieless="UseUri"/>, and it works fine.
Related
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
}
I have a project in Nop Commerce 3.0. Last week I try to enable the SSL encryption in my
website. I have enabled the SSL in my Store table then set
SecuritySettings.ForceSslForAllPages = false. Then after I test my application in local host.
My home page is getting correctly.
But when I click the Login link in my home page I get this error
Code behind
[NopHttpsRequirement(SslRequirement.Yes)]
public ActionResult Login(bool? checkoutAsGuest)
{
var model = new LoginModel();
model.UsernamesEnabled = _customerSettings.UsernamesEnabled;
model.CheckoutAsGuest = checkoutAsGuest.HasValue ? checkoutAsGuest.Value : false;
model.DisplayCaptcha = _captchaSettings.Enabled && _captchaSettings.ShowOnLoginPage;
return View(model);
}
In web.config
<authentication mode="Forms">
<forms name="NOPCOMMERCE.AUTH" loginUrl="~/login" protection="All" timeout="43200" path="/" requireSSL="false" slidingExpiration="true" />
</authentication>
but this page available in http. I don't want this page with https
Please help.
Edit :
I have a excuse to say because i didn't know how to solve this problem. This project is my
new assignment. I am not a real developer of this project. this project is alredy works in
the live website without any problem. please see the screen shot below.( url is not
correct.)
Live website home page
I clicked the login link it's correctly redirect the login page.
I check my admin section for SSL configuration. But i am wondered it's true.
I connect my live database to the local host source code i get the above local host error.
I guess anything is depending the Nop.Core project for this SSL configuration.
If you have enabled SSL, all actions annotated with NopHttpsRequirement will redirect to https. The setting SecuritySettings.ForceSslForAllPages just enforces that ALL pages (even those not annotated) should redirect to SSL.
The problem is that you are trying to run http and https on the same port. They need to be listening on different ports (you are requesting both on 9999)
We are use a reverse engineering tool for make source code from correct working Nop.Core dll from the website. we found some classes are use a helper functions for building URLs in the websites. This function is directly append the https string in the url.
These are the following classes and path in Nop.Core projects
ResolveLinksHelper.cs -- Nop.Core/Html
WebHelper.cs -- Root
CommonHelper.cs -- Root
We replace all "https" string to "http" in these functions.
Now the application is works perfectly after hosting.
i m relatively new to C# and ASP.NET and I am having trouble designing an authentication system.
I have created a website where the user has to login, after which he can access various pages in this site. When the user clicks a logout link, he returns to the login page and is given the message "you have successfully logout." Now how do I prevent the user from typing the URL of one of the internal pages, bypassing my authentication? While working with PHP, I used session_start() and ob_end_flush() at the beginning and the end of each page to control authentication. What is a similar model in ASP.NET?
Also how do I include a .cs file from app_code folder to a aspx.cs?
If you are using FormsAuthentication, this is simple to do using configuration in web.config.
<authentication mode="Forms">
<forms name=".ASPXAUTH" loginUrl="default.aspx" protection="All" path="/" slidingExpiration="true" timeout="60" />
</authentication>
This configuration forces aspx to ensure that all pages in the site can only be accessed by authorized users.
However, there is a logic problem with this configuration: no one would ever be able to login since they must be authorized to access any page in the site.
You can fix this, however, by opening "holes" in this protected by adding specific pages and indicating they can be authorized by anyone:
<location path="default.aspx">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
Now how do i prevent re entry into the site by typing the url of the internal pages in the address bar.
How are you actually tracking the authentication? Forms authentication? Windows authentication? Something custom? Essentially, what you need to do is have those pages check for a valid authentication token. If no such token exists, redirect to the login page or an error or something to that effect.
You can do this by checking for authentication manually in the Page_Init method (which can access Session data, Cookies data, etc. where you'd store such a token), you can use various methods built-in, etc.
The concept is the same as it was in PHP, the tooling is just a little different.
While working with PHP i used session_start() and ob_end_flush() at the beginning and the end of each page.....What is it im supposed to use in c#?
You don't need to explicitly start/end session state in ASP.NET. Any code in the scope of the web application can access session state/values via System.Web.HttpContext.Current.Session. Any request coming from the same session will have this data associated with it.
Also how do i include .cs file from app_code folder to a aspx.cs
While in PHP you had to include files, in ASP.NET it's compiled code so the file isn't so important. What you need to reference is the namespace/class to use the code. For example...
If you have the following in a file in App_Code:
namespace MyApplicationCode
{
public class SomeCode
{
// stuff in the class
}
}
Then from any code within the application you should be able to use it by it's fully-qualified name (MyApplicationCode.SomeCode):
var someVariable = new MyApplicationCode.SomeCode();
Additionally, you can add a using statement in the header of the code file:
using MyApplicationCode;
And then access it directly:
var someVariable = new SomeCode();
When a user is successfully authenticated with his credentials, a cookie is set with a session id that corresponds to a file that stores value on the server. This cookie confirms to the server that the user is authenticated.
Check how your system handles sessions, it can be done without a session cookie, too:
(pseudo-code)
if (User.Login(formUsername, formPassword)) {
SetCookie ("LoggedIn", 1, Time() + 3600);
}
Now, whicever page needs a logged in user to be viewed, you just check if the user has the cookie set:
(pseudo-code)
if (CookieIsSet("LoggedIn")) {
// this page can be viewed
}
else {
Redirect ("/notAuthorized");
}
When you log the user out, you can delete the cookie by setting the expiration date in the past:
(pseudo-code)
SetCookie ("LoggedIn", 1, Time() - 3600);
Now, the user cannot view the internal page as the check (CookieIsSet(...)) will fail. Now, functions, methods and all the details depend on the system, but it always works like this:
if credentials are ok, set a cookie
whenever an internal page is visited, check if cookie is set
when logging out, delete the cookie
Hope this helps.
To check if the user typed in the address bar I would simply check the UrlReferer in page load of protected pages, other aspects should be controlled by web security and state management
if (Request.UrlReferrer == null)
Response.Redirect("errorpage.aspx");
It's quite common topic I think, but I can't resolve my problem. In my application build with ASP.NET MVC 3, I'm using form authentication along with output caching:
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" name=".CMS" protection="All" timeout="43200" cookieless="UseCookies"/>
</authentication>
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="Dynamic" duration="3600" location="Client" varyByParam="id" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
My LogOff action looks folowing:
public ActionResult LogOff()
{
_formsService.SignOut();
return RedirectToAction("Index", "Dynamic");
}
this action uses simple SignOut method:
public void SignOut()
{
FormsAuthentication.SignOut();
HttpContext.Current.Session.Abandon();
// clean auth cookie
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, string.Empty);
authCookie.Expires = DateTime.Now.AddDays(-1);
HttpContext.Current.Response.Cookies.Add(authCookie);
// clean session cookie
HttpCookie sessionCookie = new HttpCookie("ASP.NET_SessionId", string.Empty);
sessionCookie.Expires = DateTime.Now.AddDays(-1);
HttpContext.Current.Response.Cookies.Add(sessionCookie);
}
But problem is following:
the page http://localhost/app/dynamic/page is protected. I cannot enter this page untill I login. After login, I have access for browsing such page. After logout, and then entering the page again, unfortunately I can still view its content.
How to prevent access to protected pages after logout, when caching is enabled and I was previously visiting such pages ? What I'm doing wrong ? The cookies should be cleaned in another way ?
Regards
The page is still cached. You need to add the following response header:
cache-control : no-cache
which doesn't actually prevent caching.
The cache-control response header's no-cache directive means that the browser
MUST NOT use the response to satisfy a subsequent request without successful revalidation
with the origin server.
If you really want to prevent caching, specify the no-store directive. That tells the browser that it
MUST NOT store any part of either this response or the request that elicited it. This
directive applies to both non-shared and shared caches. "MUST NOT store" in this context
means that the cache MUST NOT intentionally store the information in non-volatile
storage, and MUST make a best-effort attempt to remove the information from volatile
storage as promptly as possible after forwarding it.
See the HTTP 1.1 specs for details on cache-control and its directives.
Take a look at this post Asp.Net Mvc Can Not Log Out . I believe it should provide the code snippet needed to logout and clear cache.
You can't clear then browser cache from the server.
IMO the only right thing to do is invalidating the cookie on the server side(i.e. even if somebody gets to know the cookie he can't use it anymore), and optionally deleting the cookie on the client.
Just deleting the cookie isn't enough IMO.
Have you veriifed that browser actually makes request to the page http://localhost/app/dynamic/page (i.e. using Fiddler)?
If page is served from browser's cahce you need to set cache control header on that app/dynamic/page so browser is forced to query the page from server.
If page is retrived from server than see if cookies are still there (your cookie code looks ok, but still something could be wrong) OR if server side caching kicks in.
Is it possible to authenticate users across sub-domains when the authentication takes place at a sub-domain instead of the parent domain?
For example:
User logs into site1.parent.com, and then we need to send them to reporting.parent.com.
Can I authenticate them to the reporting site even though the log-in occured at a sub-domain?
So far all of the research I have done has users logging into the parent domain first and then each sub-domain has access to the authentication cookie.
When you authenticate the user, set the authentication cookie's domain to the second-level domain, i.e. parent.com. Each sub-domain will receive the parent domain's cookies on request, so authentication over each is possible since you will have a shared authentication cookie to work with.
Authentication code:
System.Web.HttpCookie authcookie = System.Web.Security.FormsAuthentication.GetAuthCookie(UserName, False);
authcookie.Domain = "parent.com";
HttpResponse.AppendCookie(authcookie);
HttpResponse.Redirect(System.Web.Security.FormsAuthentication.GetRedirectUrl(UserName,
False));
You can set the cookie to be the parent domain at authentication time but you have to explicitly set it, it will default to the full domain that you are on.
Once the auth cookie is correctly set to the parent domain, then all sub-domains should be able to read it.
As a side note, I found that after using jro's method which worked well +1, the FormsAuthenication.SignOut() method didn't work when called from a subdomain other than www/. (I'm guessing because the .Domain property doesn't match) - To get around this I used:
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
HttpCookie myCookie = new HttpCookie(FormsAuthentication.FormsCookieName);
myCookie.Domain = "parent.com";
myCookie.Expires = DateTime.Now.AddDays(-1d);
Response.Cookies.Add(myCookie);
}
In addition to setting a cookie to parent domain also need to make sure that all sites (apps) have same validationKey and decryptionKey () so they all recognise each other's authentication ticket and cookie. Pretty good article here http://www.codeproject.com/KB/aspnet/SingleSignon.aspx
Jro's answer works fine. But make sure to update the webconfig forms authentication setting "domain"
, otherwise forms authentication signout will not work properly. Here is the signout issue I came across. Trick here is to have a '.' as the prefix as the domain is set for the cookie as ".parent.com" (use a cookie inspector).
<authentication mode="Forms">
<forms cookieless="UseCookies" defaultUrl="~/Default" loginUrl="~/user/signin" domain=".parent.com" name="FormAuthentication" path="/"/>
</authentication>
Yes, sure. You may need to roll your own at some stages, but it should be doable.
One idea: as you redirect them across the boundary, give them a one-time pass token and then tell the receiving sub-domain to expect them (this user, from this IP, with this token).
2 things to be done:
MachineKey should be same in all web.config (main domain and sub domain(s))
AuthenticationCookie domain name should be same.
Follow the following article for more depth.