FormsAuthentication.RedirectFromLoginPage to a custom page - asp.net

Hi i'm using the FormsAuthentication.RedirectFromLoginPage for the user login and for redirect to default.aspx page.
I want that if a user called admin do the login is redirected to the page admin.aspx
Is it possible?

Try this, I think it's the closest you will get with a simple solution:
FormsAuthentication.SetAuthCookie(username, true);
Response.Redirect("mypage.aspx");

Authenticating Users
Assuming you have gone through my previous article mentioned above, you have a login page. Now when user clicks Login button Authenticate method fires, lets see code for that method.
protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
{
string userName = Login1.UserName;
string password = Login1.Password;
bool rememberUserName = Login1.RememberMeSet;
// for this demo purpose, I am storing user details into xml file
string dataPath = Server.MapPath("~/App_Data/UserInformation.xml");
DataSet dSet = new DataSet();
dSet.ReadXml(dataPath);
DataRow[] rows = dSet.Tables[0].Select(" UserName = '" + userName + "' AND Password = '" + password + "'");
// record validated
if (rows.Length > 0)
{
// get the role now
string roles = rows[0]["Roles"].ToString();
// Create forms authentication ticket
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // Ticket version
userName, // Username to be associated with this ticket
DateTime.Now, // Date/time ticket was issued
DateTime.Now.AddMinutes(50), // Date and time the cookie will expire
rememberUserName, // if user has chcked rememebr me then create persistent cookie
roles, // store the user data, in this case roles of the user
FormsAuthentication.FormsCookiePath); // Cookie path specified in the web.config file in <Forms> tag if any.
// To give more security it is suggested to hash it
string hashCookies = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hashCookies); // Hashed ticket
// Add the cookie to the response, user browser
Response.Cookies.Add(cookie); // Get the requested page from the url
string returnUrl = Request.QueryString["ReturnUrl"];
// check if it exists, if not then redirect to default page
if (returnUrl == null) returnUrl = "~/Default.aspx";
Response.Redirect(returnUrl);
}
else // wrong username and password
{
// do nothing, Login control will automatically show the failure message
// if you are not using Login control, show the failure message explicitely
}
}
you can check it by placing hard core role name or by fetching user roll from database. i have modified this for my entity framework.
TestEntities entities = new TestEntities();
var user = (from s in entities.UserTables
where s.UserName == loginControl.UserName
&& s.Password == loginControl.Password
select s).SingleOrDefault();
and placed the user role as:
user.Role
Along this you have do some changes in the Global.asax file
Till now we have set the Forms Authentication ticket with required details even the user roles into the cookie, now how to retrive that information on every request and find that a request is coming from which role type? To do that we need to use Application_AuthenticateRequest event of the Global.asx file. See the code below.
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
// look if any security information exists for this request
if (HttpContext.Current.User != null)
{
// see if this user is authenticated, any authenticated cookie (ticket) exists for this user
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
// see if the authentication is done using FormsAuthentication
if (HttpContext.Current.User.Identity is FormsIdentity)
{
// Get the roles stored for this request from the ticket
// get the identity of the user
FormsIdentity identity = (FormsIdentity)HttpContext.Current.User.Identity;
// get the forms authetication ticket of the user
FormsAuthenticationTicket ticket = identity.Ticket;
// get the roles stored as UserData into the ticket
string[] roles = ticket.UserData.Split(',');
// create generic principal and assign it to the current request
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(identity, roles);
}
}
}
}
In this even, after checking if user exists, he/she is authenticated and the identy type of th user is FormsIdentity, I am getting the current Identity of the user and getting the ticket I have set at the time of Authentiacting. Once I have the authenticated ticket, I just got the UserData from the ticket and split it to get roles (remember, we had stored the roles as comma separated values). Now, we have current users roles so we can pass the roles of the current user into the GenericPrincipal object along with the current identity and assign this to the curent user object. This will enable us to use the IsInRole method to check if a particular user belongs to a particular role or not.
How to Check if user has a particular role?
To check if a user belong to a particulr role, use below code. This code will return true if the current record is coming from the user who is authenticated and has role as admin.
HttpContext.Current.User.IsInRole( "admin" )
How to check if user is authenticated?
To check if the user is authenticated or not, use below code.
HttpContext.Current.User.Identity.IsAuthenticated
To get UserName of the Authenticated User
HttpContext.Current.User.Identity.Name
Remember on thing .. this code require some webconfig settings in the forms tag as:
Add following Authentication setting into your web.config file under .
<authentication mode="Forms">
<forms defaultUrl="default.aspx" loginUrl="~/login.aspx" slidingExpiration="true" timeout="20" ></forms>
</authentication>
For every user if you want to secure a particular folder, you can place setting for them either in parent web.config file (root folder) or web.config file of that folder.
Specify Role settings for the folder in root web.config file (in this case for Admin)
<location path="Admin">
<system.web>
<authorization>
<allow roles="admin"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
Write this code outside but under tag in the root's web.config file. Here, I am specifying that if the path contains the name of folder Admin then only user with "admin" roles are allowed and all other users are denied.
Specify Role settings for the folder in folder specific web.config file (in this case for User)
<system.web>
<authorization>
<allow roles="User"/>
<deny users="*"/>
</authorization>
</system.web>
Write this code into web.config file user folder. You can specify the setting for the user in root's web.config file too, the way I have done for the Admin above. This is just another way of specifying the settings. This settings should be placed under tag.
Specify setting for Authenticated user
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
Write this code into web.config file of the Secure folder. This is specifying that all anonymous users are denied for this folder and only Authenticated users are allowed irrespective of their roles.
hope this will give you little idea to solve your problem. it is working fine for me.
hope you will also solve your problem.

