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);
}
}
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 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;
}
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
I want to deserialize Json result into a model.
I am using Azure Single sign on method. when I am login with new new created user in ad (new user in your organization) i am getting proper user info. but if i created new user in AzureAd with "User with an existing user account".I am able to log in and request is also authenticated. but i am not getting user profile. user profile is null. but "responseString" contains all values for user. can any one help me for that ?
UserProfile profile = JsonConvert.DeserializeObject<UserProfile>(responseString);
public class UserProfile
{
public string DisplayName { get; set; }
public string GivenName { get; set; }
public string Surname { get; set; }
}
Json
User with an existing user account
{"odata.metadata":"https://graph.windows.net/780cdd84-48ba-4be3-8d66-b40b8bee6b0b/$metadata#directoryObjects/Microsoft.WindowsAzure.ActiveDirectory.User","value":[{"odata.type":"Microsoft.WindowsAzure.ActiveDirectory.User","objectType":"User","objectId":"****************","accountEnabled":true,"assignedLicenses":[],"assignedPlans":[],"city":null,"country":null,"department":null,"dirSyncEnabled":null,"displayName":"mahesh","facsimileTelephoneNumber":null,"givenName":"map","jobTitle":null,"lastDirSyncTime":null,"mail":null,"mailNickname":"devb_azureteam.com#EXT#","mobile":null,"otherMails":["devb#azureteam.com"],"passwordPolicies":"None","passwordProfile":null,"physicalDeliveryOfficeName":null,"postalCode":null,"preferredLanguage":null,"provisionedPlans":[],"provisioningErrors":[],"proxyAddresses":[],"state":null,"streetAddress":null,"surname":"map","telephoneNumber":null,"usageLocation":null,"userPrincipalName":"devb_azureteam.com#EXT##AzureteamLoginTest.onmicrosoft.com"},
{"odata.type":"Microsoft.WindowsAzure.ActiveDirectory.User","objectType":"User","objectId":"****************","accountEnabled":true,"assignedLicenses":[],"assignedPlans":[],"city":null,"country":null,"department":null,"dirSyncEnabled":null,"displayName":"Education at AzureTeam","facsimileTelephoneNumber":null,"givenName":"Education","jobTitle":null,"lastDirSyncTime":null,"mail":null,"mailNickname":"education_azureteam.com#EXT#","mobile":null,"otherMails":["education#azureteam.com"],"passwordPolicies":null,"passwordProfile":null,"physicalDeliveryOfficeName":null,"postalCode":null,"preferredLanguage":null,"provisionedPlans":[],"provisioningErrors":[],"proxyAddresses":[],"state":null,"streetAddress":null,"surname":"at AzureTeam","telephoneNumber":null,"usageLocation":null,"userPrincipalName":"education_azureteam.com#EXT##AzureteamLoginTest.onmicrosoft.com"},
{"odata.type":"Microsoft.WindowsAzure.ActiveDirectory.User","objectType":"User","objectId":"*****************","accountEnabled":true,"assignedLicenses":[],"assignedPlans":[],"city":null,"country":null,"department":null,"dirSyncEnabled":null,"displayName":"mahesh","facsimileTelephoneNumber":null,"givenName":"mahesh","jobTitle":null,"lastDirSyncTime":null,"mail":null,"mailNickname":"mahesh","mobile":null,"otherMails":["map#azureteam.com"],"passwordPolicies":"None","passwordProfile":null,"physicalDeliveryOfficeName":null,"postalCode":null,"preferredLanguage":null,"provisionedPlans":[],"provisioningErrors":[],"proxyAddresses":[],"state":null,"streetAddress":null,"surname":null,"telephoneNumber":null,"usageLocation":null,"userPrincipalName":"mahesh#AzureteamLoginTest.onmicrosoft.com"}]}
New user in organization
{"odata.metadata":"https://graph.windows.net/780cdd84-48ba-4be3-8d66-b40b8bee6b0b/$metadata#directoryObjects/Microsoft.WindowsAzure.ActiveDirectory.User/#Element","odata.type":"Microsoft.WindowsAzure.ActiveDirectory.User","objectType":"User","objectId":"************","accountEnabled":true,"assignedLicenses":[],"assignedPlans":[],"city":null,"country":null,"department":null,"dirSyncEnabled":null,"displayName":"mahesh","facsimileTelephoneNumber":null,"givenName":"mahesh","jobTitle":null,"lastDirSyncTime":null,"mail":null,"mailNickname":"mahesh","mobile":null,"otherMails":["map#azureteam.com"],"passwordPolicies":"None","passwordProfile":null,"physicalDeliveryOfficeName":null,"postalCode":null,"preferredLanguage":null,"provisionedPlans":[],"provisioningErrors":[],"proxyAddresses":[],"state":null,"streetAddress":null,"surname":null,"telephoneNumber":null,"usageLocation":null,"userPrincipalName":"mahesh#AzureteamLoginTest.onmicrosoft.com"}
You actually get different JSON's. I simplified them to show the problem. For new user JSON looks like that:
{
"odata.metadata":"...Microsoft.WindowsAzure.ActiveDirectory.User/#Element",
"odata.type":"Microsoft.WindowsAzure.ActiveDirectory.User",
"displayName":"mahesh",
"givenName":"mahesh",
"surname":null
}
Deserialization will work with your model. But for existing user JSON is:
{
"odata.metadata":"...Microsoft.WindowsAzure.ActiveDirectory.User",
"value":[
{
"odata.type":"Microsoft.WindowsAzure.ActiveDirectory.User",
"displayName":"mahesh",
"givenName":"map",
"surname":"map"
},
{ },
{ }
]
}
As you can see you have a value property that holds an array of users. So to be able to deserialize it you will need to create new model:
public class RootObject
{
public List<UserProfile> Value { get; set; }
}
And then:
var obj = JsonConvert.DeserializeObject<RootObject>(json);
I'm not similar with Azure Active Directory but you should check why odata.metadata property is different for these responses.
I got the saluting :)
Add this block in your global.asax
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
ClaimsIdentity id = ((ClaimsIdentity)User.Identity);
Claim claim = id.FindFirst(ClaimTypes.Email);
if (claim != null)
{
string email = claim.Value;
id.AddClaim(new Claim(ClaimTypes.Name, email));
}
}