I have created a method where a user can log into my application and then a Token is sent to the user so it can be used to view any other views in the application.When i test the login method on postman i get the 500 server error as below instead of the token. I have seen similar questions/posts but haven't seen one that is related to JWT
Below is the output
System.ArgumentNullException: Value cannot be null. (Parameter 'value') at System.Security.Claims.Claim..ctor(String type, String value, String valueType, String issuer, String originalIssuer, ClaimsIdentity subject, String propertyKey, String propertyValue) at System.Security.Claims.Claim..ctor(String type, String value) at Coop_Marketing.Controllers.AccountController.Login(LoginViewModel formdata) in /Users/Ken/Desktop/Coop_Marketing/Coop_Marketing/Coop_Marketing/Controllers/AccountController.cs:line 101 at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|10_0(ControllerActionInvoker invoker , Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructu re.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
The Account controller with the login method is below
public async Task<IActionResult> Login([FromBody] LoginViewModel formdata)
{
// Get the User from database
var user = await _userManager.FindByNameAsync(formdata.Username);
var roles = await _userManager.GetRolesAsync(user);
var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_appSettings.Secret));
double tokenExpiryTime = Convert.ToDouble(_appSettings.ExpireTime);
if(user !=null && await _userManager.CheckPasswordAsync(user,formdata.Password))
{
var tokenhandler = new JwtSecurityTokenHandler();
// Describe the token used by the user to log in
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, formdata.Username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim(ClaimTypes.Role, roles.FirstOrDefault()),
new Claim("LoggedOn", DateTime.Now.ToString()),
}),
SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256Signature),
Issuer = _appSettings.Site,
Audience = _appSettings.Audience,
Expires = DateTime.UtcNow.AddMinutes(tokenExpiryTime)
};
// Generate the token
var token = tokenhandler.CreateToken(tokenDescriptor);
return Ok(new {token= tokenhandler.WriteToken(token), expiration = token.ValidTo,username = user.UserName,userRole = roles.FirstOrDefault()});
}
// Return error
ModelState.AddModelError("","Username/Password was not Found");
return Unauthorized(new { LoginError = "Please check the Login Credentials - Invalid Username/Password was entered" });
}
}
}
And the LoginViewModel
public class LoginViewModel
{
[Required]
[Display(Name = "User Name")]
public string Username { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
}
The problem is with > var tokenDescriptor = new SecurityTokenDescriptor as per line 101 in the code which is mentioned in the output. However,I have failed to figure out why I am getting the error.
So it is like Claim has a null parameter. for that, you found this issue.
Update your code like below:-
.....
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(JwtRegisteredClaimNames.NameId,formdata.UserName), //modified
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()), //modified
new Claim(ClaimTypes.Role, roles.FirstOrDefault()),
new Claim("LoggedOn", DateTime.Now.ToString()),
}),
.....
Hope it will resolve your issue.
As you can see in the official documentation , the constructor will throw an argument null exception when the claim type/value is null. So it seems you are passing a null value(s) to the Claim constructor when creating the Claim object array.
As @Jasen mentioned to make the debugging easier change the one-line Claims array creation just for debugging and put a breakpoint to identify which claim is the culprit.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.