My contacts application has login options but does not currently tie data to a specific user. In this post I am going to fix that problem. Add a new user ID property to the Contact class for storing the user’s ID.
public string UserId { get; set; }
The change to contact model needs to be applied to the database. To do this use the following dnx command to add a new entity framework migration.
dnx . ef migration add AddContactUser --context ContactsDbContext
At this point a run of the application will apply the migration to the database. This is assuming use of Database.AsRelational().ApplyMigrations() in the constructor of the appropriate data context, if not then dnx can be used to apply the migration.
The ContactsContoller now needs to require a logged in user as well as get the ID for the logged in user. The following two using statements are need to be added to support these operations.
using System.Security.Claims; using Microsoft.AspNet.Authorization;
Microsoft.AspNet.Authorization provides access to the authorize attribute which is used to require an authorized used. The authorize attribute can be used a the class or on individual functions. This example is at the class level.
[Authorize] public class ContactsController : Controller
System.Security.Claims provides an extension method on the controller’s User property to get the current user’s ID. The following is the code from the index action for the ContactsController with the added filter for user ID on the contacts query.
public IActionResult Index(string filter) { var contacts = from c in _db.Contacts where c.UserId == User.GetUserId() select c; if (!string.IsNullOrEmpty(filter)) { contacts = contacts.Where(c => c.Name.Contains(filter) || c.Address.Contains(filter) || c.City.Contains(filter) || c.Email.Contains(filter) || c.Phone.Contains(filter) || c.State.Contains(filter) || c.ZipCode.Contains(filter)); } if (Request?.Headers != null && Request.Headers["X-Requested-With"] == "XMLHttpRequest") { return PartialView("_ContactList",contacts); } return View(contacts); }
A similar change needs to be made on all the actions in the ContactsController. For example here is the user ID being used as part of the create action.
[HttpPost] [ValidateAntiForgeryToken] public IActionResult Create(Contact contact) { if (ModelState.IsValid) { contact.UserId = User.GetUserId(); _db.Contacts.Add(contact); _db.SaveChanges(); return RedirectToAction("Index"); } return View(contact); }
This method works fine but I am not sure if only using the User ID on the server-side is best practice or not. The other option would be to send the user ID to the client as a hidden field which would get returned when the user posted changes. It seems that forcing the User ID on the contact model on the server-side would prevent it from be tampered with, but this is a topic that I need to research further.