Custom authentication in ASP.NET MVC using session - c#

There are many questions about custom authentication in ASP.NET, but none of them answers how to fully integrate it with ASP.NET mechanics.
I want to make a web application for system which already exists and have users.
Let's create new ASP.NET MVC project. I choose "No authentication" to make it from scratch (because I need to read from custom tables, custom hashing function etc, so I'll go this way).
I'll use IIdentity and IPrincipal interfaces to carry logged in user in HttpContext.
public class Identity : IIdentity
{
public Identity(string name)
{
Name = name;
IsAuthenticated = true;
}
public string Name { get; private set; }
public string AuthenticationType { get; set; }
public bool IsAuthenticated { get; set; }
}
public class Principal : IPrincipal
{
public Principal(string email)
{
Identity = new Identity(email);
}
public IIdentity Identity { get; private set; }
public bool IsInRole(string role)
{
return false;
}
}
I'll create SessionsController which will create and destroy Session. Session will contain Id of logged in user.
public class UserManager
{
public bool Authenticate(WorkerDTO user, string password)
{
using (var context = new DatabaseContext())
{
var user = context.Users.SingleOrDefault(w => w.Email == user.Email);
if (user != null)
{
// compute SHA512 of password with salt
var hash = Hash(user.Password);
if (user.HashPassword == hash)
return true;
}
return false;
}
}
}
public class SessionsController : Controller
{
private IDatabaseManager _dbManager;
private UserManager _userManager;
public SessionsController(IDatabaseManager dbManager, UserManager userManager)
{
_dbManager = dbManager;
_userManager = userManager;
}
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(UserLoginViewModel model)
{
var user = _dbManager.Give(new WorkerByEmail(model.Email));
if(user != null && _userManager.Authenticate(user, model.Password))
{
// create session
Session["UserId"] = worker.Id;
// assign User property
User = new Principal(worker.Email);
return RedirectToAction("Index", "Home");
}
return RedirectToAction("New");
}
public ActionResult Logout()
{
Session.Remove("UserId");
User = null;
return View();
}
}
Application won't remember that User = new Principal(worker.Email);.
So, if I want to make use of sessions, I want to tell my ASP.NET application that User behind UserId carried by session (probably a cookie) each request is the logged in user. In Global.asax I have available events with reasonable names:
AuthenticateRequest
PostAuthenticateRequest.
Unfortunately, Session is unavailable in these events.
How to make use of ASP.NET in my application? Maybe Session isn't the way to go?
Should I create new IPrincipal object and attach to User prop each request?
And why User property isn't null at start? Should I set it to null on logout?

Related

.NET Core Singleton Session Wrapper

I am trying to create a Session Wrapper class for ASP.NET Core Razor Pages.
In traditional ASP.NET web forms my singleton session wrapper class used to work normally, but i am not sure if in .net core it will work the same or the same class will be shared across all requests.
I want to store specific information for each request (session) and not to be shared across all requests.
i created the following wrapper :
public class MySession
{
//the ISession interface mandatory
public ISession Session { get; set; }
private static MySession instance;
private MySession() {
}
public static MySession Instance
{
get
{
if (instance == null)
{
instance = new MySession();
}
return instance;
}
}
//properties
public User User
{
get => SessionHelper.SessionExtensions.Get<User>(this.Session, "User");
set => SessionHelper.SessionExtensions.Set<User>(this.Session, "User", value);
}
public bool LoggedIn
{
get => SessionHelper.SessionExtensions.Get<bool>(this.Session, "LoggedIn");
set => SessionHelper.SessionExtensions.Set<bool>(this.Session, "LoggedIn", value);
}
}
and from my page model (Login.cshtml.cs) i am doing the following :
public void OnGet()
{
MySession.Instance.Session = HttpContext.Session;
}
and i am accessing the session and storing and retrieving information perfectly as expected for example :
public ActionResult OnPostAuthenticate()
{
string username = Request.Form["username"];
string password = Request.Form["password"];
User user = this.userDataManager.GetUserAuthentication(username, password);
if (user != null)
{
//authentication success
//save session variables
MySession.Instance.User = user;
MySession.Instance.LoggedIn = true;
return new JsonResult(new { success = true });
}
else
{
return new JsonResult(new { success = false });
}
}
And as i said everything work perfect, but i want to know if it is SAFE to use the session this way or my requests will be messed up and the same session information will be shared across all requests ?

Authentication for MVC and WebAPI using customer user, role tables

