How to have multiple logins with ASP.Net? - asp.net

I'm working on a website with an internal and an external section.
The users for both sections are different so they require a different login page. I wanted to configure the authentication differently for both folders, but ASP.Net but it's not allowed.
Example (in my main web.config):
<authentication mode="Forms">
<forms loginUrl="~/Pages/Internal/Main.aspx" defaultUrl="~/Pages/Internal/Main.aspx" cookieless="UseDeviceProfile" name=".ApplicationAuthenticatedUser" path="/" protection="All" slidingExpiration="true" timeout="45"/>
</authentication>
And in the external subfolder, I try to overwrite the settings:
<authentication mode="Forms">
<forms loginUrl="~/Pages/External/Default.aspx" defaultUrl="~/Pages/External/Default.aspx" cookieless="UseDeviceProfile" name=".ApplicationAuthenticatedUser" path="/Pages/External" protection="All" slidingExpiration="true" timeout="45"/>
</authentication>
However this gives me an error.
I tried putting both of them in their subfolders but I get the same error, the authentication configuration section must be set at the application level (I'm guessing that means the root web.config).
A possible solution is to centralize the login page and redirect depending on where the request came from, if it came from an external page, send it to the external login page, otherwise to the internal one.
It would work, but if it's possible I'd like the solution where I can configure this in the web.config.
Thanks

I am confused? Why two user data stores? I understand internal versus external, but if this is the same application, you can assign roles to give more permissions to your internal users. In addition, you can allow your internal users to access the site from home without VPN.
Even so, if you must have two stores, your best bet is duping the application. It can be the exact application, but you put it on one internal server and one external. Then you can authenticate the users at different locations. Note, however, that you still need roles, unless you are kludging up the application.
If you need to authenticate against two stores, you can do it with a custom provider. The ASP.NET login model allows for custom providers and it is very easy to build one:
http://msdn.microsoft.com/en-us/library/f1kyba5e.aspx
http://msdn.microsoft.com/en-us/library/aa479048.aspx
Now, if you must redirect to different pages (you are stuck in this model for some reason?), you can possibly do it by IP address. It is likely your internal network uses a 10 dot or 192 dot IP scheme. If so, those addresses get transfered to internal. The rest to external. This will require you setting up something that does the redirect. I know you can do this on the login page, if not with an HTTP Handler.
This seems like an awful lot of work, however. I still do not see the picture of why you have to accomplish the task in this manner.

If you can run as two different IIS applications then you can have different authentication providers (or different instances of the same provider... possibly using the same database with the application attribute on the provider to distinguish).
But different web apps means no shared state (Application and Session) and duplicating the install. For an intranet/internet this would allow the external deployment to not include components that no internet user can access (and thus improve security by reducing surface area).
Otherwise you might need a custom authentication provider that forwards to one of the built in ones depending on who is logging in.

If your your site is a single web application, you could probably use the ASP.NET Role Provider model for that, having two roles, one for internal and one for external pages (you can configure that pr. folder with the <location> configuration element).
For more information, see http://msdn.microsoft.com/en-us/library/9ab2fxh0.aspx

I have a simple way of handling this that might be of use to somebody. I basically want to be able to use the same code for a guest login and a registered user. I also have a mobile version of the website that I want to send to a different login page when the authentication ticket expires.
Probably not the most elegant solution, but simple enough:
Public Sub btnSubmit_Click(ByVal sender As Object, ByVal e As EventArgs)
If Page.IsValid Then
Dim userLogin As String = ""
userLogin = System.Guid.NewGuid.ToString
FormsAuthentication.RedirectFromLoginPage(userLogin, False)
' place a url param throughout my app, only four pages so no
' big problem there in this case g stands for guest
Response.Redirect("menu.aspx?m=g", False)
End If
End Sub
Then, in Global.asax:
Protected Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As System.EventArgs)
If Not Request.IsAuthenticated And _
(Not Request.RawUrl.ToLower.Contains("guestlogin.aspx")) And _
(Not Request.RawUrl.ToLower.Contains("registeredlogin.aspx")) And _
(Not Request.RawUrl.ToLower.Contains("mobilelogin.aspx")) Then
Response.Redirect("spLogin.aspx?m=" & Request.QueryString("m"))
End If
End Sub
Then, in your login page (the one specified in your Web.config):
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
if request.querystring("m")="g" then
Response.Redirect("guestlogin.aspx?m=g")
elseif request.querystring("m")="r" then
Response.Redirect("registeredlogin.aspx?m=r")
elseif request.querystring("m")="m" then
Response.Redirect("mobilelogin.aspx?m=m")
end if
End If
End Sib

