I'm working on Blazor server App project. I have the following codes for CustomAuthenticationStateProvider:
CustomAuthenticationStateProvider.cs
UserSession.cs
LoginController:
The problem is that when I check Context.User?.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value; in SignalR hub, Context.UserIdentifier is always null. How can I fix this?
CustomAuthenticationStateProvider.cs
publicclass CustomAuthenticationStateProvider : AuthenticationStateProvider {privatereadonly ProtectedSessionStorage _sessionStorage;private ClaimsPrincipal _anonymous = new ClaimsPrincipal(new ClaimsIdentity());public CustomAuthenticationStateProvider(ProtectedSessionStorage sessionStorage) { _sessionStorage = sessionStorage; }publicoverrideasync Task<AuthenticationState> GetAuthenticationStateAsync() {try {var userSessionStorageResult = await _sessionStorage.GetAsync<UserSession>("UserSession");var userSession = userSessionStorageResult.Success ? userSessionStorageResult.Value : null;if (userSession == null) {returnawait Task.FromResult(new AuthenticationState(_anonymous)); }var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim> {new Claim(ClaimTypes.Name, userSession.Username),new Claim(ClaimTypes.Role, userSession.UserRole),new Claim(ClaimTypes.NameIdentifier, userSession.UserId.ToString()) }, "Jwt"));returnawait Task.FromResult(new AuthenticationState(claimsPrincipal)); }catch (Exception) {returnawait Task.FromResult(new AuthenticationState(_anonymous)); } }publicasync Task UpdateAuthenticationState(UserSession userSession) { ClaimsPrincipal claimsPrincipal;if (userSession != null) {await _sessionStorage.SetAsync("UserSession", userSession);await _sessionStorage.SetAsync("Token", userSession.TokenText); claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim> {new Claim(ClaimTypes.Name, userSession.Username),new Claim(ClaimTypes.Role, userSession.UserRole),new Claim(ClaimTypes.NameIdentifier, userSession.UserId.ToString()) })); }else {await _sessionStorage.DeleteAsync("UserSession"); claimsPrincipal = _anonymous; } NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(claimsPrincipal))); } }
UserSession.cs
publicclass UserSession {publicint UserId { get; set; }publicstring Username { get; set; }publicstring UserRole { get; set; }publicstring Name { get; set; }publicstring TokenText { get; set; } }
LoginController:
[Route("api/[controller]/[action]")] [ApiController]publicclass ApiLoginController : ControllerBase {privatereadonly SqliteContext _sqlServerContext;privatereadonly IConfiguration _configuration;privatereadonly IUserService _userService;public ApiLoginController(SqliteContext sqlServerContext, IConfiguration configuration, IUserService userService) { _sqlServerContext = sqlServerContext; _configuration = configuration; _userService = userService; } [HttpPost] publicasync Task<IActionResult> LoginSystem([FromBody] UserLoginVM loginModel) {var user = await _sqlServerContext.Users.Include(x => x.RoleRefNavigation) .FirstOrDefaultAsync(x => x.Username == loginModel.Username && x.IsActive);if (user == null) {return BadRequest("Invalid credentials."); }if (!MatchPasswordHash(loginModel.Password, user.Password, user.SaltPassword)) {return BadRequest("Invalid credentials."); }if (!user.IsActive) {return StatusCode(403, "User is not active."); }if (user.IsLocked) { DateTime setDate = (DateTime)user.LockUntil; DateTime current = DateTime.Now;if (setDate > current) {return StatusCode(403, "User is restricted."); }await _userService.UnsetUserLimits(user.UserId); } user.RoleRefNavigation = await _sqlServerContext.Roles.FirstOrDefaultAsync(x => x.RoleId == user.RoleRef);string token = CreateToken(user);var data = new { tokenText = token, username = user.Username, userId = user.UserId.ToString(), name = user.Name, role = user.RoleRefNavigation.User_Role };await _userService.RegisterLoginTime(user.UserId);return Ok(data); }privatestring CreateToken(User user) { List<Claim> claims = new List<Claim>() {new Claim(ClaimTypes.NameIdentifier, user.Username),new Claim(ClaimTypes.Role, user.RoleRefNavigation.User_Role),new Claim(type: "UserId", value: user.UserId.ToString()) };var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration.GetSection("Jwt:Key").Value!));var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);var token = new JwtSecurityToken( claims: claims, issuer: _configuration["Jwt:Issuer"], audience: _configuration["Jwt:Issuer"], expires: DateTime.Now.AddHours(8), signingCredentials: creds );var jwt = new JwtSecurityTokenHandler().WriteToken(token);return jwt; }privatebool MatchPasswordHash(string passwordText, byte[] password, byte[] passwordKey) {using (var hmac = new HMACSHA512(passwordKey)) {var passwordHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(passwordText));for (int i = 0; i < passwordHash.Length; i++) {if (passwordHash[i] != password[i]) {returnfalse; } }returntrue; } } }
The problem is that when I check Context.User?.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value; in SignalR hub, Context.UserIdentifier is always null. How can I fix this?