If you are using the ASP.NET MembershipProvider login control, you can write your logic in the LoggedIn event
<asp:Login id="Login1" runat="server" OnLoggedIn="OnLoggedIn"></asp:Login>
protecetd void OnLoggedIn(object sender, EventArgs e)
{
if(Roles.IsUserInRole(User.Identity.Name, "Administrators"))
{
//Redirect to admin page
Response.Redirect("~/Admin.aspx");
}
}
Don't forget to put some protection on the admin.aspx page aswell, incase someone types in the url directly

The default behavior is to redirect to the originally requested resource, so if a user tried to access 'admin.aspx' and isn't authenticated, the user is sent to the login page. After successfully authenticating, the user is sent to the originally requested url (admin.aspx).
user -> "admin.aspx" -> noauth -> login -> "admin.aspx"
So instead of manually trying to send users somewhere, is using this default behavior not going to work for you? The default behavior is actually "robust" (it can be "admin2.aspx", "admin3.aspx" and so on... you can have any number of "protected resources" and the built in process handles all of it....)

Related

Single sign on inside asp.net mvc web application

I have two domains ,on our internal network:-
DomainA
DomainB
Both domains can communicate with each other’s, but they do NOT trust each other.
So currently I have deployed my asp.net MVC web application inside domainA on IIS, but I need users who are on DomainB Active directory to be able to login to the asp.net mvc using their domainB AD credentials . I am open to both windows authentication and form authentication inside my asp.net mvc .
But the only requirement that came from the client is that they want users who access the asp.net mvc intranet application from their machine on domainB, to be able to lo-gin to the system without having a login page; they can either:-
enter the username and password through the browser pop-up,
or to sign in automatically.
So can anyone advice what are the approaches I can follow, to achieve this?
Thanks
EDIT
I have read the following article http://msdn.microsoft.com/en-us/library/ff650307.aspx, about how i can authenticate asp.net mvc users from multiple domains, so inside my asp.net mvc i did the following :-
I added the following to my web.config:-
<system.web>
<membership>
<providers>
<add name="TestDomain1ADMembershipProvider" type="System.Web.Security.ActiveDirectoryMembershipProvider" connectionStringName="TestDomain1ConnectionString" connectionUsername="ad-domainA.intra\it360ad.user" connectionPassword="$$$$$" />
</providers>
</membership>
&
<add name="TestDomain1ConnectionString" connectionString="LDAP://ad-domainA.intra/CN=Users,DC=ad-domainA,DC=intra" />
and i added the following Account.controller:-
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
MembershipProvider domainProvider;
domainProvider = Membership.Providers["TestDomain1ADMembershipProvider"];
// Validate the user with the membership system.
if (domainProvider.ValidateUser(model.UserName, model.Password))
{
// If there is a RequestUrl query string attribute, the user has
// been redirected to the login page by forms authentication after
// requesting another page while not authenticated.
if (Request.QueryString["ReturnUrl"] != null)
{
// RedirectFromLoginPage sets the authorization cookie and then
// redirects to the page the user originally requested.
// Set second parameter to false so cookie is not persistent
// across sessions.
FormsAuthentication.RedirectFromLoginPage(
model.UserName, false);
}
else
{
// If there is no RequestUrl query string attribute, just set
// the authentication cookie. Provide navigation on the login page
// to pages that require authentication, or user can use browser
// to navigate to protected pages.
// Set second parameter to false so cookie is not persistent
// across sessions.
FormsAuthentication.SetAuthCookie(model.UserName, false);
}
}
else
{
// Response.Write("Invalid UserID and Password");
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
////////////
//if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
//{
return RedirectToLocal(returnUrl);
//}
// If we got this far, something failed, redisplay form
}
But currently when the user try to login , he will get always the following message
•The user name or password provided is incorrect.
so can you advice if my code is correct ?
You have to deploy a SSO solution like Active Directory Federation Services on a server that is joined to DomainB.
Then implement authentication in your application (for example, WS-Federation Passive Requestor) that targets that SSO solution and standard Windows Authentication that targets DomainA.

