Authentication changes in .NET 4.5.1 - asp.net

I have some knowledge on .NET 4.5, but totally new to 4.5.1. As I read, they have a couple of changes so that apps work with Identity, which is nice for scale web apps.
That being said, I need to work on a web app with a basic user/password login system and I'm wondering if this template Individual User Accounts can work, or if I have to go with No Authentication? Please explain your answer.

basic user/password login system
Individual User Accounts will configure ASP.Net Identity for you. In addition, it will also create basic login, logout and other extra templates too. Click on Learn more for more information.
However, if you just need simple FormAuthentication, you want to select No Authentication.
The following is the example of simple FormAuthentication.
Sign-In method
public void SignIn(string username, bool createPersistentCookie)
{
var now = DateTime.UtcNow.ToLocalTime();
TimeSpan expirationTimeSpan = FormsAuthentication.Timeout;
var ticket = new FormsAuthenticationTicket(
1 /*version*/,
username,
now,
now.Add(expirationTimeSpan),
createPersistentCookie,
"" /*userData*/,
FormsAuthentication.FormsCookiePath);
var encryptedTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName,
encryptedTicket)
{
HttpOnly = true,
Secure = FormsAuthentication.RequireSSL,
Path = FormsAuthentication.FormsCookiePath
};
if (ticket.IsPersistent)
{
cookie.Expires = ticket.Expiration;
}
if (FormsAuthentication.CookieDomain != null)
{
cookie.Domain = FormsAuthentication.CookieDomain;
}
Response.Cookies.Add(cookie);
}
Global.asax.cs
You need this in order to retrieve the username from cookie, and save it in IPrincipal Object.
public class Global : HttpApplication
{
private void Application_AuthenticateRequest(object sender, EventArgs e)
{
HttpCookie decryptedCookie =
Context.Request.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket ticket =
FormsAuthentication.Decrypt(decryptedCookie.Value);
var identity = new GenericIdentity(ticket.Name);
var principal = new GenericPrincipal(identity, null);
HttpContext.Current.User = principal;
Thread.CurrentPrincipal = HttpContext.Current.User;
}
}
web.config
Make sure you have authentication tag in web.config.
For example,
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" />
</authentication>
Usage
public ActionResult Index()
{
var username = User.Identity.Name;
return View();
}

Related

ASP.NET MVC Remember me

