Roles for GenericPrincipal are being lost - c#

I am attempting to set up roles on my dynamic data website, but am encountering an issue.
Between the time I set up by Generic Principal, and when the default page is hit the roles of my principal are lost. The principal is still listed as the context.user, for when I debug my session and inspect the context.user object the user I set up is the user I set up; but all roles related to that user seem to be lost.
This is how I am going about it.
Login page
public partial class Login : System.Web.UI.Page
{
string[] masterServiceResult;
protected void btnSubmit_Click(object sender, EventArgs e)
{
if (IsAuthenticUser(login_email.Text, login_password.Text))
{
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, masterServiceResult[0], DateTime.Now, DateTime.Now.AddMinutes(30), false, masterServiceResult[2], FormsAuthentication.FormsCookiePath);
string encryptedCookie = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedCookie);
Response.Cookies.Add(cookie);
Response.Redirect(FormsAuthentication.GetRedirectUrl(masterServiceResult[0], true));
}
}
private bool IsAuthenticUser(string email, string password)
{
MembershipService membershipService = new MembershipService();
var result = membershipService.GetUserAndRoleFromCredentials(email, password);
//result is an array with userName, userId, error(bool) as string, and error message if any
if (bool.Parse(result[3]) == false)
{
masterServiceResult = result;
return true;
}
return false;
}
}
Then in Global.asax I have attached to the AuthenticateRequest event
void Global_AuthenticateRequest(object sender, EventArgs e)
{
if (Request.IsAuthenticated)
SetupRoles();
}
private void SetupRoles()
{
if (Context.User != null)
{
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];
if (authCookie == null)
return;
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
string[] roles = authTicket.UserData.Split(new char[] { '|' });
IIdentity fi = Context.User.Identity;
IPrincipal pi = new GenericPrincipal(fi, roles);
Context.User = pi;
//sets the application principal to use the logged on user account
System.Threading.Thread.CurrentPrincipal = System.Web.HttpContext.Current.User;
// sanity check to ensure the role was set up.
if (User.IsInRole("Admin"))
{
int i = 0; // debug line
}
}
}
At this point everything is working as intended; my test account is now set as the context.user, the Current Principal user is set as the current user, and the users role of Admin is causing the "int i=0" debug line to be hit. However once the default page Page_Load is hit this is no longer the case.
Default page. Updated 01/03/12 to show another test case.
protected void Page_Load(object sender, EventArgs e)
{
System.Collections.IList visibleTables = MetaModel.Default.VisibleTables;
// this line comes back as false, though at this point the user name is the same.
if (User.IsInRole("Admin"))
{
int i = 0;
}
// the next 4 lines of code is a work around I can do to retrieve the roles from the ticket.
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
// this line of code will correctly show the users role
string[] roles = authTicket.UserData.Split(new char[] { '|' });
foreach (var table in MetaModel.Default.Tables)
{
var permissions = ((MetaTable)table).GetTablePermissions(roles);
if (permissions.Contains(TablePermissionsAttribute.Permissions.DenyRead))
visibleTables.Remove(table);
}
if (visibleTables.Count == 0)
{
throw new InvalidOperationException("There are no accessible tables. Make sure that at least one data model is registered in Global.asax and scaffolding is enabled or implement custom pages.");
}
Menu1.DataSource = visibleTables;
Menu1.DataBind();
}
*Updated: I am a able to get the roles for the user rom the authentication ticket, and the principal is correct, but I am baffled as to why the User.IsInRole line fails.

Related

Thread.CurrentPrincipal is ClaimsIdentity returns false and how to get the claims from Thread.CurrentPrincipal