Form Authentication issue

Trying to use form authentication to only allow access to a page once they have logged in via the login page. When I login and attempt the redirect it just redirects me back to the login page.
Web Login Control
protected void WebGenLogin_Authenticate(object sender, AuthenticateEventArgs e)
{
//Verify user against active directory
if (new AD().validate(WebGenLogin.UserName, WebGenLogin.Password))
{
Session["UserAuthentication"] = WebGenLogin.UserName;
Session.Timeout = 30;
FormsAuthentication.RedirectFromLoginPage(WebGenLogin.UserName, WebGenLogin.RememberMeSet);
Response.Redirect("~/WebGen/Gen/Create.aspx");
}
else
{
Session["UserAuthentication"] = "";
Response.Redirect("http://thekickback.com/rickroll/rickroll.php");
}
}
Create.aspx Web.config
<authentication mode="Forms">
<forms defaultUrl="~/WebGen/Gen/Create.aspx" loginUrl="../Login.aspx" slidingExpiration="true" timeout="30" />
</authentication>
Can you try this:
if (new AD().validate(WebGenLogin.UserName, WebGenLogin.Password))
{
Session["UserAuthentication"] = WebGenLogin.UserName;
Session.Timeout = 30;
FormsAuthentication.SetAuthCookie(WebGenLogin.UserName, false);
FormsAuthentication.RedirectFromLoginPage(WebGenLogin.UserName, WebGenLogin.RememberMeSet);
***SNIP***
I don't know what type of object AD() calls into, but you may not be using the default ASP.NET membership functionality. As I recall, the ValidateUser method on the membership class has the side-effect of actually logging the user in if it returns true.
After authenticating the user, you may need to set HttpContext.User to a new IPrincipal representing the user, and then call FormsAuthentication.SetAuthCookie() before redirecting them.
Ok I figured it out. It had nothing to do with my code. I did however remove storing the username in the session.
What I have to do was change the root site on IIS to an application.
Authentication mode line was placed in the root with Login.aspx
Create.aspx was in another folder. I removed the authentication mode from it's Web.config and just put in the deny section and all is working correctly.
The code actually worked. Found it to be an issue with IIS. Needed to turn the entire folder structure into an application rather than other parts of it.

Set Admin Home to Forbidden without Correct Login Credentials

I am making a website and i want Admin Home/User Home Page to Be Forbidden without Verifying with Correct Login Credentials. But How i Can.
protected void btn_Login_Click(object sender, EventArgs e)
{
if (CheckFields())
{
user.Employee_ID = txtEID.Text;
user.Employee_Password = txtEPassword.Text;
user.Role = ddlRole.SelectedItem.ToString();
if (uc.Login(user)==true && user.Role=="Admin")
{
Session["userID"] = user.Employee_ID;
Response.Redirect("~/Admin/Admin.aspx");
}
else if (uc.Login(user) == true && user.Role == "Team Admin")
{
Session["userID"] = user.Employee_ID;
Response.Redirect("TeamAdmin.aspx");
}
else if (uc.Login(user) == true && user.Role == "Team Admin")
{
Session["userID"] = user.Employee_ID;
Response.Redirect("TeamMember.aspx");
}
}
}
upto here it is working fine. but when i set the path directly to admin page (http://mysite.com/Admin/Admin.aspx) of the site it is showing all the contents of the admin page. i need admin page to b forbidden without verifying correct login credentials
Create a web.config file in the Admin directory with this:
<configuration>
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</configuration>
If you're using some form of asp.net authentication it will deny any user that is not authenticated.
You need to implement authorization (http://msdn.microsoft.com/en-us/library/wce3kxhd.aspx).
If your admin page is in a separate folder that only administrators should have access to, then you can put a web.config file in that folder with an authorization section as described in the link above. You could set this authorization section to restrict access to only users who are members of the Admin role. In order for this to work properly, you must be using ASP.Net security and authorization.
Alternatively, in the Page_Load() method of the Admin.aspx page, you could put code to check that the user is a member of the Admins role. If they are not, redirect them to another page.
I prefer configuring page level authorization in the .config files because it keeps it in one place and allow it to be easily updated in the future.

Proper creation of a cross-domain forms authentication cookie

I'm just creating a simple test between two server. Basically if a user has already authenticated I want to be able to pass them between applications. I changed the keys to hide them
I have three questions:
What is the proper way to validate the cookie across domain application. For example, when the user lands at successpage.aspx what should I be checking for?
Is the below code valid for creating a cross domain authentication cookie?
Do I have my web.config setup properly?
My code:
if (authenticated == true)
{
//FormsAuthentication.SetAuthCookie(userName, false);
bool IsPersistent = true;
DateTime expirationDate = new DateTime();
if (IsPersistent)
expirationDate = DateTime.Now.AddYears(1);
else
expirationDate = DateTime.Now.AddMinutes(300);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1,
userAuthName,
DateTime.Now,
expirationDate,
IsPersistent,
userAuthName,
FormsAuthentication.FormsCookiePath);
string eth = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, eth);
if (IsPersistent)
cookie.Expires = ticket.Expiration;
cookie.Domain = ".myDomain.com";
Response.SetCookie(cookie);
Response.Cookies.Add(cookie);
Response.Redirect("successpage.aspx");
}
My config:
<authentication mode="Forms">
<forms loginUrl="~/Default.aspx" timeout="2880" name=".AUTHCOOKIE" domain="myDomain.com" cookieless="UseCookies" enableCrossAppRedirects="true"/>
</authentication>
<customErrors mode="Off" defaultRedirect="failure.aspx" />
<machineKey decryptionKey="#" validationKey="*" validation="SHA1" decryption="AES"/>
What is the proper way to validate the cookie across domain application.
For example, when the user lands at successpage.aspx what should I be checking for ?
There shouldn't be anything to check. Forms authentication mechanism will retrieve the ticket from the cookie, check if it is valid. If not present, or invalid, user will redirected to ~/Default.aspx .
This will work provided your cookie matches the configuration of your web.config
Is the below code valid for creating a cross domain authentication cookie ?
I think you shouldn't try to override the settings of your web.config by manually handling the cookie. I think there are better ways for handling cookie persistence (see below for web.config) and you are just implementing a part of the Forms authentication API (loosing web.config for SSL for example )
here, your manual cookie is not HttpOnly : you could for example be subject to cookie theft through XSS
FormsAuthentication has its own way of handling the cookie (see the TimeOut attribute description in http://msdn.microsoft.com/en-us/library/1d3t3c61%28v=vs.80%29.aspx) Your cookie persistence mechanism will be overwritten by this automatic behavior
Your code should just be :
if (authenticated)
{
bool isPersistent = whateverIwant;
FormsAuthentication.SetAuthCookie(userName, isPersistent );
Response.Redirect("successpage.aspx");
}
Do I have my web.config setup properly?
It should be ok for the domain attribute, as long as you want to share authentication among direct subdomains of mydomain.com (it won't work for x.y.mydomain.com), and mydomain.com is not in the public suffix list ( http://publicsuffix.org/list/ )
I would change the timeout and slidingExpiration attributes to :
<forms loginUrl="~/Default.aspx" timeout="525600" slidingExpiration="false" name=".AUTHCOOKIE" domain="myDomain.com" cookieless="UseCookies" enableCrossAppRedirects="true"/>
I guess it is a good way to handle the choice between one year persistent cookies and session cookies. See https://stackoverflow.com/a/3748723/1236044 for more info

asp.net: check whether session is valid

how to check whether users is authenticated and session is valid on pages after say 30 mins.
Assuming you are either hooking into the standard ASP.NET membership providers or using Basic/Digest authentication in IIS then you can easily tell if a user is authenticated using:
if (Request.IsAuthenticated)
{
// User is authenticated, allow them to do things
}
If their authentication token has expired (defaults to 20 minutes with Forms Auth, Windows auth should re-authenticate correctly with each request), then the next time you check that, IsAuthenticated will return false.
You could store a token in the users session that maps back to their user account (either a hash of their user name, or their user id or similar), and then check the two on the request - if they don't match, then the session has become invalid:
// Get the current user, and store their ID in session
MembershipUser user = Membership.GetUser();
Session["UserId"] = user.ProviderUserKey;
To check this:
if (null != Session["UserId"]) {
MembershipUser user = Membership.GetUser();
if (Session["UserId"] == user.ProviderUserKey) {
// User and session are valid
}
}
But to be honest, it depends on what you are trying to do.
If you want to restrict access to certain areas of your website if the user isn't logged in, then there are mechanisms in the configuration that allow for that:
In your web.config you can add lines like the following:
<location path="SecureDirectory">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</location>
This will deny all anonymous users access to the directory /SecureDirectory/ and all content below it, and direct them instead to your configured login page - for more information on the Authorization element, see "How to: Configure Directories Using Location Settings".
You have to make the session expire after certain time.
So, there is a section in your web.config or you have to add the section in <system.web />
Put this section inside:
<sessionState mode="InProc" stateConnectionString="tcpip=127.0.0.1:42424"
stateNetworkTimeout="10" cookieless="false" timeout="30" />
If you notice we are using InProc mode and timeout to be 30
Now its all up to you.
Add a key in Session object when you can find in any WebForm page.
public void btnLogin(object sender, EventArgs e) {
if (validUser) {
Session["authenticated"] = true;
}
}
and check Session["authenticated"] when required.
Session object will be expired in 30 minutes of session instantiation.
Hope this help. Please feel free to leave me a comment if you face trouble.
In the start of a session, you can store some key value in session state via the Global.asax:
void Session_Start(object sender, EventArgs e)
{
Session["userId"] = userId; // obtained from a data source or some other unique value, etc.
}
Whenever a user makes a page request or postback, on page load of any or all your pages, check if session value is null:
protected void Page_Load(object sender, EventArgs e)
{
if(Session["userId"] == null)
{
Response.Redirect("logout.aspx");
}
// do other stuff
}
If it is, then the session has expired and you can redirect then to logout page or whatever. The timeout interval is defined in your web.config file.

Resources