I find lots of information about Identity but nothing specifically addressing this very common scenario.
I have a controller named ShowAccount() that should display the account data of the currently logged in user, and prevent him from seeing anything but its own account.
Also unauthenticated users should not be able to access this functionality at all.
How do I achieve this?
Thanks
Unauthenticated Users
K, I'll start with the simpler request, to block unauthenticated user from having access at all to your controller just add this attribute:
[Authorize]
above your controller, or if you want to allow some\disable some functions in the controller you can place it above the specific function.
In case you want to block your entire controller and allow just a few functions you can use this attribute:
[AllowAnonymous]
Limit user access to his own data
I'm doing something similar in one of my project so I thought it might help, nothing fancy, I would love to hear a better option myself.
For your 2nd issue, I assume that you have a model that stores data and that data has some kind relation to the UserID (foreign key maybe?).
What you can do is in your controler - filter the data you send back to the user, i.e on the view instead of returning:
return View(db.MyDB.ToList());
return:
MyDBClass data = db.MyDB.Where(u => u.UserID == GetUserID()).ToList();
return View(data);
Assume GetUserID() is a function that gives you the current user ID, in case you use the default authentication in MVC I can share it here as well.
This solution tho is not complete, you need to continue enforcing it in any other actions such as edit\delete\create or what ever other actions you support, you need to always check that the user is accessing only his data by comparing between the userID saved in the DB to the one in the request.
Hope this helps.
I had a similar challenge but I got mine
public ActionResult Create()
{
return View();
}
// POST: ArtistGig/Create
[HttpPost]
public ActionResult Create(ArtistGig artistGig)
{
var userid = User.Identity.GetUserId();
///
var artist = db.ArtistHubs.SingleOrDefault(a => a.ApplicationUserId == userid).Id;
artistGig.ArtistHubId = artist;
db.ArtistGigs.Add(artistGig);
db.SaveChanges();
return RedirectToAction("Index");
}
User.Identity.GetUseId is to query for the loged in user's Id according to the DbContext you are using
Related
I'm building a web app (asp.net mvc),
where i'm using the attribute [Authorize] on GET and Post.
For example:
[Authorize]
public ActionResult EditClient(string id)
{
//Do Stuff
}
I now want to look to ensure that the logged in user, can only access data that belongs to that user\account?
But I'm not sure how to do this, does .Net already provide methods\attributes to use?
For example, this is how I would get a client:
[Authorize]
public ActionResult EditClient(string id)
{
var user= new Token(this.User.Identity.Name);
//user.id
//user.accountId
//So does this Client belong to the same account as the user is in?
//We know the client and user both belong to an account(id)
//Are we allowed to return the below?
var client = _clientService.GetClient(id);
//client.id
//client.accountId
}
As mentioned not to sure what best practice\options I should apply, obviously I know I should apply this kind of logic in most places?
Ideas? Sample?
There are many ways you could achieve this. for example you could create a custom attribute that takes in the parameter and checks the resource belongs to the requesting user. This could get complex as you'd have many different attributes for each type of entity you are accessing.
You probably want other validation rules such as the requested client even exists (i.e. non existing id) I would extract a bunch of rules out such as entity exists, requested entity belongs to authorised user, entity is editable etc etc and inject that into your actions before performing changes or returning said entities, you could throw custom exceptions depending on which validation fails and then send a generic 500, or 400 down to the user with minimal error details (no stack trace). So your action could look something like:
[Authorize]
public ActionResult EditClient(string id)
{
editClientValidator.Validate(id);
var user= new Token(this.User.Identity.Name);
//user.id
//user.accountId
//So does this Client belong to the same account as the user is in?
//We know the client and user both belong to an account(id)
//Are we allowed to return the below?
var client = _clientService.GetClient(id);
//client.id
//client.accountId
}
Where the EditClientValidator class contains your custom rules for editing a client. Alternatively you could create an attribute essentially doing the same thing but only for access (client belongs to the authenticated user)
First consider the following code snippet generated by ASP.NET Core MVC scaffolding.
// GET: Students/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var student = await _context.Students
.SingleOrDefaultAsync(m => m.ID == id);
if (student == null)
{
return NotFound();
}
return View(student);
}
// POST: Students/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var student = await _context.Students.SingleOrDefaultAsync(m => m.ID == id);
_context.Students.Remove(student);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
There are some differences in HttpGet and HttpPost action methods as follows:
id is nullable in Get but not nullable in Post.
The preliminary check as follows is only in Get.
Code:
if (id == null)
{
return NotFound();
}
var student = await _context.Students
.SingleOrDefaultAsync(m => m.ID == id);
if (student == null)
{
return NotFound();
}
Questions:
For example, the visitor request id=5 to be deleted in GET but later he tampers with the id in POST by setting it to a number such as id=6 or setting it to an invalid value such as id=xodsfsdofsdfosdfsd. As there is no preliminary checks in HttpPost, how to prevent this?
You probably want to add a check into your POST action to validate the user because you cannot prevent tampering with the value.
The user deleting student may or may not have authorization to delete the student. That's up to your application to decide. That's not something a scaffolding tool can decide for you.
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var authorized = ValidateStudentDeletion(User, studentId: id);
if (authorized)
{
// delete student
...
return RedirectToAction("Index");
}
}
I have been dealing with this issue for a long time and after reviewing a number of articles, here is my conclusion:
Let's assume that the sent id is taken from a checkbox or a dropdown list. Now if the user changes the value, what would happen:
If you want to modify the database, the first thing you need to check it is whether the data is associated with the user or not. If not, here comes other scenarios:
The user intentionally had tampered the id. Well, if that's the case, the user account must be blocked but how? Check No. 2:
The user has logged out and logged in again but with a different account in a different browser tab. Now, the page that sends the request has the previous-logged-in account information.
Basically, account information is retrieved from authentication cookies and if a user opens a tab and logs out and logins in with a different account, you need to find a way to figure out.
Here are some suggestions for you:
Use SignalR or other means to refresh all browser tabs if the user logs out. That's what most well-known companies practice suck as Google or Facebook.
If you want to make it easier, hash the username or whatever data you save in authentication cookie and save it in a hidden field in your HTML code. Then compare it with the same hashed value of the cookie that is retrieved from the request. If they do conflict, block the user from taking any action and refresh the page.
What I do normally is saving these conflicts in the database and if it exceeds a limits, I realize the user is junk!
Well, here is my recommendations.
1- Data Access Authorization (this what you need)
Before deleting the user with EF context, you need to validate that the logged in user really owns the student record. For instance, you need to validate that the student object returned has a user id (or other name) identical to logged in user id before choosing to remove the record. This will forbid existing logged-in applications users from tampering data of other existing users. Usually, each record must have its own owner foreign key db field.
2- Use of GUID:
Never share your primary key student id this way. Instead, create a StudentGUID db field with uniqueidentifier type. This is what you need to share with the browser. This will forbid bad users from running an automated client code against your server with auto increment integers.
3- Session Authorization:
If you are using asp.net Forms authentication, then its easy to add the [Authorize] attribute to mvc actions you need to secure. This will make sure only logged in users can execute your mvc action code.
I am trying to see if there is something "out of the box" in ASP.net5 for authorization for my application needs. I am using a group/permission based approach for authorization. Using Identity3 I am using Role as Group and then I have created permissions from this. Each permission has a resource that it links to and 1 or more values, like:
Resource = Page, Permissions = Add, Update, View, Delete
Another complication is that the groups have dynamic names, and dynamic permissions!!
I have started to read about authorization in ASP.net5 and it seems that I have found something called Policies, which sound good. It seems to force you to use Claims, which is possible if I use a ClaimsTransformer to get all my permissions and add them as claims from the Db. But am I right in thinking that I would have to create a policy for each Permission, on each resource? That seems like a lot of setup.
Is there anything that I do not know about is already built in ASP.net5 that I could use? Like an attribute like this
[Authorize("Page", "Delete")]
Which I could add to the PageController Delete method.
If I have to use some sort of service and DI that into the controller to implement this, then that would be fine as well.
There is a ClaimsPrincipalPermissionAttribute that can fit to your requirements.
Or you can implement your own AuthorizeAttribute.
I use AspNet.Security.OpenIdConnect.Server for authorization. But you can also have a look at OpenIddict
In any case you can add the Authorize attribute to any method you want like this
[Authorize(Roles = "Administrator,SimpleUser,AnOtherRole")]
public void MyMethod() {}
Resource based authorization might fulfill your needs, but I am a little confused with the page being the resource, rather than what the page acts upon.
Taking your Page/Delete combination, I would imagine that rather than the resource being Page, your Page Delete action takes a parameter, indicating the page that is to be deleted? (If this is not the case then this approach isn't going to work of course)
In this case you'd do something like
[Authorize]
public class PageController : Controller
{
IAuthorizationService _authorizationService;
public PageController(IAuthorizationService authorizationService)
{
_authorizationService = authorizationService;
}
public Delete(int pageId)
{
var page = pageRepo.GetPage(pageId);
if (await authorizationService.AuthorizeAsync(User, page, Operations.Delete))
{
return View(page);
}
else
{
return new ChallengeResult();
}
}
}
In order to enable this you're write a handler based on page and an Operations requirement (or any old requirement, but a parameterized operations requirement means you can write a single handler and branch accordingly).
We tried very hard to move away from putting data in the attribute, and move it into requirements, because data in attributes is, to be frank, a maintenance nightmare.
One other thing to note; as handlers are resolved through DI you could inject your user to permissions resolver into the handler, which would avoid using claims transformation.
ASP.NET provides authentication mechanism out of the box which is easy to use, example:
public class HomeController : Controller
{
[Authorize]
public ActionResult Index()
{
ViewBag.Message = "This can be viewed only by authenticated users only";
return View();
}
[Authorize(Roles="admin")]
public ActionResult AdminIndex()
{
ViewBag.Message = "This can be viewed only by users in Admin role only";
return View();
}
}
Check this tutorial
Or if you want more sophisticated mechanism you can implement your own memberhsip provider based on the ASP.NET Membership Provider
I have a web app that uses MVC Controllers strictly to render HTML 5 and Javascript (Knockout JS) interface.
The login is handled by a "Login" MVC Contoller:
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Index(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
return RedirectToLocal(returnUrl);
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
Once logged into the app, requests are made to my application via AJAX calls to my WEB API controllers (RESTful API)
I then ensure that the user is allowed to perform the action that he's trying to do by filtering repo calls based on the user's AccountId. My problem I'm trying to solve is avoiding having to make a repo call to get the accountId at every single transaction.
Consider the following simplified REST endpoint:
// GET api/tasks
public IEnumerable<Dto.Get.tasks> Get()
{
long accountId = _accountRepository.FindBy(a => a.Name == HttpContext.Current.User.Identity.Name).Single().Id;
var modelTasks= _taskRepository.Where(t => t.AccountId == accountId).ToList();
return _taskModelConverter.Convert(modelTasks);
}
The line long accountId = _accountRepository.FindBy(a => a.Name == HttpContext.Current.User.Identity.Name).Single().Id; is peppered throughout my code.
I'd like to find a less "chatty" way getting my account id. Any ideas?
Cool problem and glad to see you are thinking along these lines! One easy option (but still chatty if you're not careful) is to store the account id in a cookie:
HttpContext.Current.Response.Cookies.Add(new Cookie("a_id", account_id));
But you will find that violates all kinds of security rules. A better approach would be to encrypt the account id:
var accountBytes = Encoding.UTF8.GetBytes(idString);
var encrypted = MachineKey.Protect(accountBytes, "Account Id");
var cookieValue = Convert.ToBase64String(encrypted);
// Stuff that bad boy into a cookie
(Be sure to set a purpose when using MachineKey.Protect as this will add a bit of hashing to the value before encrypting it).
What I usually do is create a bit of custom authentication logic in my Global.asax.cs class which does the work of your WebSecurity.Login above. A lot depends on how comfortable you are with writing that kind of code.
Remember to round-trip your cookies and set proper time-outs. Also, try to keep any cookie you set to under 1.5k as described under "Minimize Request Size" on this page - cookie and header size contribute significantly to request time when hitting RESTful endpoints!
A base controller class is probably what you need in this instance. Of course it will mean that you still continue to call the database every time you redirect to a new view.
I really looked, googled, this site, for a few days now, tried a bunch of different things, and I can't find an answer.
so, I'm trying to create a web application that will display client information after purchase. I'm using VS2012 express and C#, and I decided to use MVC4, mostly because there was a tutorial on ASP.NET that came pretty close to what I was looking to do. Any comments on my choices is not requested but also not unwelcome.
So the admin will enter all sales information at the end of each day. We record client phone numbers as account numbers in our sales protocol, so my thought was, to keep it simple, to just use the clients phone number as a login to the web application as well. Also, that way, when a client logs into the site to view the database, the database would filter automatically so that the particular client could only see their transactions.
The tutorial I followed is here.
I figured out that this is the point where the filter needs to be applied, but i'm having a lot of trouble doing so.
The controller is named "MainController"
The database is named "Main", table is "Mains"
"AccountNumber" is the field in the db that should match the Current User Id
public ActionResult Index()
{
return View(db.Mains.ToList());
}
As I understand it, I have to place [InitializeSimpleMembership] above, then grab the UserId, then define it as the filter.
First of all, you should decide one of these way:
1) keeping login and user info in a separate table and let the SimpleMembership(SM) does its default jobs.
2) Using an existing table so store users info and tell the SM which table is for.
Approach One:
To handle this approach, all you need is that you create users manually(as you do, I think) and add an extra line to the action method which is responsible of creating customers:
[HttpPost]
public ActionResult Create(Customer model)
{
if (ModelState.IsValid)
{
db.Customers.Add(model);
try
{
db.SaveChanges();
// here it is ...
WebSecurity.CreateUserAndAccount(model.Phone, model.Password);
}
catch
{
insertError = true;
}
// .. Other codes ...
}
Now, your customers can simply login to the site with their phone no. as username and that password.
And to retrieve items related to a specific user - which is currently logged into site - simply use the following query:
public ActionResult Index()
{
return View(db.Mains.Where(m => m.AccountNumber == User.Identity.Name)
.ToList());
}
If you also need approach two, tell me to update my answer and put it here