I have asp.net web application (.NET Framework 4.8), which has ADFS at this point of time for authentication. Now I am working on migration of ADFS authentication to Azure AD with Authorization code flow and OIDC protocol.
I have added a SecurityTokenValidated notification in the OIDC middleware where I am trying to perform validation and add code for custom claim with following code:
SecurityTokenValidated = notification =>
{
AddUserClaimsToPrincipal(notification.AuthenticationTicket.Identity);
return Task.FromResult(0);
}
private void AddUserClaimsToPrincipal(ClaimsIdentity identity)
{
string nameClaimValue = string.Empty; // Get Alias
string emailClaimValue = string.Empty; // Get Email
string displayClaimNameValue = string.Empty; // Get Display Name
IPrincipal principal;
Claim displayNameClaim = identity.FindFirst(t => t.Type == CLAIM_DISPLAYNAME);
Claim emailClaim = identity.FindFirst(t => t.Type == CLAIM_EMAIL);
if (displayNameClaim != null)
{
displayClaimNameValue = displayNameClaim.Value;
}
if (emailClaim != null)
{
emailClaimValue = emailClaim.Value;
}
nameClaimValue = emailClaimValue;
List<string> roles;
bool userExists = ValidateUser(nameClaimValue, out roles);
identity.AddClaim(new Claim("SampleApp_UserAuthorized", userExists.ToString()));
if (identity.FindFirst(t => t.Type == CLAIM_Role) == null)
{
foreach (var role in roles)
{
identity.AddClaim(new Claim(CLAIM_Role, role));
}
}
}
Now I am trying to validate the user authorization in Global.asax file using the event : Application_PostAuthenticateRequest
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
if (Thread.CurrentPrincipal.Identity.IsAuthenticated && Thread.CurrentPrincipal is ClaimsIdentity)
{
// Code to fetch the claims
// If the incoming claim contains the custom claim : SampleApp_UserAuthorized then send the user to
// unaurhorized.html page
}
}
In the above code I see Thread.CurrentPrincipal.Identity.IsAuthenticated is returning true but on the other hand Thread.CurrentPrincipal is ClaimsIdentity is returning false.
I want to fetch the custom claim:SampleApp_UserAuthorized in the Application_PostAuthenticateRequest to send the user to the unauthorized.html page
Can anyone help me with some code sample to fix this issue.
I have fixed the issue with the following code and it works fine for me now:
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
if (!User.Identity.IsAuthenticated)
{
//this.Context.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" }, WsFederationAuthenticationDefaults.AuthenticationType);
this.Context.GetOwinContext().Authentication.Challenge(new AuthenticationProperties{RedirectUri = "/"}, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
if (Thread.CurrentPrincipal.Identity.IsAuthenticated && Thread.CurrentPrincipal is System.Security.Claims.ClaimsPrincipal)
{
if (!Convert.ToBoolean(((System.Security.Claims.ClaimsPrincipal)Thread.CurrentPrincipal).FindFirst(c => c.Type == "SampleApp_UserAuthorized").Value))
{
//Avoid Redirection for static files (used in Access denied page)
List<string> staticcontentpaths = new List<string>{".css", ".js", ".png", ".gif", ".jpg", ".jpeg", ".ico", ".svg", ".woff", ".ttf"};
string extension = Path.GetExtension(HttpContext.Current.Request.PhysicalPath).ToLower();
if (!staticcontentpaths.Contains(extension))
{
Server.Execute("~/errors/auth.html");
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
}
}
}

User authentication Failed

I have created a Website in ASP.Net MVC5 and used a login function in it, it works fine on localhost but when I uploaded the site server,
Server redirects me to the login page on each click.
Below is login Function
public ActionResult DoLogin(string username, string password)
{
if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password))
{
var user = new UserRepository().GetAll()
.Where(u => u.UserName.ToUpper() == username.Trim().ToUpper()
&& u.Password == password).SingleOrDefault();
if (user != null)
{
FormsAuthentication.SetAuthCookie(user.UserName,true);
var authTicket = new FormsAuthenticationTicket(1, user.UserName, DateTime.Now, DateTime.Now.AddHours(24), true, user.Roles);
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
HttpContext.Response.Cookies.Add(authCookie);
Session["Name"] = user.Name;
return RedirectToAction("Index", "Student");
}
}
ViewBag.ErrorMessage = "User Name or Password is incorrect";
return View("Login");
}
then I added the below function in Global.asax.cs File.
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
var authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
if (authTicket != null && !authTicket.Expired)
{
var roles = authTicket.UserData.Split(',');
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(new FormsIdentity(authTicket), roles);
}
}
}
After that I have added [Authorize(Roles = "Admin")] before each Controller (Not before methods in Controller) I want to Restrict access to.
Now, whenever I login, It redirects me to Index Method of Student controller, after that I click on some other link it again takes me to the Login page. Sometimes it takes me to clicked link without taking me to Login Page. Whats the Problem with my code? it works fine on localhost.
You are assigning the auth ticket twice;
if (user != null)
{
//FormsAuthentication.SetAuthCookie(user.UserName,true); Remove it
var authTicket = new FormsAuthenticationTicket(1, user.UserName, DateTime.Now, DateTime.Now.AddHours(24), true, user.Roles);
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
HttpContext.Response.Cookies.Add(authCookie);
Session["Name"] = user.Name;
return RedirectToAction("Index", "Student");
}
Also, I suggest you to don't store the roles in cookie because if the user role has been removed or added new one, you can't update them. So,
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
var authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
if (authTicket != null && !authTicket.Expired)
{
var roles = authTicket.UserData.Split(',');//fill it from database and it could be better to use cache mechanism for performance concern
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(new FormsIdentity(authTicket), roles);
}
}
}

