How to update AppDBContext in ASP.NET Core Web API - asp.net-core

I am quite new to ASP.NET and I am bit stuck with this.
I am creating an entry in my DB while registering the user:
private async Task<bool> CreateEntryInUserActions(AppUser user)
{
var entity = new UserActionEntity
{
UserId = user.Id,
};
await _context.tbl_UserActions.AddAsync(entity);
await _context.SaveChangesAsync();
return true;
}
I want to change the IsPasswordChanged field in the UserActions table to true when a user changes his/her password.
I am trying something like:
private async Task<bool> UpdateUserAction()
{
var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value; // gives me current user's id
var user = _context.tbl_UserActions
.Where(x => x.UserId.ToString() == userId).Select(x => x.IsPasswordChanged);
}
but I am not sure how to proceed and update this to "true". How do I update this entry?

You need to fetch the useraction entity from the table and then set the IsPasswordChanged property to true.
Try this:
private async Task<bool> UpdateUserAction()
{
var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value; // gives me current user's id
var user = _context.tbl_UserActions.FirstOrDefault(x => x.UserId.ToString() == userId);
if(user != null) //check if the record is not null
{
user.IsPasswordChanged = true; // set the column to desired value
_context.tbl_UserActions.Update(user);
await _context.SaveChangesAsync();
}
}

Related

When I sign in with SignInAsync in ASP.NET Core Identity, RedirectToLocal is not authenticated

When I sign in with SignInAsync in ASP.NET Core Identity, RedirectToLocal is not authenticated.
If I log in without returning Url or go to allow anonymous action, it works fine, but when I redirect authenticate action return to the login page like the user never signs in. Still, I go to allow anonymous action and see the user sign in everything is okay.
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> LoginWithSms(string userId, string code, string returnUrl)
{
if (userId == null)
{
throw new ArgumentNullException(nameof(userId));
}
if (code == null)
{
throw new ArgumentNullException(nameof(code));
}
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
throw new ApplicationException(string.Format(ErrorConstant.UnableToLoadUser, userId));
}
var result = await _userManager.ConfirmEmailAsync(user, code);
if (!result.Succeeded)
{
return View("AccountError", this.GetErrorVm(AccountConstant.Persian.ConfirmSms, ErrorConstant.Persian.CodeWrong));
}
if (!user.PhoneNumberConfirmed)
{
user.PhoneNumberConfirmed = true;
_context.Users.Update(user);
_context.SaveChanges();
}
await _signInManager.SignInAsync(user, true);
await _setUserActivityLog.SetLogAsync(user.Id, AccountConstant.Persian.LoginToProfile);
return RedirectToLocal(string.IsNullOrEmpty(returnUrl) ? AccountConstant.Routes.ReturnUrlManageIndex : returnUrl);
}
redirect action:
[HttpGet]
[ActionDetail(menuCode: MenuConstant.ManageService.Code, name: "پاسخ دادن به تیکت")]
public async Task<IActionResult> TicketAnswer(long id)
{
var baseTicket = await _context.Tickets.Include(t => t.TicketType).Include(t => t.TicketRecords)
.ThenInclude(tr => tr.Person)
.SingleOrDefaultAsync(t => t.Id == id);
if (baseTicket == null)
{
return NotFound();
}
var vm = new ManageVm<TicketAnwserVm>
{
Entity = new TicketAnwserVm
{
QuickResponses = _context.QuickResponses.OrderBy(qr => qr.Title).Select(qr => new QuickResponseVm
{
Id = qr.Id,
Title = qr.Title
}),
Ticket = new TicketDisplayVm(baseTicket.StartDate)
{
Id = baseTicket.Id,
PersonId = baseTicket.PersonId,
State = baseTicket.State,
Subject = baseTicket.Subject,
TicketTypeName = baseTicket.TicketType.Name,
TicketRecords = baseTicket.TicketRecords.Join(_context.Users, tr => tr.PersonId,
u => u.PersonId,
(tr, u) => new TicketRecordVm(tr.Date)
{
Id = tr.Id,
PersonName = tr.Person.Name,
UserId = u.Id,
Content = tr.Content,
IsOwner = tr.IsOwner,
TicketId = tr.TicketId,
Status = tr.IsOwner ? TicketStatus.Out : TicketStatus.In
})
}
}
};
return View(vm);
}

