Add user role from identity to custom database - c#

I have a function that finds a user in a database and adds a role. The role is added in the Asp.net identity table. I am trying to update the role that also exists in another table in a different database. I've written the logic but I'm not quite there yet. So my question is, how do I correctly add the role that is being added to the aspnetuserroles table, to my custom table located in a different database? Any assistance would be helpful.
public async Task<bool> AddARole(string email, string role)
{
//get the users email address and role from the Person table
var currentUser = _context.PersonTable.Where(x => x.Email == email && x.UserRole == role);
var UserManager = _serviceProvider
.GetRequiredService<UserManager<ApplicationUser>>();
var userExists = await UserManager.FindByEmailAsync(email);
if (userExists!= null)
{
await UserManager.AddToRoleAsync(user, role);
//update the person's role
_context.PersonTable.Update(currentUser);
}
_identityContext.SaveChanges();
//save the changes
await _context.SaveChangesAsync();
return true;
}

Related

Delete user from default mvc application database

I am trying to delete an already existing user from a database, which was created automatically when creating MVC application.
The database consists of tables:
AspNetUsers
AspNetUserRoles
AspNetUserLogins
AspNetUserClaims
AspNetRoles
In my code it looks like this:
var user = new ApplicationUser { UserName = model.email, Email = model.email };
var context = new ApplicationDbContext();
context.Users.Attach(user);
context.Users.Remove(user);
context.SaveChangesAsync();
return RedirectToAction("OperationSuccess", "Account");
I have also tried this:
var user = new ApplicationUser { UserName = model.email, Email = model.email };
var context = new ApplicationDbContext();
UserManager.DeleteAsync(user);
But it doesn't help at all. The application itselt does not break and does not show any errors, but the user is still in the database. How do I delete it?
Try this code:
public async Task<IdentityResult> DeleteUser(string email)
{
var user = UserManager.Users.FirstOrDefault(x => x.Email == email);
if(user == null) return null;
var result = await UserManager.DeleteAsync(user); //here result has two properties Errors and Succeeded.
return result;
}
Also, your code is not working because you are creating the object yourself and assigning only two properties yourself in spite of fetching the data from database.
Hi I think you have some versioning problem and its seems that you need to give one extra paramater to the DeleteAsync method.
Kindly refer the below link, since they had same kind of issue and resolved it.
https://stackoverflow.com/a/24594440/3397630
Hope it may give you some idea for your solution too.
Thanks
Karthik
Hope below code will help you to fix your problem
[HttpPost]
public async Task<ActionResult> Delete(string userId)
{
// Check for for both ID and exit if not found
if (String.IsNullEmpty(userId))
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var user = UserManager.Users.SingleOrDefault(u => u.Id == Userid);
// Look for user in the UserStore
// If not found, exit
if (user == null)
{
return HttpNotFound();
}
var results = await UserManager.DeleteAsync(user); // Remove user from UserStore
// If the statement is a success
if (results.Succeeded)
{
// Redirect to Users page
return RedirectToAction("Index", "Users");
}
else
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
}

Identity Server 4 Register Users from External Providers

I'm trying to store new users data from the claims return from an external login.
lets just say I have a model
public class User
{
Guid UniqueIdentifier;
string Username;
string Firstname;
string LastName;
string Email;
Date DateOfBirth;
}
and a method to add a user to the Database:
_userService.Add(new User());
This is the standard IdentityServer implementation for their eternal login call back.
[HttpGet]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl)
{
// read external identity from the temporary cookie
var info = await HttpContext.Authentication.GetAuthenticateInfoAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
var tempUser = info?.Principal;
if (tempUser == null)
{
throw new Exception("External authentication error");
}
// retrieve claims of the external user
var claims = tempUser.Claims.ToList();
// try to determine the unique id of the external user - the most common claim type for that are the sub claim and the NameIdentifier
// depending on the external provider, some other claim type might be used
var userIdClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject);
if (userIdClaim == null)
{
userIdClaim = claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier);
}
if (userIdClaim == null)
{
throw new Exception("Unknown userid");
}
// remove the user id claim from the claims collection and move to the userId property
// also set the name of the external authentication provider
claims.Remove(userIdClaim);
var provider = info.Properties.Items["scheme"];
var userId = userIdClaim.Value;
// check if the external user is already provisioned
var user = await _userManager.FindByLoginAsync(provider, userId);
if (user == null)
{
user = new IdentityUser { UserName = Guid.NewGuid().ToString()
};
await _userManager.CreateAsync(user);
await _userManager.AddLoginAsync(user, new UserLoginInfo(provider, userId, provider));
}
var additionalClaims = new List<Claim>();
// if the external system sent a session id claim, copy it over
var sid = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId);
if (sid != null)
{
additionalClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value));
}
// issue authentication cookie for user
await HttpContext.Authentication.SignInAsync(user.Id, user.UserName, provider, additionalClaims.ToArray());
// delete temporary cookie used during external authentication
await HttpContext.Authentication.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
// validate return URL and redirect back to authorization endpoint
if (_interaction.IsValidReturnUrl(returnUrl))
{
return Redirect(returnUrl);
}
return Redirect("~/");
}
My question is how can I retrieve a unique Identifier for each user that logs in? Say a user logs in via google, I Cannot use their email address as a unique id because they potentially could already have registered using that email from another provider?
You might need to construct some form of identifier that comes from the token issued from the external provider. In OpenID Connect the subject claim is assumed to be locally unique to the provider. One way to construct a globally unique identifier is to make some sort of composite key (or constructed key) that uses both the issuer (iss) claim and the subject (sub) claim of the token, this identifier is generally guaranteed to be globally unique.

