I am creating a to-do item where each item on creation belongs to a specific user. I am using the AspNetCore identity with a table AspNetUsers. I am trying to get the ID of the current user in an OnInitializedAsync method. I am running into a small problem. I found on another post that FirstOrDefault().Value will return the user Id, however, since my User model uses GUID as the UserID and the AspNetCore.Identity does not, I am running into an issue of how I could change the AspNetCore.Identity userID to a GUID. Other tables are reliant on the GUID type set for UserID (migration presents this error) and so I cannot change it. However, This might also not be the correct approach. I am wondering how I could retrieve the UserID correctly?
#inject AuthenticationStateProvider GetAuthenticationStateAsync
private Todo todoItem { get; set; } = new Todo ();
protected override async Task OnInitializedAsync()
{
todoItem.owningUser = new User();
var authstate = await GetAuthenticationStateAsync.GetAuthenticationStateAsync();
var user = authstate.User;
var id = user.Claims.FirstOrDefault().Value;
todoItem.owningUser.UserId = Guid.Parse(id); // did not work
}
You should set a breakpoint to browse through the user object in VS. Then you can see what claims are there, what they are called, etc. If you are using standard Identity, then the following should work:
#inject AuthenticationStateProvider ASP
#code {
protected override async Task OnInitializedAsync()
{
var user = (await ASP.GetAuthenticationStateAsync()).User;
var UserStringId = user.FindFirst(c => c.Type.Contains("nameidentifier"))?.Value;
todoItem.owningUser.UserId = Guid.Parse(UserStringId);
}
}
Related
I have stucked with a problem to add UserId property from my ApplicationUser to domain Player entity.
This is my domain entity Player, where I have virtual ApplicationUserId property.
I had an idea to write UserId after I created User in UserService, but could't proceed because of protection level of setter. Should I change remove protection level or there is another approach to achive result?
Maybe I should create a method in domain like SetUserId where I will set private property with UserId came from IdentityServer.CreateUser? Does it good approach?
public class Player: MyEntity
{
public string UserName { get; private set; }
public virtual Guid ApplicationUserId { get; private set; }
private Player()
{ }
}
UserService.cs snippet where user is creating
public async Task<(AppSignInResult result, SignInData data)> CreateUser(string username, string password, string email, string country)
{
var user = new ApplicationUser() { UserName = username, Email = email};
var result = await _userManager.CreateAsync(user, password);
...
// here is call of mediatr command
var command = new CreatePlayerCommand(username, country);
var id = await _mediator.Send(command);
...
return ...
}
CreatePlayerCommand.Handle handler code
public async Task<int> Handle(CreatePlayerCommand request, CancellationToken cancellationToken)
{
var player = new Player(
request.userName);
_unitOfWork.Players.Add(player);
await _unitOfWork.SaveChanges();
return player.Id;
}
Why do you need to set Player.ApplicationUserId? You can generate it in the constructor or in DB. Why do you change the Id? If you access only from CreateUser command, you don't need it. Because the id is not coming from the client. You should generate automatically like that:
Player(string userName, ...)
{
ApplicationUserId = Guid.NewGuid();
UserName = userName;
...
}
I've created method in domain entity
private void SetUserId(Guid userId)
{
ApplicationUserId = userId;
}
And passing in CreatePlayerCommand and passing user id from _userManager.CreateAsync result. It works like desired.
Although there's no best idea for this sort of issue, this one could be designed like _buyerId in order class. Please see the link first.
_buyerId should be set in order class because it's a private field. Also, for persisting _buyerId by EF, for example, it could be configured in orderConfiguration.
I'm trying to pull out of database objects where current user Id is in list of those objects.
My model:
public class Procedure
{
...
public IList<User> Lawyers{ get; set; }
...
}
And in controller:
[HttpGet]
public async Task<IActionResult> MyProcedures()
{
var user = await userManager.GetUserAsync(User);
var procedures = context.Procedures.Where(x => x.Lawyers.Contains(user));
return View(procedures);
}
This only selects one object.
EDIT:
Problem is in my User class, it takes only one Id from Procedure and that is why its showing my only one (last added). Thanks for help guys!
The way you wrote will work if you have icomparable or something implemented that can tell that a lawyer is “the” user if user id matches.
Without that you would need to do lawyers.Any(x=> x.UserId == user.UserId)
You are missing something, which user ??
Either pass something into your function signature or get it from HTTPContext you need to get the user you want to find
If you are using ASP core its bit different from the old ways
First this:
var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
And now..
// make sure you can access/inject it if you want
// private.. _httpContextAccessor
// then in your action
[HttpGet]
public async Task<IActionResult> MyProcedures()
{
// again make sure you can access the context _httpContextAccessor
var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
var procedures = context.Procedures.Where(x => x.Lawyers.Contains(userId).FirstOrDefault());
//... fill in whatever logic you want..
return View(parnice);
}
Update 2 based on question/comments:
Do this in two Steps
Step 1: Get the Current User (with claims or HTTPContext as shown below), for e.g. System.Security.Claims.ClaimsPrincipal currentUser = this.User;
Step 2: Using the user, find all the related Lawyers etc. context.Procedures.Where(x => x.Lawyers.Contains(userId)
Make sure to register HttpContextAccessor in your startup... double check this.. to register in your Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
In the original Core version, I have to double check if it changed now, assuming your code is inside an MVC controller:
public class YourController : Microsoft.AspNetCore.Mvc.Controller
Now, since you have the Controller base class, you can get the IClaimsPrincipal from the User property
System.Security.Claims.ClaimsPrincipal currentUser = this.User;
You can check the claims directly (without a round trip to the database):
var userId = _userManager.GetUserId(User); // Get user id:
To your second comment, you can get either the UserId or UserName
// user's userId
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier)
// user's userName
var userName = User.FindFirstValue(ClaimTypes.Name)
A nice reference for you hope it helps :)
I'm trying to create an authentication server for multiple applications with Owin.
For that purpose i'm adding a clientId to my AspNetUserRole table like this.
public class ApplicationUserRoles : IdentityUserRole
{
public ApplicationUserRoles() : base() { }
[Key]
public string ClientId { get; set; }
[ForeignKey("ClientId")]
public Audience Client { get; set; }
}
That is working fine.
What I can't find is where to override the role list creation when i'm doing the GrantResourceOwnerCredentials so i can filter only de roles for the current cilentId.
I tried to change it in UserManager FindAsync(username,password) but Roles is readonly.
I thing maybe I should override something on UserStore but i'm not sure which method should be overrided, and how I can be sure i'm loading all the other data such as claims and logins and checking all the security concerns it already does.
Any idea will be much appreciated.
Ok
I'm sure this isn't the best answer but it works and i'm sure it doesn't change anything in the indentity flow.
I change in my ApplicationUserManager the create identity method. I take thw identity created by owin, remove the role claims it already has and add new ones from the rolesi want.
public override async Task<ClaimsIdentity> CreateIdentityAsync(ApplicationUser user, string authenticationType)
{
var identityClaims = await base.CreateIdentityAsync(user, authenticationType);
List<Claim> claimsDelete = new List<Claim>();
foreach (var claim in identityClaims.Claims)
{
if (claim.Type == ClaimTypes.Role)
claimsDelete.Add(claim);
}
foreach(var claim in claimsDelete)
identityClaims.TryRemoveClaim(claim);
var roleStore = new RoleStore();
var roles = roleStore.GetAll();
for (int i = 0; i < user.Roles.Count(); i++)
{
if (((ApplicationUserRoles)((List<IdentityUserRole>)user.Roles)[i]).ClientId == this.clientId)
{
identityClaims.AddClaim(new Claim(ClaimTypes.Role, roles[((List<IdentityUserRole>)user.Roles)[i].RoleId]));
}
}
return identityClaims;
}
For security, I don't want just anybody to be able to register on my site but I can't stop users from registering while there is a registration page accessible on the site, so I added an "Authorized" flag to the ApplicationUser model:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Authorized { get; set; }
}
The idea is that if a user, whether logged in or not, does not have the value for this property set to true, the user will not be able to access secured content. A previously authorized user will have to authorize new users.
This brings into question how to get the first user authorized so I figured I'd seed the user in on my Migration. See below:
namespace Ortund.Migrations
{
internal sealed class Configuration :
DbMigrationsConfiguration<Ortund.Models.ApplicationDbContext>
{
protected override void Seed(Ortund.Models.ApplicationDbContext context)
{
context.Users.AddOrUpdate(
u => u.Id,
new Models.ApplicationUser { EmailAddress = "email#site.com", Authorized = true, EmailConfirmed = true }
);
}
}
}
Obviously I'm missing some crucial fields here Password hashes for one and that's where I'm having trouble.
I don't know how the UserManager hashes the password so I can't seem replicate that process here since the function is not an async Task.
For reference, here's the Register method in the AccountController:
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
...
}
}
The CreateAsync function specifies a cleartext password string that will be hashed. Attempting to replicate this in my Seed method above, I only have access to ApplicationUserManager.Create(Microsoft.AspNet.Identity.Owin.IdentityFactorOptions<ApplicationUserManager> options, Microsoft.Owin.IOwinContext context) which as far as I can see, doesn't suit my requirement here.
How can I create a full ApplicationUser in my seeded data?
You could call in to the UserManager.Create method and pass it the username and plain text password. That will hash the password for you and get that into the database
Something like this:
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
var user = new ApplicationUser { UserName = "username"};
userManager.Create(user, "password");
I've run into an issue when working with Microsoft MVC 5 and Identities 3.0.0 rc1-final.
So I've modified the AppUser.cs model that extends IdentityUser to include a FirstName and LastName column. My next objective is to create a claim for the user that stores there FullName, a concatenate of Firstname and Lastname. I've successfully done this when seeding the database. I can also successfully update the database when a user modifies there profile by changing there names. When a user makes a change to there profile I'd like to update the "FullName" claim within the database claims table as well as the users cookie so that I can display there full name in the header of the page. The problem is I can't seem to persist the data into the table. I am able to update the claims within the User object but that's about it. I've researched and tried a dozen different things but there doesn't seem to be a solution to my problem, below is some of my code:
AppUser.cs Model
public class AppUser : IdentityUser {
[StringLength(255)]
public string FirstName { get; set; }
[StringLength(255)]
public string LastName { get; set; }
}
Helper Classes. This was derived from my research and came accross this post. I realize I'm missing part of his method, the last 2 lines, but I could never get past compiliation errors when calling the authentication manager.
public static void AddUpdateClaim(this IPrincipal currentPrincipal, string key, string value) {
var identity = currentPrincipal.Identity as ClaimsIdentity;
if (identity == null)
return;
// check for existing claim and remove it
var existingClaim = identity.FindFirst(key);
if (existingClaim != null)
identity.RemoveClaim(existingClaim);
// add new claim
identity.AddClaim(new Claim(key, value));
}
ManageController, this is the post method for updating user profile:
public async Task<IActionResult> Index(IndexViewModel model) {
if (!ModelState.IsValid) {
return View(ModelState);
}
var user = await base.GetCurrentUserAsync();
if (user != null) {
user.Email = model.Email;
user.FirstName = model.FirstName;
user.LastName = model.LastName;
User.AddUpdateClaim("FullName", "Test User");
var result = await _userManager.UpdateAsync(user);
if (result.Succeeded) {
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.AccoutUpdated });
} else {
_logger.LogError(1, "Error updating user: {username}", user.UserName);
base.AddErrors(result);
return View(model);
}
}
// If we got this far, something failed, redisplay form with errors.
return View(model);
}
Any help at this point would be greatly appreciated as I've beaten my head against this wall for a while now!
I figured this out a couple days ago... basically I over complicated it. :P