Can't access Claim by it's key after login with ASP.NET Core Identity in .NET 6 Project

I have a Blazor project in .NET 6. I am using ASP.NET Core Identity for login. After login, I have set some Claims with a custom Key. Here is the code:
Login Code:
var createdUser = await _userManager.FindByNameAsync(Input.UserName.Trim());
var LoginResult = await _signInManager.PasswordSignInAsync(Input.UserName, Input.Password, Input.RememberMe, lockoutOnFailure: false);
if (LoginResult.Succeeded)
{
var userRoles = await _userManager.GetRolesAsync(createdUser);
var claims = new List<Claim>()
{
new Claim("UserId", createdUser?.Id),
new Claim("Username", createdUser?.UserName),
new Claim("OrganizationId", createdUser?.OrganizationId)
};
if(userRoles!= null && userRoles.Count > 0)
{
foreach (var role in userRoles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
}
var appIdentity = new ClaimsIdentity(claims);
var claimPrincpal = new ClaimsPrincipal(appIdentity);
Thread.CurrentPrincipal = claimPrincpal;
User.AddIdentity(appIdentity);
return LocalRedirect(returnUrl);
}
After Login I have a UserInfoService class and there I have some code to get the Claim value. Here is the code below:
public class UserInfoService : IUserInfoService
{
private readonly AuthenticationStateProvider _authProvider;
private readonly UserManager<ApplicationUser> _userManager;
public UserInfoService(
AuthenticationStateProvider authProvider,
UserManager<ApplicationUser> userManager)
{
_authProvider = authProvider;
_userManager = userManager;
}
public async Task<string> GetUserName()
{
var auth = await _authProvider.GetAuthenticationStateAsync();
var user = auth.User;
if (!user.Identity.IsAuthenticated)
{
return null;
}
else
{
var claimsIdentity = user.Identity as ClaimsIdentity;
var userName = claimsIdentity.FindFirst("Username")?.Value;
if (string.IsNullOrWhiteSpace(userName))
return null;
return userName;
}
}
}
And In my Program.cs file I have the below settings for identity:
builder.Services.AddIdentity<ApplicationUser, ApplicationRole>(options => options.SignIn.RequireConfirmedAccount = false)
.AddRoles<ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
But whenever I call GetUserName method to get the username from Claim, it returns null. I tried to debug the code and during debugging whenever the breakpoint comes upon this line (var claimsIdentity = user.Identity as ClaimsIdentity;), I hover the mouse and have the below information into the Claims Property, which I don't understand how to access. I don't even see the Claims Key (UserId, Username) which I set during the login.
Can anyone help to find the answer to how can I access Claims Key and Value?
var appIdentity = new ClaimsIdentity(claims);
var claimPrincpal = new ClaimsPrincipal(appIdentity);
Thread.CurrentPrincipal = claimPrincpal;
User.AddIdentity(appIdentity);
By using the above code, the new claim will be added to the HttpContext.User, it will store data while processing a single request. The collection's contents are discarded after a request is processed.
So, I suggest you can store the Claims in the AspNetUserClaims table, you could use the UserManager.AddClaimAsync() method to add the specified claim to the user, and then use the SignInManager or ApplicationDbContext to get tye user's claims. Check the following sample code (Login post method):
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
//find current user.
var user = await _userManager.FindByEmailAsync(Input.Email);
//based on user information to query the user and role policy table. Here I set the user role directly.
var userrole = "User";
if (user.UserName.Contains("aa"))
{
userrole = "Admin";
}
//get the current user claims principal
var claimsPrincipal = await _signInManager.CreateUserPrincipalAsync(user);
//get the current user's claims.
var claimresult = claimsPrincipal.Claims.ToList();
//it it doesn't contains the Role claims, add a role claims
if (!claimresult.Any(c => c.Type == ClaimTypes.Role))
{
//add claims to current user.
await _userManager.AddClaimAsync(user, new Claim(ClaimTypes.Role, userrole));
}
//refresh the Login
await _signInManager.RefreshSignInAsync(user);
_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}
Based on the answer from the Lik Now I have added the below code in my login page and it's working fine.
var claims = new List<Claim>()
{
new Claim("UserId", createdUser?.Id),
new Claim("UserName", createdUser?.UserName),
new Claim("OrganizationId", createdUser?.OrganizationId),
new Claim("OrganizationName", OrganizationName)
};
var userRoles = await _userManager.GetRolesAsync(createdUser);
if (userRoles != null && userRoles.Count > 0)
{
foreach (var role in userRoles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
}
var claimsPrincipal = await _signInManager.CreateUserPrincipalAsync(createdUser);
var claimResult = claimsPrincipal.Claims.ToList();
if (claims != null && claims.Count > 0)
{
await _userManager.AddClaimsAsync(createdUser, claims.ToList());
}
await _signInManager.RefreshSignInAsync(createdUser);

Couldn't avoid NullReferenceException in Action Filter (ASP.NET Core)

I'm writing an action filter for setting LastAccessDate user property. On retrieving user's record from DB, i'm getting NullReferenceException. How to get rid of this exception? Here is my Action Filter:
public class LogActivity : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var resultContext = await next();
var id = int.Parse(resultContext.RouteData.Values["id"].ToString());
var repo = resultContext.HttpContext.RequestServices.GetService<UserRepo>();
Console.WriteLine(id);
// var user = await repo.GetRespondent(id);
var user= repo.GetRespondent(id).Result; <========= Here Exception occurs
if (user != null)
{
user.LastAccessDate = DateTime.Now;
await repo.SaveAll();
}
}
}
Here is my UserRepo repository's get method:
public async Task<User> GetRespondent(int id)
{
var user= await _context.User.FirstOrDefaultAsync(u => u.Id == id);
if (user!= null)
return user
return null;
}
Replace this line
var user= repo.GetRespondent(id).Result; <========= Here Exception occurs
with
var user = await repo.GetRespondent(id);