Get AspNetUsers (CurrentUser) from database after external login

I am trying to get a steam user avatar, after he logs in with OpenID.
But I don't know how to retrieve the database user right after login (to add the avater to the database manually)
I added the new attribute to the Application User:
// Add profile data for application users by adding properties to the ApplicationUser class
public class ApplicationUser : IdentityUser
{
public string AvatarPath { get; set; }
}
I created a new Migration and updated the DB. I am trying to alter the data for the user, right after login, but how do I retrieve the current user?!
AccountController:
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
if (result.Succeeded)
{
//Get Steam User
if(info.LoginProvider.ToLower() == "steam")
{
//I need the DB User
}
_logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider);
return RedirectToLocal(returnUrl);
}
I tried this:
ApplicationUser u = await GetCurrentUserAsync();
u is null
Found the solution:
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
if (result.Succeeded)
{
//Get Steam User
if(info.LoginProvider.ToLower() == "steam")
{
var user = await _userManager.FindByLoginAsync("Steam", info.ProviderKey);
user.AvatarPath = SteamApi.GetSteamAvatar(playerData);
var ir = await _userManager.UpdateAsync(user);
if(!ir.Succeeded)
{
foreach (var err in ir.Errors)
{
Console.WriteLine(err.Code + " - " + err.Description);
}
}
}
}
To fix errors with steam users containing UTF characters, see this:
.NET MVC 6 / vNext UserValidator to allow alphanumeric characters
Settings AllowedUserNameCharacters to string.empty skips the user name validation.
Only problem, the UserManager does not update the data in memory, redirection to my HomeController still shows old data ... but the database got updated correctly

Manually initialize Asp.Net Identity tables?

I created a website and published it to Azure. Now I want to protect some pages with authentication so I added the following code in the method Seed of Migrations.Configuration and published it to Azure.
However, the code is run even on local. The table AspNetRoles is still empty on both local and Azure SQL server. I tried to use Update-Database -Script -SourceMigration:0 to generate all the SQL statements but there is no SQL inserting the initial data to these tables of Asp.Net identity.
// Create role
var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
const string roleName = "CanEdit";
var role = roleManager.FindByName(roleName);
if (role == null)
{
role = new Microsoft.AspNet.Identity.EntityFramework.IdentityRole(roleName);
var roleresult = roleManager.Create(role);
}
var memberEmails = Properties.Settings.Default.CanEditMembers.Split(';');
foreach (var email in memberEmails)
{
var user = userManager.FindByName(email.Trim());
if (user != null)
{
var rolesForUser = userManager.GetRoles(user.Id);
if (!rolesForUser.Contains(role.Name))
{
var result = userManager.AddToRole(user.Id, role.Name);
}
}
}
Basically I want to insert 'CanEdit' to the AspNetRoles (I guess) and insert some other values (Users and their associations with the role) to these AspNetUser.... tables. Some of my Controls are already decorated with attribute [Authorize(Roles = "CanEdit")].

Correct way of setting the role for user when he is registered with Identity

I have a question, I'm new to identity, but still i would like to know what would be the correct way of assigning role to a user when he is registering?
I have here a code:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.UserName };
RoleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext()));
IdentityRole role = new IdentityRole("Admin");
await RoleManager.CreateAsync(role);
// Store Gender as Claim
user.Claims.Add(new IdentityUserClaim() { ClaimType = ClaimTypes.Gender, ClaimValue = "Male" });
//user.Roles.Add(new IdentityUserRole() { RoleId=role.Id, UserId=user.Id });
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
//await UserManager.AddToRoleAsync(user.Id, "Admin");
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
This is just a test code, but basically if i use method UserManager.AddToROleAsync( ...) it works, BUT, it only happens after the user is added, so basically i do twice the roundtrip to database.
I tried doing it with user.Roles.Add(...) but i get an error when running it.
So my question would be what is the most efficient and correct way of doing it?
I don't know if there's a better way. I normally to it the same way as you do, first creating the role (if it doesn't exist), then creating the user, and as a last step adding the user to the role.
To use user.Roles.Add(...) the role must be present. The reason is the database (in this case Entity Framework and SQL Server). When looking closer at the Identity database you'll see that there is a relationship between the AspNetRoles and AspNetUsers table through the AspNetUserRoles table which has the UserId and the RoleId as a key. That means you can't add a user to a role when the user does not exist yet (and vice versa). So in my opinion you have to do twice the roundtrip (if you don't directly work on the context).
This works fine (Asp.Net Core Identity):
var role = await this.roleManager.FindByNameAsync( "Admin" );
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var userRole = new IdentityUserRole<int> {
RoleId = role.Id,
};
user.Roles.Add(userRole );
var result = await this.userManager.CreateAsync( user, model.Password);

Categories

Resources