Related

ASP.NET Forms Authentication Across Applications Issue

This has been the bane of my existence for the better part of a week.
I have four existing webforms applications that utilize forms authentication. The URL for each is mydomain/app1/, mydomain/app2/, etc. I have been tasked with creating a new application that will function as a single sign-on application with the URL mydomain/ssoapp. Once a user logs in, it basically compiles everything from the pre-existing apps that the user has access to, so our users don't have to go out and log into each of them separately. But the old applications need to function as they currently do.
The important part of my web.config is as follows:
<authentication mode="Forms" >
<forms loginUrl="frmLogin.aspx?Type=login" name="sqlAuthCookie" protection="All" path="/" domain="mydomain"
timeout="60" cookieless="UseCookies" enableCrossAppRedirects="true" />
</authentication>
<machineKey validation="SHA1" decryption="AES" decryptionKey="mykey" validationKey="myvalkey"/>
Simply adding this to the web.config for all of the applications worked like a charm....for three of them.
In the SSO application I'm creating a formsauthenticationticket, cookie, and adding that to the response with the following code. Each of the four pre-existing applications uses this same code as well:
Dim lTicket As New FormsAuthenticationTicket( _
1, _
pstrUserId.ToString, _
System.DateTime.Now, _
System.DateTime.Now.AddMinutes(60), _
True, _
pstrUserId.ToString, _
FormsAuthentication.FormsCookiePath)
' Encrypt the ticket.
Dim lencTicket As String = FormsAuthentication.Encrypt(lTicket)
' Create the cookie and add to response
Dim cookie As HttpCookie = New HttpCookie(FormsAuthentication.FormsCookieName, lencTicket)
cookie.Domain = ".mydomain.gov"
pobjResponse.SetCookie(cookie)
pobjResponse.Cookies.Add(cookie)
'Cleanup
lencTicket = Nothing
lTicket = Nothing
In chrome debugger for the SSO application, I log in and the cookie is created with the correct information.
I can click on my menu list, which uses a response.redirect to go out to the other applications. For the 3 working applications, I bypass the login screen, go directly to the form I need, and the cookie is unchanged
For the problem child application, I can still see the cookie however I am redirected back to the login screen.
If I login from this point, a new cookie is created, with the same name as the preexisting one, however the domain has the "www" prefix on it
Other useful information (maybe):
I've ensured that all machinekey, decryption key, validation method, etc match across applications
My domain is in the format of sub1.mid1.gov . I've tried every combination of the format for this in the cookie assignment and web.config. Both with and without the preceding dot.
I've removed httpRuntime from the web.config as some others had mentioned this causes issues.
There are no errors in the IIS logs
All applications are running under the same apppool currently
Currently I'm contemplating taking some vacation time so I don't feel bad about crying in the corner on my employers dime. I'm sure it's something ridiculously simple, but I appreciate any help in the matter. Thanks!

ASP.net Custom membership on top of quality center authorization

