Every HttpRequest to any page in the Asp.net App is somehow authenticated. I check it by printing the debug information in the view:
<p>Request.IsAuthenticated: <%= Request.IsAuthenticated %></p>
Authentification mode for the project is set to "Windows" in Web.config:
<authentication mode="Windows" />
Even simple HTTP request without cookies turns out to be authenticated.
What is the cause of this behavior? Did I miss some setting in Web.config?
The browser uses Windows Integrated Authentication - which means it automatically logs the user in using their windows credentials - without asking.
If you set <authentication mode="Windows" /> it is going to log the user in with their Windows credentials automatically.
For more information, see here.
Related
I'm trying to set up FormsAuthentication in .Net App. It happens to be an SSRS authentication Extension but for the purpose of this question that shouldn't matter.
Logon flow:
Application redirects to logon.aspx when website is opened.
Logon.aspx redirects to auth site to authenticate (IDP).
Auth site redirects back to Logon.aspx.
Logon.aspx sets Forms authentication cookie.
Logon.aspx redirects to home page -> here's where it goes wrong, it redirects back to Logon.aspx.
Web.config:
<authentication mode="Forms">
<forms loginUrl="logon.aspx" name="sqlAuthCookie" protection="All" path="/" timeout="180" enableCrossAppRedirects="true">
</forms>
</authentication>
<authorization>
<deny users="?" />
<allow users="*" />
</authorization>
<identity impersonate="false" />
I tried a two ways to authenticate via forms:
// Doesn't redirect
FormsAuthentication.RedirectFromLoginPage(username, createPersistentCookie: true);
// Set's cookie in response then redirects but comes back
FormsAuthentication.SetAuthCookie(username, createPersistentCookie: true);
string returnUrl = "/ReportServer";
Response.Redirect(returnUrl, false);
I don't know if it's a setting I missed or maybe the auth cookie is lost somewhere; when it redirects back to Logon.aspx the auth cookie is gone.
This is a shot in the dark really - it looks to me like you're missing some logic. I can recommend at least one troubleshooting task:
Ensure that forms authentication is set to use cookies (read more)
Open IIS Manager and navigate to the level where Forms Authentication is enabled
In Features View, double-click Authentication
On the Authentication page, select Forms Authentication
In the Actions pane, click Edit
In the Cookie settings section, select Use cookies from the Mode dropdown
I am using windows authentication impersonation in my MVC application.when i open the application the browser display a prompt for the credentials and validate the domain users.
But now i also want to create user in my application and also want to authenticate that users which is stored in my database.
Is it possible to authenticate Application DB users as well with windows authentication for domain users. i did much R & D on this but didn't found any solution yet. I will appreciate your suggestions. Thanks!
If I understand you correctly, you want to allow both Windows Authentication and Forms Authentication. This is not a common thing to do, but I have done it. Here is how I did it:
You have to use forms authentication as your primary authentication. So build the Forms Authentication as you normally would: you have a login page that, after submitting, validates the credentials from your database. The tricky part is adding Windows Authentication.
To do this, create one action in your authentication controller that uses Windows authentication. For this example, I'll assume your controller is AuthController and we'll call the action WinLogin. That action will look something like this:
[Authorize]
public ActionResult WinLogin() {
var principal = HttpContext.User;
if (principal == null || !principal.Identity.IsAuthenticated) {
//Windows authentication failed
return new HttpUnauthorizedResult();
}
// User is validated, so create the form authentication cookie
FormsAuthentication.SetAuthCookie(principal.Identity.Name, false);
return new EmptyResult();
}
It just checks if the user is validated and, if so, sets the Forms Authentication cookie with their AD username.
For that to use Windows Authentication, you have to update your web.config to tell it to use Windows Authentication for only that one action. You do that with a <location> tag:
<location path="Auth/WinLogin">
<system.webServer>
<security>
<authentication>
<windowsAuthentication enabled="true" />
<anonymousAuthentication enabled="false" />
</authentication>
</security>
</system.webServer>
<system.web>
<authorization>
<allow users="?" />
</authorization>
</system.web>
</location>
By default, IIS won't let you change the authentication method at this place in the config. You need to update the "Feature Delegation" in IIS Manager to allow it.
In IIS Manager, click the server name on the left.
On the right, double-click "Feature Delegation" under the "Management" section.
Change both "Authentication - Anonymous" and "Authentication - Windows" to "Read/Write".
If you use IIS Express for debugging, you have to do something similar for that:
In the project folder, open the file .vs\config\applicationhost.config.
Modify these two lines so they say "Allow":
<section name="anonymousAuthentication" overrideModeDefault="Allow" />
<section name="windowsAuthentication" overrideModeDefault="Allow" />
Next update your login page to hide the username and password fields by default (let's say they are inside a box with an id of loginBox). The idea is that you perform an AJAX request to the WinLogin action, and if that succeeds, then you forward the user on to the main page or whichever page they were trying to go to. If you use jQuery, that will look something like this:
$.get("#Url.Action("WinLogin", "Auth")")
.done(function() {
//success! forward to the page they want
window.location.replace(returnUrl);
}).fail(function() {
//failed - show manual login prompt
$("#loginBox").show();
});
});
As long as your website is already a trusted website (which I assume so if you already have Windows Authentication working now), then the Windows Authentication will happen during that AJAX GET request.
Notice the use of window.location.replace(), which will not add the login page to the browser history, so if the user then hits the back button, they do not come back to the login page. It makes things a little more seamless.
You could also add a loading circle or something to indicate that the user should wait while that GET happens, but you can decide that.
With all this in place, the user experience should be:
They access a page.
They are not authenticated, so they get redirected to the login page.
The login page attempts the Windows Authentication in the background.
If the Windows Authentication succeeds, they are automatically redirected back to the page they wanted.
If Windows Authentication failed, the username and password boxes appear and they can login manually.
We have an application developed in Asp.Net MVC which uses session and Cookies. Which is declared in Web.config as -
<sessionState cookieless="true" regenerateExpiredSessionId="true" timeout="20"/>
Observation is, if we login in Chrome it generates a token in URL as
http://localhost:34343/(S(ypr1jdz2lk5ysiqearcracfj))/Home/Index
If the same url is copied in new Chrome window or in Internet explored, user still logged in and can see the home page.
In this case, we want user to logout and redirect to login page again if he copies the link from one browser to another browser window.
Can you please help with some references on this?
Thank You!
which uses session and Cookies.
You do not use cookies in your apllication because of this setting
cookieless="true"
URL contains sessionID and you can see it (S(ypr1jdz2lk5ysiqearcracfj)). If you send this url via another browser, your server application uses this parameter to find session and identificate user. If you change the settigns to
<sessionState cookieless="false" regenerateExpiredSessionId="true" timeout="20"/>
SessionID will be store in cookies and cannot be shared between different browsers. Of course if you start new window of the same browser, user will be still logged in.
TL/DR: I am experiencing an HTTP-302 redirect loop when trying to share an authentication cookie between an ASP.NET WebForms site and an ASP.NET MVC web site that are served on separate sub-domains.
Details
a.website.com - The existing WebForms site. Works fine.
b.website.com - The new MVC site I'm trying to integrate using the shared cookie.
User reaches b.website.com and is not yet authenticated. They are redirected to a.website.com/Login.aspx.
User enters their username/password.
The user is redirected to the original desired page on b.website.com.
b.website.com receives the authentication cookie in the request, but for some reason fails to see the user as authenticated. It sends them back to a.website.com/Login.aspx.
a.website.com sees the cookie and realizes the user is authenticated, and redirects the user back to b.website.com.
Steps 4 and 5 repeat until the browser breaks the loop.
Setup
The setup follows the MSDN documentation for Configuring Forms Authentication Across Applications precisely: the two sites share machine key settings and Forms authentication settings. Both sites reside on the same server and IIS instance. Both sites force SSL. In the web.config file, the Membership and RoleManager settings are identical.
For a.website.com, which hosts the login page:
<httpCookies httpOnlyCookies="true" requireSSL="true" />
<authentication mode="Forms">
<forms name="COOKIE_NAME"
domain=".website.com"
loginUrl="login.aspx"
defaultUrl="login.aspx"
requireSSL="true"
cookieless="UseCookies"
protection="All"
enableCrossAppRedirects="true"
path="/"/>
</authentication>
<machineKey
validationKey="((validation key))"
decryptionKey="((decryption key))"
validation="SHA1"
decryption="AES" />
b.website.com is of course pretty similar, except the login URL for unauthorized users directs them to the login on site "a".
<httpCookies httpOnlyCookies="true" requireSSL="true" />
<authentication mode="Forms">
<forms name="COOKIE_NAME"
domain=".website.com"
loginUrl="https://a.website.com/login.aspx"
defaultUrl="login.aspx"
requireSSL="true"
cookieless="UseCookies"
protection="All"
enableCrossAppRedirects="true"
path="/"/>
</authentication>
<machineKey validationKey="((validation key))"
decryptionKey="((decryption key))"
validation="SHA1"
decryption="AES" />
Troubleshooting
This setup works as expected on my development machine and our test server (albeit with no domain setting and loginUrl set accordingly) where both web sites are running from the same domain, just different port numbers. For example, in local development, site "a" might run from https://development:44301/ and site "b" might run from https://development:44302/. However, on the production machine--where they are actually on different subdomains--I experience the redirect loop.
Using the browser web developer tools, I can see that the authorization cookie is being sent to b.website.com after the login redirect. Also on b.website.com, if I remove the [Authorize] attribute decorating the controller, the pages load as expected. I'm reasonably sure the issue is limited to how the MVC site is handling the authentication cookie in the limited subdomain scenario.
It sounds like the same issue was reported on SO here and here, but those users had not set enableCrossAppRedirects="true". That setting (which is enabled in my code) seems to be required for subdomain redirects, as I've already tried. This SO article details the setting a bit more and indicates the redirect should be done via SSL, which I am also doing.
What am I missing? How can I better debug the authentication failure that apparently occurs when site "b" receives but does not acknowledge the cookie?
I finally found an answer, unmentioned in the MSDN documentation I referenced above. Many thanks to Steve Smith's 2-year old blog entry.
The older WebForms site targeted a previous release of .NET, so there is an additional compatibility mode string setting on the machineKey config to handle this. If both applications were the same version, this would be unnecessary.
To be absolutely clear for future searchers-- In the web.config of the older a.website.com WebForms site, targeting an earlier .NET release, I had used an ordinary machinekey setting:
<machineKey
validationKey="((validation key))"
decryptionKey="((decryption key))"
validation="SHA1"
decryption="AES" />
In the web.config of the newer MVC site, I had to also specify the compatibility mode to work with the older site's framework:
<machineKey
compatibilityMode="Framework20SP2"
validationKey="((validation key))"
decryptionKey="((decryption key))"
validation="SHA1"
decryption="AES" />
Note that the compatibility mode string is NOT necessarily the targeted framework of the other app. Any targeted version between 2.0SP2 and 4.5 will need a setting of "Framework20SP2". Check the link above to make sure you're picking the right one!
With this in place (along with the matching forms settings in the original MSDN article), everything worked perfectly.
I foresee myself spending another day figuring out why my authentication is broken when we get around to upgrading the older site's targeted framework!
I am implementing C# authorization using jquery cookies for my page. I set/encrypt username and password in the cookie and in my admin page, if I recognize cookie, then the user is authorized. If not, he gets redirected to the login page. The problem is, that cookie is read after page is loaded, so I can manually hit admin page and only in couple seconds it will get redirected. How do I prevent loading admin page for visitors, who have no cookie yet? What is a correct architecture for cookie based authorization?
Note: I am not using ASP.NET roles or User tables. I implemented my own tables for users.
I suspect that you're re-inventing the wheel. You don't have to use the Membership Provider and ASP.Net membership schema in order to take advantage of forms authentication. When the user logs in, simply drop the Auth Ticket (cookie) on them and you're done. You can then simply do the admin check on the admin page.
Some suggestions below...
Edit: I originally posted a means of storing roles in the Auth Ticket via UserData, but I think it's overkill for this situation.
Web.config:
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="30" slidingExpiration="true" />
</authentication>
. . .
<membership>
<providers>
<clear />
</providers>
</membership>
Post login:
When the user submits their username and password, validate them and check to see if they are an admin:
if (UserIsValid(username, pwd)) // some validation call
{
FormsAuthentication.SetAuthCookie(username, true);
}
Admin.aspx:
Finally, a quick hack to restrict access to an admin page. When the page loads, check that the user is / is not an admin:
if (!IsAdmin(User.Identity.Name)) // some admin call
Response.Redirect("Default.aspx");
The problem is, that you use client side code for your security check. If someone would disable JavaScript completely, he would never be redirected. Move the check to your server side code.