Authentication for MVC and WebAPI using customer user, role tables - c#

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
}

Related

Custom authentication in ASP.NET MVC using session

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?

How design a controller action to respond to guess and authorized users

I have an action in my controller design for users already register in my system:
[Authorize]
public ActionResult getUserData()
{
string UserId = User.Identity.GetUserId();
return getDataFromDB(UserID);
}
And is working OK. Also redirect to the LOGIN page if you arent authenticated yet.
But I also want to have a dummy function getFakeData() for anonymous users visiting the page so they can see the like a demo of the page.
Where should I put the validation to see if user is authenticated or not and change the behaviour?
On the webpage I can set the dataUrl based if user is authenticated. I can make a separated function without [Authorize] tag
#if (Request.IsAuthenticated)
{
dataUrl = '=/Project/Controller/getUserData'
} else {
dataUrl = '=/Project/Controller/getFakeData'
}
Or can I do it on the same action controller and checking if user is authenticated or not? But not sure is that is possible.
Or is a better way to do it?
There are various ways to do this but here is my recommendation
// For ASP.Net MVC 5 simply inherit from AuthorizationAttribute and override the methods.
public class AccessControlAttribute : Attribute, IAuthorizationFilter
{
private readonly Roles role;
public AccessControlAttribute(Roles role) {
this.role = role;
}
private Boolean AuthorizationCore(AuthorizationFilterContext context) {
var username = context.HttpContext.Request.Cookies["loginCookie_username"];
var password = context.HttpContext.Request.Cookies["loginCookie_password"];
if (role == Roles.FakeFullAccess) {
username = "FAKE";
goto final;
}
//In ASP.Net MVC 5 use Ninject for dependency injection and get the service using : [NinjectContext].GetKernel.Get<DbContext>();
DbContext db = (DbContext) context.HttpContext.RequestServices.GetService(typeof(DbContext));
if (username != null && password != null) {
var findUser = db.Set<Login>().Find(username);
if (findUser != null && findUser.Password.Equals(password) && findUser.RoleId == (int)role) {
goto final;
}
}
return false;
final: {
context.HttpContext.User.AddIdentity(new System.Security.Principal.GenericIdentity(username));
return true;
}
}
private void HandleUnauthorizedRequest(AuthorizationFilterContext context) {
context.Result = new RedirectToRouteResult(new {
area = "",
controller = "",
action = ""
});
}
public void OnAuthorization(AuthorizationFilterContext context)
{
if (AuthorizationCore(context))
{
// If using a combination of roles, you have to unmask it
if (role == Roles.FakeFullAccess) {
context.HttpContext.Request.Headers.Add("Render", "FakeAccess");
}
else if (role == Roles.Admin)
{
context.HttpContext.Request.Headers.Add("Render", "AdminAccess");
}
}
else {
HandleUnauthorizedRequest(context);
}
}
}
[Flags]
public enum Roles
{
FakeFullAccess = 0,
ReadOnly = 1,
Admin = 2,
Supervisor = 1 << 2,
AnotherRole = 1 << 3
}
in your view you can read the added header and customize the view (in ASP.Net Core there's no access to ControllerContext and ViewBag, if using ASP.Net MVC 5 you don't need to use the header trick)
// For ASP.Net MVC 5 use the ViewBag or ViewData
#Html.Partial(HttpContext.Request.Header["Render"])
//Assuming this renders the menu with proper functions.
Now you have fully customizable role based authentication system with fake access for testing.
Update:
To consume the attribute do the following
[AccessControl(Role.Admin)]
public TestController: Controller {
...
}
// Dedicated for testing
[AccessControl(Role.FakeAccess)]
public PreviewController: TestCoontroller{}
You can also combine roles if required like [AccessControl(Role.FakeAccess | Role.ReadOnly)] but you have to implement an unmasking method.

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
{
}

CustomAuthorize Attribute Not Updating After Adding/Changing User