I am relatively new to authorization/memberships in asp.net, so pls excuse if I ask anything silly. I have been looking at lot of examples to implement a custom membership provider in .net (in stackoverflow, codeproject, devX, and www.asp.net) and coded based on that but somehow couldn't get it working.
My requirement - our organization heavily uses HP's Quality center(QC), I am developing an asp.net application, its login page will use QC'a API for authenticating a user. I also have a SQL database in which I'll store the QC users who have registered to my application (just store QC user id's in DB, not password, like I said, password authentication is done using QC API). There will be a user-roles table in my DB to define the roles for registered users.
Why use 'membership' instead of some simple 'forms authentication' - because maybe in future I want to decouple QC authentication.
So, with this I started with first step - developing custom membership class(named AutoCenterMembershipProvider) and login page. I only need validateuser method. following is the approach I took to start with:
1. Ask user for QC user id/password, user clicks 'Authenticate' button
2. login page's code behind-'Authenticate' button's onClick method- checks if user is found in SQL database and if found, then uses QC API to authenticate user id-password
3. Second set of controls on Login page is enabled - ask user to select which QC Domain and Project user wants to login. Options for Domain and Project dropdown lists are also obtained using QC API after authenticating user. User selects those and clicks Login button
4. On Login button's click - call Membership.ValidateUser(objQCSession.UserName, objQCSession.Password). Since user is already validated using QC api, for simplicity I just return 'true' from my custom implementation of Membership.ValidateUser. Then I call - FormsAuthentication.RedirectFromLoginPage(obj_ACUser.QCSession.UserName, True) to direct user to apps default page provieded in web.config's - app_FAs.aspx.
The issue is - after user is redirected to app_FAs.aspx page, it directs user back to login page. I am trying to find out the mistake or missing piece.
Web.config looks like below:
<authentication mode="Forms">
<forms loginUrl="~\Pages\Login.aspx" defaultUrl="App_FAs.aspx"></forms>
</authentication>
<authorization>
<deny users="?"/>
</authorization>
<membership defaultProvider="AutoCenterMembershipProvider">
<providers>
<clear/>
<add name="AutoCenterMembershipProvider"
type="CustomMembership.Models.AutoCenterMembershipProvider"
enablePasswordRetrieval="false" enablePasswordReset="false"
requiresQuestionAndAnswer="false" requiresUniqueEmail="false"
maxInvalidPasswordAttempts="100" minRequiredPasswordLength="100"
minRequiredNonalphanumericCharacters="0"
passwordAttemptWindow="100" applicationName="/" />
</providers>
</membership>
and customMembership class is like:
Public Class AutoCenterMembershipProvider
Inherits System.Web.Security.MembershipProvider
Public Overrides Function ValidateUser(ByVal username As String, ByVal password As String) As Boolean
Return True
End Function
rest all members are 'Not implemented'
any help, pointers to missing piece, mistake is greatly appreciated, thanks in advance
Authenticate button click code
Private Sub btn_Authenticate_Click(ByVal sender as Object, ByVal e As System.Web.UI.ImageClickEventArgs) Handles btn_Authenticate.click
objQCSession = Session("QCUserSession")
If Membership.ValidateUser(objQCSession.UserName, objQCSession.Password) then
FormaAuthentication.RedirectFromLoginPage(objQCSession.UserName, True)
End if
End Sub
Currenlty, 2nd step - btn_Authenticate_Click method 1 - is just to assign FormAuthenticationTicket to cookie, and redirecting user to app_FAs.aspx page. It doesn't really need Custom Membership Provider's features.
If I understand your problem correctly, I would change the logic like this.
1) After validating user for QC, create FormAuthenticationTicket like this in the same method.
FormsAuthentication.SetAuthCookie("UserName", true|false);
2) btn_Authenticate_Click (does something and) redirects user to app_FAs.aspx
You do not even need Custom Membership Provider. If you want to use Custom Membership Provider, you can implement in 1st step (Not in 2nd step).

IIS7 Block request to asp.net app using external IP