how to include the role to the JWT token returning?

What I have in my mind when navigating through the application, I want to save the token to the localhost along with role name and I will check if the users have access to a certain link. Is that how it works? with Authgard in Angular 8?. Can you give me some insight of navigating an application with the role from Identity(which is built in from ASP.net core 3.1).
login
// POST api/auth/login
[HttpPost("login")]
public async Task<IActionResult> Post([FromBody]CredentialsViewModel credentials)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password);
if (identity == null)
{
//return null;
return BadRequest(Error.AddErrorToModelState("login_failure", "Invalid username or password.", ModelState));
}
var jwt = await Tokens.GenerateJwt(identity, _jwtFactory, credentials.UserName, _jwtOptions, new JsonSerializerSettings { Formatting = Formatting.Indented });
return new OkObjectResult(jwt);
}
Generate Token Method
public static async Task<string> GenerateJwt(ClaimsIdentity identity, IJwtFactory jwtFactory, string userName, JwtIssuerOptions jwtOptions, JsonSerializerSettings serializerSettings)
{
var response = new
{
id = identity.Claims.Single(c => c.Type == "id").Value,
//probably here I want to send the role too!!
auth_token = await jwtFactory.GenerateEncodedToken(userName, identity),
expires_in = (int)jwtOptions.ValidFor.TotalSeconds
};
return JsonConvert.SerializeObject(response, serializerSettings);
}
}
You need to add claims information when generating your JWT.
Here`s an example
And another one:
1 part(how to implement JWT), 2 part(about claims here)

.net core get data for a user not the current user -- Identity

I have the following which gets me my info:
public async Task<IActionResult> OnGetAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID
'{_userManager.GetUserId(User)}'.");
}
var userName = await _userManager.GetUserNameAsync(user);
var email = await _userManager.GetEmailAsync(user);
var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
Username = userName;
Input = new InputModel
{
Email = email,
PhoneNumber = phoneNumber,
FriendlyName = user.FriendlyName
};
IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user);
return Page();
}
How would I get another users info... how to pass user that is not the current user I guess. I have tried several things and I think i'm missing something obvious here.
Thanks!
There are UserManager<TUser>.FindByXXX() methods that return user object:
Task<TUser> FindByEmailAsync(string email);
Task<TUser> FindByIdAsync(string userId);
Task<TUser> FindByLoginAsync(string loginProvider, string providerKey);
Task<TUser> FindByNameAsync(string userName);
Ended up using this:
var user = await _userManager.FindByIdAsync(Userid);
Getting Userid from the route.