How to validate for existing account on login click

I currently have a login form and clicking the login button invokes the following method:
protected void btnLogin_Click(object sender, EventArgs e)
{
AccountBLL accBLL = new AccountBLL();
string username = tbUsername.Text;
string password = accBLL.getAccount(username).Password;
if (tbPassword.Text == password)
{
// Authenticate user
string role = accBLL.getAccount(username).Role;
// Create Form Authentication Ticket
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1, username, DateTime.Now, DateTime.Now.AddMinutes(60), false, role);
// Encrypt the ticket
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
// Create cookie
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
// Add cookie to outgoing cookie collection
Response.Cookies.Add(authCookie);
// Redirect to the URL
if (role == "admin")
Response.Redirect("AdminHome.aspx");
else if (role == "user")
Response.Redirect("UserHome.aspx");
else
lblMsg.Text = "Invalid login!";
}
else
{
lblMsg.Text = "Invalid password.";
}
//string accountID = (Session["AccountID"]).ToString();
}
However, if the account doesn't exist/is not created yet I will get a null pointer error on string password = accBLL.getAccount(username).Password; because the username passed in to to the getAccount() method doesn't exist. So my question is, how should I edit the code to implement some sort of validation, should the event that the userid passed in to retrieve the password is non existent, an error message would be displayed to the user instead?
Well, you've said it yourself that the method getAccount returns null when the provided username does not exist. If I were you I would check if accBLL.getAccount(username) returns a null. If it does, I would not execute the rest of the code and instead display a label with a message to the user that the enterd username does not exist.
First check if the username exists, and if it does, then check if the password is correct.
I hope this helps.
Change these lines
string password = accBLL.getAccount(username).Password;
if (tbPassword.Text == password)
to this.
var account == accBLL.getAccount(username);
if (account != null && tbPassword.Text == account.Password)
in your GetAccount method compare for null
like
fill dt
and compare dt
if (dt.rows.count==0)
{
return null;
}
this will return null value if account is not created yet.
and then your login button click event will look like this.
protected void btnLogin_Click(object sender, EventArgs e)
{
AccountBLL accBLL = new AccountBLL();
string username = tbUsername.Text;
string password = accBLL.getAccount(username).Password;
if(password != null)
{
if (tbPassword.Text == password)
{
// Authenticate user
string role = accBLL.getAccount(username).Role;
// Create Form Authentication Ticket
..... your remaining code including else
}
else
{
lblMsg.Text = "Account Doesnt Exist";
}

Simplemembership and cookie userdata compatibillity

I am trying to use SimpleMembershipProvider for FormsAuthentication. Now this provider internally creates a FormsAuth cookie without any additional userdata.
I want to include some other information in the cookie such as UserId, Role, etc
I have implemented following-
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (isAuthorized)
{
var formsCookie = httpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
var identity = new AppUserIdentity(string.Empty, true);
if (formsCookie != null)
{
var cookieValue = FormsAuthentication.Decrypt(formsCookie.Value);
if (cookieValue != null && !string.IsNullOrEmpty(cookieValue.UserData))
{
var cookieData = SerializerXml.Deserialize<UserNonSensitiveData>(cookieValue.UserData);
identity = new AppUserIdentity(cookieValue.Name, cookieData.UserId, true);
}
else if (cookieValue != null)
{
//TODO: Find out technique to get userid value here
identity = new AppUserIdentity(cookieValue.Name, null, true);
}
}
var principal = new AppUserPrincipal(identity);
httpContext.User = Thread.CurrentPrincipal = principal;
}
return isAuthorized;
}
}
This attribute is decorated on all required controller methods. When a user registers or login on the website I am updating the cookie as well with additional userdata (serialized string)
var newticket = new FormsAuthenticationTicket(ticket.Version,
ticket.Name,
ticket.IssueDate,
ticket.Expiration,
ticket.IsPersistent,
userdata,
ticket.CookiePath);
// Encrypt the ticket and store it in the cookie
cookie.Value = FormsAuthentication.Encrypt(newticket);
cookie.Expires = newticket.Expiration.AddHours(24);
Response.Cookies.Set(cookie);
However, in MyAuthorizeAttribute it never gets userdata in the cookie. Is there anything wrong in the above code? Or something missing somewhere else?
Found the answer to this question,
check out the link
http://www.codetails.com/2013/05/25/using-claims-identity-with-simplemembership-in-asp-net-mvc/