I have a asp.net app (IIS7), and I want block access to it using external server IP. I only want to allow access using my domain.
For example, my domain is domain.com and IP 161.0.0.1 and I want to block the access to http://161.0.0.1/webapp/
I prefer do it using web.config
Thx in advance,
In IIS you configure exactly what IP / DNS name combination you want the site to respond to. You can easily force it to only respond on a particular IP.
For IIS 7:
Open the Internet Information Services (IIS) Manager
Expand Sites and right click on your website.
Click on Edit Bindings.
Edit the existing entry and set the IP address to 161.0.0.1. Also set the domain name to domain.com.
Click OK, the Click Close.
Now your site wil only respond to that particular domain name and won't respond via IP address only.
If your site uses an SSL certificate then see the following question which talks about how to configure IIS to force the hostname to be used:
https://serverfault.com/questions/96810/iis7-cant-set-host-name-on-site-with-ssl-cert-and-port-443
which links to:
http://www.sslshopper.com/article-ssl-host-headers-in-iis-7.html
This link is even better for doing it entirely through the UI: http://blog.armgasys.com/?p=80
OK so if you want the Site to be accessible via DNS name but not via IP, the only way to distinguish that is to examine the requested host name in the header. There are two ways to do that I know of:
1) Configure Bindings dialog in IIS Manager. This is the easiest to set up but doesn't work for HTTPS. Just put www.domain.com into the hostname field and requests to the IP will be rejected. For HTTPS if your security certificate is for a specific hostname, the user will get a security warning if they try to connect via IP, but typically they can override the warning (depending on browser settings).
Edit: Chris Lively has linked to a way to make this method work for HTTPS bindings as well, see his answer for more information.
2) Alternately you can examine the header in code. Here is an example of an IHttpModule which accomplishes what you want. It is also a drop-in solution that is configured in web.config.
Code:
Public Class HostNameCheck
Implements IHttpModule
Public Sub Dispose() Implements System.Web.IHttpModule.Dispose
End Sub
Public Sub Init(context As System.Web.HttpApplication) Implements System.Web.IHttpModule.Init
AddHandler context.BeginRequest, AddressOf context_BeginRequest
End Sub
Private Sub context_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
Dim objApp As HttpApplication = DirectCast(sender, HttpApplication)
If objApp.Request.Url.Host <> ConfigurationManager.AppSettings("AcceptedHostName") Then
objApp.Response.Clear()
objApp.Response.StatusCode = 403
objApp.Response.SubStatusCode = 6
objApp.Response.Flush()
End If
End Sub
End Class
Web.config:
<configuration>
<appSettings>
<add key="AcceptedHostName" value="www.domain.com"/>
</appSettings>
<system.webServer>
<modules>
<add name="HostNameCheck" type="HostNameCheck"/>
</modules>
</system.webServer>
</configuration>

How to use HTTPS in an ASP.Net Application