I need to create a authentication for my MVC Application and WebAPI.
I have the user credential details & role information in a separate table in database. Can anyone suggest which model i can use to achieve this.
Thanks
Which Web Api are you using if it is 2 than try below code, and let me know if i could help you more, because i had same scenario like you have
you have to create a custom authorization filter and call it above ActionMethod,
Create a different class in your project and change build mode in Compile
public class BasicAuthenticationAttribute : AuthorizationFilterAttribute
{
public static bool VaidateUserRoleWise(string username, string password, int RoleId)
{
//DO DATABASE CONNECTION DO QUERY HERE
if (Username == username && Password == password)
{
return true;
}
else
{
return false;
}
}
public override void OnAuthorization(QuizzrApi.Controllers.QuizzrController.InputParamAdminLogin LoginDetails)
{
System.Web.Http.Controllers.HttpActionContext actionContext = null;
if (LoginDetails == null)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
else
{
//Bellow is the static method called above will return true or false if user matches
if (!VaidateUserRoleWise(LoginDetails.UserName, LoginDetails.Password, 1))
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
base.OnAuthorization(actionContext);
}
}
In controller :
[Route("AuthorizeSystemAdmin")]
[HttpPost]
[BasicAuthentication]
public HttpResponseMessage Login([FromBody] InputParamAdminLogin AdminLoginInput)
{
//do your logic here
}

Allow user to visit [Authorize] pages - MVC

My project got pages with [Authorize] where user have to log in to visit those pages.
Upon successful login with same userid and password as in database, the current users id get stored in session. But how do I do I authenticate/allow user to visit pages with [Authorize]?
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(User u)
{
if (ModelState.IsValid) //this is check validity
{
using (UserEntities db = new UserEntities())
{
var v = db.Users.Where(a=>a.UserName.Equals(u.UserName) && a.Password.Equals(u.Password)).FirstOrDefault();
if (v != null)
{
Session["LoggedUserID"] = u.Id.ToString();
Session["UserFullname"] = u.Name.ToString();
return RedirectToAction("AfterLogin");
}
}
}
return View(u);
}
Any help is much appreciate. Thanks.
If you absolutely want to manage login and security yourself using Session, You can create your own action filter which checks whether session has a user id set to it.
Something like this
public class AuthorizeWithSession : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Session == null ||
context.HttpContext.Session["LoggedUserID"]==null)
{
context.Result =
new RedirectToRouteResult(new RouteValueDictionary(
new {controller = "Account", action = "Login"}));
}
base.OnActionExecuting(context);
}
}
Now decorate this action filter on your secure actions/controllers
[AuthorizeWithSession]
public class TeamController : Controller
{
}
You should have your own role management if you want to control what the users can do.
Each user should have one or more roles, each role can have a set of permissions and you can create an action filter that inherits from AuthorizeAttribute to make sure it is executed as early as possible.
Inside the AuthorizeCore method of the AuthorizeAttribute , you will see if the user is authenticated or not, and if he is authenticated then you can read his identity, read his roles and permissions from the database and compare it to a value passed to the role.
ex:
public class RequireRoleAttribute : AuthorizeAttribute
{
public RoleEnum[] RequiredRoles { get; set; }
public RequireRoleAttribute()
{
}
public RequireRoleAttribute(params RoleEnum[] roles)
: this()
{
RequiredRoles = roles;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var principle = httpContext.User;
if (principle == null || principle.Identity == null || !principle.Identity.IsAuthenticated)
{
return false;
}
if (RequiredRoles != null)
{
if (!HasRole(RequiredRoles))
{
httpContext.Response.Redirect("/AccessDenied");
}
}
return base.AuthorizeCore(httpContext);
}
public bool HasRole(RoleEnum[] roles)
{
foreach (var role in roles)
{
if (HasRole(role))
return true;
}
return false;
}
public bool HasRole(RoleEnum role)
{
return true if the user role has the role specified (read it from database for example)
}
}
Then in your controller, just annotate the controller or action with the attribute
[RequireRole(RoleEnum.Administator)]
public class MySecureController : Controller
{
}

Work with the user after login, C# Identity