I'm having an issue with an internal MVC site. I may have titled this wrong as I don't know exactly where the issue lies. I have the following custom authorize attribute:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public CustomAuthorizeAttribute(params string[] roles)
{
using (var dataLayer = new CarrierBoundEntities())
{
string userNames = string.Empty;
foreach (var user in dataLayer.tbl_PremiumWriteOffs_Users)
{
if (roles.Contains(user.Role))
{
userNames += user.Username + ",";
}
}
if (userNames.Length > 0)
{
// Remove last comma
userNames.Remove(userNames.Length - 1);
}
Users = userNames;
}
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
}
}
The datalayer is the default DbContext created by the Entity Framework. There is a view that allows users with an admin role to add/edit users. Here is the controller that handles a post to edit a user:
[HttpPost]
[ValidateAntiForgeryToken]
[CustomAuthorize(new string[] { "Admin" })]
public ActionResult UsersEditView(UsersVM viewModel)
{
using (var dataLayer = new CarrierBoundEntities())
{
var userToEdit = dataLayer.tbl_PremiumWriteOffs_Users.SingleOrDefault(x => x.ID == viewModel.UserSubmit.ID);
if (userToEdit == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
// If the username was changed then check and display error if it's the same as another entry
if (userToEdit.Username.Equals(viewModel.UserSubmit.Username, StringComparison.OrdinalIgnoreCase) == false)
{
foreach (var user in dataLayer.tbl_PremiumWriteOffs_Users)
{
if (user.Username.Equals(viewModel.UserSubmit.Username, StringComparison.OrdinalIgnoreCase))
{
ModelState.AddModelError("UserSubmit.Username", "A user with this username already exists.");
break;
}
}
}
if (ModelState.IsValid)
{
userToEdit.Username = viewModel.UserSubmit.Username;
userToEdit.Role = viewModel.UserSubmit.Role;
userToEdit.UserLastModified = this.User.Identity.Name;
userToEdit.DateLastModified = DateTime.Now;
dataLayer.SaveChanges();
return RedirectToAction("UsersView");
}
viewModel.RoleSelect = GetRoleSelectList();
return View(viewModel);
}
}
Now when I run it locally on my machine things work fine, but when deployed on a server something doesn't get updated when a new user is added or the role of an existing user is changed. New users still don't get access to any part of the site, and a user that is changed from admin to user will still have access to the admin areas. It stays this way until the app is restarted on the server.
The odd thing is that after making a change, the change is visible on both the front end and back end, so it seems that both the database and entity context are being updated fine. So I'm thinking it might be the custom authorize attributes that aren't updating with the new list of usernames, but I really have no idea and am having trouble debugging since it works as it should locally.
Any help would be greatly appreciated.
The way you are trying to extend the AuthorizeAttribue is wrong. You need to do something like this:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
private string[] _roles;
public CustomAuthorizeAttribute(params string[] roles)
{
_roles = roles;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var user = httpContext.User;
var isAuthorized = false;
// check in the database to see if the user
// is in one of the Roles in _roles and therefore authorized...
if(/* some code to check if the user is authorized */)
{
isAuthorized = true;
}
return isAuthorized;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
}
}

.Net Web Api - Override AuthorizationFilter

Hello I have a web api controller inside a mvc web site.
I'm trying to allow access to the controller using 2 rules:
User is admin or the request came from local computer;
I'm new to AuthorizationFilterAttribute but I tried to write one that limit access
to local request only:
public class WebApiLocalRequestAuthorizationFilter : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext == null)
{
throw new ArgumentNullException("httpContext");
}
if (actionContext.Request.IsLocal())
{
return;
}
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
actionContext.Response.Content = new StringContent("Username and password are missings or invalid");
}
}
Then I decorated my controller with 2 attributes as
[Authorize(Roles = "Admin")]
[WebApiLocalRequestAuthorizationFilter]
public class ContactController : ApiController
{
public ContactModel Get(int id)
{
ContactsService contactsService = new ContactsService();
return contactsService.GetContactById(id).Map<ContactModel>();
}
}
But as I suspected , now, in order to access the controller I need to be admin and the request should be made from localhost. How can I do it?
Kind regards,
Tal Humy
One solution is to create a class that inherits from AuthorizeAttribute
e.g. something like this
public class MyAuthorizeAttribute: AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool accessAllowed = false;
bool isInGroup = false;
List<string> roleValues = Roles.Split(',').Select(rValue => rValue.Trim().ToUpper()).ToList();
foreach (string role in roleValues)
{
isInGroup = IdentityExtensions.UserHasRole(httpContext.User.Identity, role);
if (isInGroup)
{
accessAllowed = true;
break;
}
}
//add any other validation here
//if (actionContext.Request.IsLocal()) accessAllowed = true;
if (!accessAllowed)
{
//do some logging
}
return accessAllowed;
}
...
}
Then you can use it like so:
[MyAuthorizeAttribute(Roles = "Support,Admin")]
In the above code, IdentityExtensions checks for, and caches, ActiveDirectory roles which also allows us to fake the current user having roles by changing the cache.

Categories