I'm currently troubleshooting a NullReferenceException
in a .NET Core API and Angular application, but I've hit a roadblock.
The specific issue arises when trying to update the "About" section of a User
.
Take a look at the text area screenshot for reference
Within the backend code, specifically in the AuthController
, the Login method is responsible for creating claims and appears to be functioning correctly:
[HttpPost("login")]
public async Task<IActionResult> Login(LoginViewModel loginViewModel)
{
// User login process
var userFromRepo = await _authRepository.Login(loginViewModel.Email.ToLower(), loginViewModel.Password);
// Ensure user is logged in
if (userFromRepo == null)
return Unauthorized();
// Generate claims using user id and main email
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, userFromRepo.Id),
new Claim(ClaimTypes.Name, userFromRepo.MainEmail),
new Claim(ClaimTypes.Name, userFromRepo.FirstName)
};
// Create key from secret token
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config.GetSection("AppSettings:Token").Value));
// Generate hash and credentials
var cred = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);
// Define token properties
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.Now.AddDays(1),
SigningCredentials = cred
};
// Initialize token handler
var tokenHandler = new JwtSecurityTokenHandler();
// Create token
var token = tokenHandler.CreateToken(tokenDescriptor);
// Emit token and respond to request
return Ok(new
{
token = tokenHandler.WriteToken(token),
});
}
There's also a method designed to modify a User property:
[HttpPut("{id}")]
public async Task<IActionResult> UpdateUser(string id, UserForUpdateDto userForUpdateDto)
{
// Validate if the user ID updating the profile matches the ID in the token
if (id != User.FindFirst(ClaimTypes.NameIdentifier).Value)
{
return Unauthorized();
}
var userFromRepo = await _doMoreRepo.GetUser(id);
userFromRepo.About = userForUpdateDto.About;
await _doMoreRepo.UpdateUser(id);
return NoContent();
}
While debugging, a 500 error occurs with the message:
System.NullReferenceException: Object reference not set to an instance of an object
This error originates in the line of code:
User.FindFirst(ClaimTypes.NameIdentifier).Value
Everything works as expected in Postman, leading me to suspect that I'm not fetching anything from NameIdentifier
, but I can't pinpoint why?
I'm running out of places to investigate further. Help!
In my Angular front end code, you'll find the following - the Login
method adds the token to storage upon authentication:
login(model: any) {
return this.http.post(this.url + 'login', model)
.pipe(
map((response: any) => {
const user = response;
if (user) {
localStorage.setItem('token', user.token);
this.decodedToken = this.jwtHelper.decodeToken(user.token);
console.log('This is decoded token');
console.log(this.decodedToken);
}
})
);
}
Profile update function:
updateProfile() {
this.userService.updateUser(this.authService.decodedToken.nameid, this.user).subscribe(next => {
this.alertify.success('Profile updated');
this.editForm.reset(this.user);
}, error => {
console.log(error);
this.alertify.error(error);
});
}
An issue on Stack Overflow was similarhere. I revisited the solution but it seems that my tokenGetter()
is included in my app.module.ts
:
export function tokenGetter() {
return localStorage.getItem('token');
}
along with the import:
JwtModule.forRoot({
config: {
tokenGetter,
}
})
To narrow down the problem area, I replaced the code snippet:
public async IActionResult UpdateUser(string id, UserForUpdateDto userForUpdateDto)
{
if (id != User.FindFirst(ClaimTypes.NameIdentifier).Value)
{
return Unauthorized();
}
var userFromRepo = await _doMoreRepo.GetUser(id);
userFromRepo.About = userForUpdateDto.About;
await _doMoreRepo.UpdateUser(id);
return NoContent();
}
with a hardcoded value like this:
public async IActionResult UpdateUser(string id, UserForUpdateDto userForUpdateDto)
{
if (id != "user ID value")
{
return Unauthorized();
}
var userFromRepo = await _doMoreRepo.GetUser(id);
userFromRepo.About = userForUpdateDto.About;
await _doMoreRepo.UpdateUser(id);
return NoContent();
}
The hardcoded value approach works smoothly without any errors, successfully updating the property.
What could I possibly be overlooking?
EDIT:
Here's my updateUser()
method where the http.put
request is carried out:
updateUser(id: string, user: User) {
// console.log('user ID is: ' + id);
// console.log('User object passed to updateUser() is: ');
// console.log(user);
return this.http.put(this.baseUrl + 'user/' + id, user);
}
This contacts the UpdateUser()
function in the UserController.cs
on the backend.