I've got a project based in ASP.NET MVC 4 that simple authentication.
I'm trying to get my site to automatically log the user in when they check the remember me checkbox. However I'm having problems getting this working. After closing down the browser and reopening it the user is never logged in.
After checking (http://forums.asp.net/t/1654606.aspx#4310292) I've added a machine key in, generated by IIS. I've set automatically generate at runtime and generate a unique key for each application have both been disabled and I've Generated Keys). Unfortunately this hasn't worked.
Looking at "Remember me" with ASP.NET MVC Authentication is not working, I've added in the line FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe) but that didn't work either so I've now commented it out.
I tried the answer given on ASP.NET MVC RememberMe but that doesn't seem to work either.
Am I missing something obvious?
//FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (model.RememberMe)
{
//int timeout = model.RememberMe ? 525600 : 2; // Timeout in minutes,525600 = 365 days
int timeout = 525600;
var ticket = new FormsAuthenticationTicket(model.UserName, model.RememberMe, timeout);
string encrypted = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encrypted);
cookie.Expires = System.DateTime.Now.AddMinutes(timeout);//My Line
Response.Cookies.Add(cookie);
}
this is how i do it
public class MyAuthentication
{
public static HttpCookie GetAuthenticationCookie(LoginModel model, bool persistLogin)
{
// userData storing data in ticktet and then cookie
JavaScriptSerializer js = new JavaScriptSerializer();
var userData = js.Serialize(model);
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1,
"akash",
DateTime.Now,
DateTime.Now.AddHours(1),
persistLogin,
userData);
string encTicket = FormsAuthentication.Encrypt(authTicket);
HttpCookie cookie= new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
cookie.Expires = authTicket.Expiration; //must do it for cookie expiration
return cookie;
}
internal static bool Login(string UserName, string Password)
{
//UserName="akash" Password="akash"
//check can be done by DB
if (UserName== "akash" && Password == "akash")
return true;
else
return false;
}
}
and then
[HttpGet]
[AllowAnonymous]
public ActionResult Login()
{
//ViewBag.Message = "Your contact page.";
HttpCookie cookie = Request.Cookies[FormsAuthentication.FormsCookieName];
// var ek = cookie.Value;
try
{
//some times no cookie in browser
JavaScriptSerializer js = new JavaScriptSerializer();
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
//string data = ticket.UserData;
LoginModel model = js.Deserialize<LoginModel>(ticket.UserData);
if (MyAuthentication.Login(model.UserName, model.Password) == true)
{
RedirectToAction("Index", "Home");
}
}
catch
{
}
return View();
you can check it on Global.asax or authorization filter.
make sure you have web.config has
<authentication mode="Forms">
<forms defaultUrl="/Home/Login" loginUrl="/home/Login" timeout="2880">
</forms>
</authentication>
and [Authorize] attribute before all controller.
builder.Services.AddControllersWithViews();
var constr = builder.Configuration["ConnectionStrings:Default"];
builder.Services.AddDbContext<AppDbContext>(opt =>
{
opt.UseSqlServer(constr);
});
builder.Services.AddIdentity<AppUser, IdentityRole>(opt =>
{
opt.Password.RequiredLength = 8;
opt.Password.RequireDigit= true;
opt.Password.RequireLowercase= true;
opt.Password.RequireUppercase= true;
opt.Password.RequireNonAlphanumeric= true;
opt.User.RequireUniqueEmail= true;
opt.Lockout.MaxFailedAccessAttempts= 5;
opt.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromSeconds(10);
opt.Lockout.AllowedForNewUsers= true;
}).AddEntityFrameworkStores<AppDbContext
().AddDefaultTokenProviders();
builder.Services.AddSession(opt =>
{
opt.IdleTimeout = TimeSpan.FromSeconds(15);
});
builder.Services.ConfigureApplicationCookie(opt =>
{
opt.LoginPath = "/Auth/Login";
});
app.UseSession();
app.UseAuthentication();
app.UseAuthorization();

GenericPrincipal IsInRole returns false for HttpContext.User

I have a credential method to set user credentials via GenericPrincipal. I am using asp.net MVC
public void SetCredentials(HttpContextBase context, string username, bool createPersistenceCookie)
{
FormsAuthentication.SetAuthCookie(username, createPersistenceCookie);
IIdentity identity = new GenericIdentity(username);
IPrincipal principal = new GenericPrincipal(identity,new []{"standart"});
context.User = principal;
}
I want to check User.IsInRole("standart") in controller action, but it returns false.
context.User.IsInRole("standart") //returns false
I want to use context.User in my application, but it returns always false.
I think you used asp.net membership api before. And now you want to create custom principal in your application.
When you send request to server, server uses a new clean HttpContext. So you lost your old informations. If you want to use old session informations is application, you shuld save your data in server or client side. You can do this two way.
Client cookie
Server session
I recommand you to use client cookies. Because data is being stored to client side, so you save server resources.
public void SetCredentials(HttpContextBase context, string username, bool createPersistenceCookie)
{
var formsAuthenticationTicket = new FormsAuthenticationTicket(
1,
username,
DateTime.Now,
DateTime.Now.AddMilliseconds(FormsAuthentication.Timeout.TotalMilliseconds),
createPersistenceCookie,
roles
);
var encryptedTicket = FormsAuthentication.Encrypt(formsAuthenticationTicket);
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
HttpContext.Current.Response.AppendCookie(authCookie);
}
I sended encrypted cookie to client side. And I should check this cookie all incoming request to server application.
And now in Global.asax file:
protected void Application_AuthenticateRequest(object sender, System.EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie == null) return;
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value);
IIdentity identity = new GenericIdentity(ticket.Name);
IPrincipal principal = new GenericPrincipal(identity, ticket.UserData.Split('|'));
HttpContext.Current.User = principal;
}
I hope solve your issue.

User.Identity.IsAuthenticated returns false sometimes

Im using asp.net 4.0 and Form auth.
To check if a user is authenticated or not, i use User.Identity.IsAuthenticated.
Most of time it works perfect but i dont know how, sometimes it returns false even if user has auth.
My web.config:
<authentication mode="Forms">
<forms name=".xyz" loginUrl="~/" timeout="120" protection="All" path="/" slidingexpiration=true/>
</authentication>
In 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;
}
and in login page:
FormsAuthenticationTicket authTick = new FormsAuthenticationTicket(1, email.Text, DateTime.Now, DateTime.Now.AddDays(360), true, password.Text, FormsAuthentication.FormsCookiePath);
string encriptTicket = FormsAuthentication.Encrypt(authTick);
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encriptTicket);
authCookie.Expires = DateTime.Now.AddDays(360);
Response.Cookies.Add(authCookie);
I also use ajax request in every 5 min. to keep session alive and this also reset auth timeout because slidingexpiration value.
I don't know what is wrong with it. sometimes same session and in same minute, it returns false for one page even if it returns true for all the other page. I never got this error but my visitors claim about that problem.
i found the problem. The problem was about difference between www.address.com and address.com.
www version pretend like a sub domain and creates new session and auth. If server redirects to www address when user came without www prefix, error happens. I will try url rewriting to solve it.

User is in role "admin" but [Authorize(Roles="admin")] won't authenticate