How to get the cookie value in asp.net website

I am creating a cookie and storing the value of username after succesfull login. How can I access the cookie when the website is opened. If the cookie exist I want to fill the username text box from the cookie value. And how to decrypt the value to get the username. I am doing server side validation by getting the userdetails from the database. I am using vs 2010 with c#
FormsAuthenticationTicket tkt;
string cookiestr;
HttpCookie ck;
tkt = new FormsAuthenticationTicket(1, txtUserName.Value, DateTime.Now,
DateTime.Now.AddYears(1), chk_Rememberme.Checked, "User Email");
cookiestr = FormsAuthentication.Encrypt(tkt);
ck = new HttpCookie(FormsAuthentication.FormsCookieName, cookiestr);
if (chk_Rememberme.Checked)
{
ck.Expires = tkt.Expiration;
ck.Path = FormsAuthentication.FormsCookiePath;
Response.Cookies.Add(ck);
}
cookie is created with name as .YAFNET_Authentication and content is encrypted
Webconfig:
<forms name=".YAFNET_Authentication" loginUrl="Home.aspx"
protection="All" timeout="15000" cookieless="UseCookies"/>
You may use Request.Cookies collection to read the cookies.
if(Request.Cookies["key"]!=null)
{
var value=Request.Cookies["key"].Value;
}
FormsAuthentication.Decrypt takes the actual value of the cookie, not the name of it. You can get the cookie value like
HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName].Value;
and decrypt that.
add this function to your global.asax
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];
if (authCookie == null)
{
return;
}
FormsAuthenticationTicket authTicket = null;
try
{
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch
{
return;
}
if (authTicket == null)
{
return;
}
string[] roles = authTicket.UserData.Split(new char[] { '|' });
FormsIdentity id = new FormsIdentity(authTicket);
GenericPrincipal principal = new GenericPrincipal(id, roles);
Context.User = principal;
}
then you can use HttpContext.Current.User.Identity.Name to get username. hope it helps
HttpCookie cook = new HttpCookie("testcook");
cook = Request.Cookies["CookName"];
if (cook != null)
{
lbl_cookie_value.Text = cook.Value;
}
else
{
lbl_cookie_value.Text = "Empty value";
}
Reference Click here

Categories