I have the following class that defines a user:
public class AppUser : IdentityUser
{
public int UserType { get; set; }
[DefaultValue("true")]
public bool NewUser { get; set; }
}
Im using the following code In my AuthController to sign in a user:
public async Task<ActionResult> LogIn(LogInModel model)
{
if (!ModelState.IsValid)
{
return View();
}
var user = await userManager.FindAsync(model.Email, model.Password);
if (user != null)
{
await SignIn(user);
return Redirect(GetRedirectUrl(model.ReturnUrl));
}
// user authN failed
ModelState.AddModelError("", "Invalid email or password");
return View();
}
Here Is my controller that handels everything when the user Is logged In:
[Authorize]
public class HomeController : Controller
{
private readonly UserManager<AppUser> userManager;
public HomeController()
: this(Startup.UserManagerFactory.Invoke())
{
}
public HomeController(UserManager<AppUser> userManager)
{
this.userManager = userManager;
}
// GET: Home
public ActionResult Index()
{
var ctx = Request.GetOwinContext();
var authManager = ctx.Authentication;
//var rolesForUser = userManager.GetRoles(user.Id);
//Kolla om det är new user
//Om det är new user, visa "setup"-sida där man kan konfa sitt konto
//Annars renderar vi default
return View();
}
protected override void Dispose(bool disposing)
{
if (disposing && userManager != null)
{
userManager.Dispose();
}
base.Dispose(disposing);
}
}
My question Is: How do I work with the logged in user? How can I access the user when he/she has logged In? Can I use the AppUser-class here to represent the user who Is logged In?
The AppUser Is a description Of the corresponding user table in the database, so how can I access all this properties that belongs to the logged in user? Can I use some information from the var ctx = Request.GetOwinContext();?
In PHP for example, you are using the session_id that represents the user. But how do you do It here in C# with Identity?
you can retrieve the user authenticated in HttpRequest, this way in any controller HttpContext.Current.User.Identity
I recommend that you verified if you request is authenticated this way HttpContext.Current.User.Identity.IsAuthenticated
In Asp.Net Mvc in controller can you retrieve context with the class ViewContex
I hope I've helped

Keeping current user object in memory

In a WinForms application I want to keep the currently logged user in memory through out the applications life. So that in subsequent user actions I could check the permission against the user. Another option is to store the user information in a text file locally but it seems not safe.
User Log In verification code
private void ValidateUser()
{
var hashEntered = Encryption.GetHash(_View.UserID, _View.Password); //hash of the salted password entered by user.
var User = _DataService.GetUser(_View.UserID); //user trying to log in
if (user != null)
{
var hashInDB = user.PassWord;
if (hashEntered != hashInDB)
{
MessageBox.Show("Invalid password");
}
else
{
_MainView.show();
}
}
else
{
MessageBox.Show("Invalid user name");
}
}
So for the MainView, the currently logged user should be available.
How to keep the current user object in memory until the program exits?
I would suggest a singleton.
public class UserSession{
private static volatile User currentUser;
private static object syncRoot = new Object();
private UserSession() {}
public static User GetUser(){
if (currentUser == null) throw new Exception("Not logged in.");
return currentUser;
}
public static void Login(User user){
if (currentUser != null) throw new Exception("Already logged in");
lock(syncRoot){
currentUser = user;
}
}
public static void Logout(){
lock(syncRoot){
currentUser = null;
}
}
}
You can store the user data in System.Threading.Thread.CurrentPrincipal of type IPrincipal which also has a property called Identity (which is of type IIdentity). The difference between those two is that you just store security associated data of user (suppose permission or roles ) in principal and other data in Identity. you can use Microsoft's already existing implementation of those two interfaces or you can build them by your own. here is an example
class CustomPrincipal : IPrincipal
{
public IEnumerable<string> Roles { get; set; }
public IIdentity Identity { get; set; }
public bool IsInRole(string role)
{
// check user for appropriate roles
return false;
}
}
class CustomIdentity : IIdentity
{
public int UserId { get; set; }
public string AuthenticationType { get; set; }
public bool IsAuthenticated
{
get { return !string.IsNullOrWhiteSpace(Name); }
}
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
CustomIdentity identity = new CustomIdentity
{
UserId = 1,
Name = "user1"
};
CustomPrincipal principal = new CustomPrincipal
{
Identity = identity,
Roles = new List<string> { "admin", "superAdmin" }
};
System.Threading.Thread.CurrentPrincipal = principal;
}
}
Principal is the part of ExecutionContext so it will be copied from thread to thread. so even if you were to start a new thread or task or any asynchronous job that would try to get the Principal it would be there
you would use this code than to retrieve the user principal
System.Threading.Thread.CurrentPrincipal as CustomPrincipal
and this to get user identity
System.Threading.Thread.CurrentPrincipal.Identity as CustomIdentity

Categories