I found a great answer on SO describing how to set up custom user roles, and I've done the same in my project. So in my Login service I have:
public ActionResult Login() {
// password authentication stuff omitted here
var roles = GetRoles(user.Type); // returns a string e.g. "admin,user"
var authTicket = new FormsAuthenticationTicket(
1,
userName,
DateTime.Now,
DateTime.Now.AddMinutes(20), // expiry
false,
roles,
"/");
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName,
FormsAuthentication.Encrypt(authTicket));
Response.Cookies.Add(cookie);
return new XmlResult(xmlDoc); // don't worry so much about this - returns XML as ActionResult
}
And in Global.asax.cs, I have (copied verbatim from the other answer):
protected void Application_AuthenticateRequest(Object sender, EventArgs e) {
var authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null) {
var authTicket = FormsAuthentication.Decrypt(authCookie.Value);
var roles = authTicket.UserData.Split(new Char[] { ',' });
var userPrincipal = new GenericPrincipal(new GenericIdentity(authTicket.Name), roles);
Context.User = userPrincipal;
}
}
Then, in my ServicesController class, I have:
[Authorize(Roles = "admin")]
//[Authorize]
public ActionResult DoAdminStuff() {
...
}
I login as a user with the "admin" role, and that works. Then I call /services/doadminstuff - and I get access denied, even though when I put a breakpoint in Global.asax.cs, I can see that my roles do include "admin". If I comment out the first Authorize attribute (with roles) and just use a plain vanilla Authorize, then I can access the service.
I must be missing something critical here - but where to start looking?
I would recommend you use a custom authorize attribute instead of Application_AuthenticateRequest:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
string cookieName = FormsAuthentication.FormsCookieName;
if (!filterContext.HttpContext.User.Identity.IsAuthenticated ||
filterContext.HttpContext.Request.Cookies == null ||
filterContext.HttpContext.Request.Cookies[cookieName] == null
)
{
HandleUnauthorizedRequest(filterContext);
return;
}
var authCookie = filterContext.HttpContext.Request.Cookies[cookieName];
var authTicket = FormsAuthentication.Decrypt(authCookie.Value);
string[] roles = authTicket.UserData.Split(',');
var userIdentity = new GenericIdentity(authTicket.Name);
var userPrincipal = new GenericPrincipal(userIdentity, roles);
filterContext.HttpContext.User = userPrincipal;
base.OnAuthorization(filterContext);
}
}
and then:
[CustomAuthorize(Roles = "admin")]
public ActionResult DoAdminStuff()
{
...
}
Also a very important thing is to ensure that when you login an authentication cookie is emitted because you return an XML file. Use FireBug to inspect whether the authentication cookie is properly sent when you try to access the url /services/doadminstuff.
I would change principal assign at first:
Thread.CurrentPrincipal = userPrincipal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = userPrincipal;
}
as ASP.NET documentation stands.

ASP.NET Updating the FormsAuthenticationTicket

When a user logins into my site i create the following authenticate ticket:
// Create the authentication ticket
var authTicket = new FormsAuthenticationTicket(1, // Version
userName, // Username
DateTime.UtcNow, // Creation
DateTime.UtcNow.AddMinutes(10080), // Expiration
createPersistentCookie, // Persistent
user.Role.RoleName + "|~|" + user.UserID + "|~|" + user.TimeZoneID); // Additional data
// Encrypt the ticket
var encTicket = FormsAuthentication.Encrypt(authTicket);
// Store the ticket in a cookie
HttpContext.Current.Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket) { Expires = authTicket.Expiration });
Then in my Global.asax.cs file i have the following:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
// Get the authentication cookie
var authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
// If it exists then decrypt and setup the generic principal
if (authCookie != null && !string.IsNullOrEmpty(authCookie.Value))
{
var ticket = FormsAuthentication.Decrypt(authCookie.Value);
var id = new UserIdentity(ticket); // This class simply takes the value from the cookie and then sets the properties on the class for the role, user id and time zone id
var principal = new GenericPrincipal(id, new string[] { id.RoleName });
HttpContext.Current.User = principal;
}
}
protected void Session_Start(object sender, EventArgs e)
{
// If the user has been disabled then log them out
if (Request.IsAuthenticated)
{
var user = _userRepository.Single(u => u.UserName == HttpContext.Current.User.Identity.Name);
if (!user.Enabled)
FormsAuthentication.SignOut();
}
}
So far so good. The problem i have is that if an administrator changes a user's role or time zone then the next time they return to the site their ticket is not updated (if they selected remember me when logging in).
Here's my authentication settings incase it helps:
<authentication mode="Forms">
<forms timeout="10080" slidingExpiration="true" />
</authentication>
<membership userIsOnlineTimeWindow="15" />
I've been reading up on slidingExpiration but as far as i can tell it only increases the expiration time and doesn't renew the contents of the cookie. I'd really appreciate it if someone could help. Thanks
I simply changed my Session_Start to:
// If the user is disabled then log them out else update their ticket
if (Request.IsAuthenticated)
{
var user = _userRepository.Single(u => u.UserName == HttpContext.Current.User.Identity.Name);
if (!user.Enabled)
FormsAuthentication.SignOut();
else
RenewTicket(); // This calls the same code to create the cookie as used when logging in
}
My proposal would be to do another cookie for the remember.
This way session info can be in-memory cookie, while remember me cookie can be set to persist.

Resources