I want to use HTTPS in my ASP.NET web application, but only for the Login.aspx page.
How can this be accomplished?
First get or create a certificate
Get the SecureWebPageModule module from http://www.codeproject.com/Articles/7206/Switching-Between-HTTP-and-HTTPS-Automatically-Ver. Instructions for setup can be found in the article.
Add secureWebPages tag to web.config
<configuration>
...
<secureWebPages enabled="true">
...
</secureWebPages>
...
<system.web>
...
</system.web>
</configuration>
Add files and directories to be use for https protocol:
<secureWebPages enabled="true">
<file path="Login.aspx" />
<file path="Admin/Calendar.aspx" ignore="True" />
<file path="Members/Users.aspx" />
<directory path="Admin" />
<directory path="Members/Secure" />
</secureWebPages>
Hope this helps!
You can publish your own certificate or you can purchase one. The caveat is that purchasing one, depending on the company, means that it's already stored in the certificate store for most browsers. Your self published one will not be and your users will have to take the extra step of installing your cert.
You don't say what version of IIS you're using, but here are some detailed instructions for IIS 6
You can purchase relatively cheap certs or you can go with the big boys (verisign) and get an extended validation certificate which turns your address bar in IE, green. It's also a somewhat rigorous validation process and takes time.
If you know all of the users that will be hitting your website, there's no problem with installing your own. However, for an open website with anonymous users (that you don't know), it's probably best to purchase one that is already in most major browsers, certificate stores.
You can enable SSL via IIS and require it for only your login.aspx page and not for the rest.
After you get SSL setup/installed, you want to do some sort of redirect on the login page to https://. Then whatever page the user is sent to after validation, it can just be http://.
Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
If Request.IsSecureConnection = False And _
Not Request.Url.Host.Contains("localhost") Then
Response.Redirect(Request.Url.AbsoluteUri.Replace("http://", "https://"))
End If
End Sub
This may be easier to implement on a master page or just all the pages you require https. By checking for "localhost" you will avoid getting an error in your testing environment (Unless your test server has another name than check for that: "mytestservername").
disclaimer - I was involved in the development of this project
I would recommend using http://nuget.org/packages/SecurePages/ It gives you the ability to secure specific pages or use Regex to define matches. It will also force all pages not matching the Regex or directly specified back to HTTP.
You can install it via NuGet: Install-Package SecurePages
Docs are here: https://github.com/webadvanced/Secure-Page-manager-for-asp.net#secure-pages
Simple Usage:
SecurePagesConfiguration.Urls.AddUrl("/cart");
or
SecurePagesConfiguration.Urls.AddRegex(#"(.*)account", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);
You can enable HTTPS in your IIS config, but it won't be "secure" unless you acquire an SSL Certificate and plug it into IIS. Make sure you have port 443 open.

In my codebehind class, how do I retrieve the authorized roles?

I have the following in my web.config:
<location path="RestrictedPage.aspx">
<system.web>
<authorization>
<allow roles="Group1Admin, Group3Admin, Group7Admin"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
Within RestrictedPage.aspx.cs, how do I retrieve the allowed roles collection that contains Group1Admin, Group3Admin, and Group7Admin?
Here's why I ask:
The web.config is handling the authorization to the page. That works fine. But I'm going to have a couple of these pages (say RestrictedPage.aspx, RestrictedPage2.aspx, RestrictedPage3.aspx). Each of these pages is going to have my custom webcontrol on it. And each of these pages will have different allowed roles. My webcontrol has a dropdown list. The choices within the dropdown depend on the intersection of the user's roles and the page's allowed roles.
As mentioned below, searching the web.config with XPath would probably work. I was just hoping for something more framework-y. Kind of like SiteMap. When I put roles in my web.sitemap, I can grab them using SiteMap.CurrentNode.Roles (my website is using Windows authentication, so I can't use web.sitemap for security trimming and I'd rather maintain roles in only one file).
// set the configuration path to your config file
string configPath = "??";
Configuration config = WebConfigurationManager.OpenWebConfiguration(configPath);
// Get the object related to the <identity> section.
AuthorizationSection section = (AuthorizationSection)config.GetSection("system.web/authorization");
from the section object get the AuthorizationRuleCollection object where you can then extract the Roles.
Note: You'll probably need to modify the path to the section a bit since you start with "location path="RestrictedPage.aspx"", I didn't try that scenario.
if {User.IsInRole("Group1Admin"){//do stuff}
Is that what your asking?
I'm not sure for certain, but I would have thought that this is checked before your page is even processed, so if a user is not in a role they would never reach your page. Which ultimately would make the visibility of this redundant in the page.
I'm convinced that there is a better way to read this information, but here is a way that you can read the allow values from a web.config file.
XmlDocument webConfigReader = new XmlDocument();
webConfigReader.Load(Server.MapPath("web.config"));
XmlNodeList root = webConfigReader.SelectNodes("//location[#path="RestrictedPage.aspx"]//allow//#roles");
foreach (XmlNode node in root)
{
Response.Write(node.Value);
}
Of course, the ASP.NET role provider will handle this for you, so reading these values is only really relevant if you plan to do something with them in the code-behind beside authorizing users, which you may be doing.
Hope this helps--you may have to split your result using the , character.
What typically happens is this...
When the user hits your page, if authentication/authorization is active, the Application_Authentication event is raised. Unless you are using Windows Authentication against something like Active Directory, the IPrincipal and Identity objects will not be available to you, so you can't access the User.IsInRole() method. However, you CAN do this by adding the following code into your Global.asax file:
Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As EventArgs)
Dim formsAuthTicket As FormsAuthenticationTicket
Dim httpCook As HttpCookie
Dim objGenericIdentity As GenericIdentity
Dim objMyAppPrincipal As CustomPrincipal
Dim strRoles As String()
Log.Info("Starting Application AuthenticateRequest Method...")
httpCook = Context.Request.Cookies.Get("authCookieEAF")
formsAuthTicket = FormsAuthentication.Decrypt(httpCook.Value)
objGenericIdentity = New GenericIdentity(formsAuthTicket.Name)
strRoles = formsAuthTicket.UserData.Split("|"c)
objMyAppPrincipal = New CustomPrincipal(objGenericIdentity, strRoles)
HttpContext.Current.User = objMyAppPrincipal
Log.Info("Application AuthenticateRequest Method Complete.")
End Sub
This will put a cookie into the browser session with the proper user and role credentials you can access in the web app.
Ideally, your user is only going to be in one role in an application, so I believe that is why you have the role check method available to you. It would be easy enough to write a helper method for you that would iterate through the list of roles in the application and test to see what role they are in.

Resources