I am having a scenario which the Api must return an UnAuthorized result when login fails. However, serialization from await HttpClient.PostJsonAsync(url, credentials); causes a javascript error from the console which stops the login page from functioning as expected. Here are the bits that I have.
API Code:
[HttpPost("LoginV2")]
[SwaggerOperation(Tags = new[] { "Auth" })]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<IActionResult> LoginV2([FromBody] LoginDto login)
{
var result = await _signInManager.PasswordSignInAsync(login.Email, login.Password, false, false);
if (result.Succeeded)
{
var appUser = _userManager.Users.SingleOrDefault(r => r.UserName == login.Email);
var token = new TokenDto()
{
JwtToken = GenerateJwtToken(appUser),
RefreshToken = GenerateRefreshToken()
};
var appUserAuth = new IdentityWithTokenDto()
{
////......Omitted
};
return Ok(appUserAuth);
}
return Unauthorized();
}
The Blazor code:
async Task SubmitCredentials()
{
var result = await Client.PostJsonAsync<IdentityWithTokenDto>(url, credentials);
loginFailure = string.IsNullOrEmpty(result.Token.JwtToken);
if (!loginFailure)
{
await tokenAuthenticationStateProvider.SetTokenAsync(result.Token.JwtToken, DateTime.Now.AddDays(1));
Navigation.NavigateTo("/",true);
}
}
I can't figure it out how to handle the unauthorized result since the httpclient will handle it and the blazor javascript will throw an error to the console which stops the login form from working. Is this the proper behavior or any ways to properly handle it??
UPDATE: The answer below points me out to the resolution which is not to use PostAsJsonAsync<> on this scenario and manually convert the result.
Here's the updated code:
var response = await Client.PostAsJsonAsync(url, credentials);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsAsync<IdentityWithTokenDto>();
await tokenAuthenticationStateProvider.SetTokenAsync(data.Token.JwtToken, DateTime.Now.AddDays(1));
Navigation.NavigateTo("/", true);
}
else
{
//Update the UI
}
The first option is of course to change your Action method and return a valid Dto result with .Token.JwtToken == null
. Then your Blazor code can stay as is.
That would be my preferred way, the Login req itself is not 'UnAuthorized', it is just denied. The return Unauthorized();
here is very debatable.
If you still want to handle it on the client: you can't get at the response status now because you directly convert form Json. You will have to split that:
//var result = await Client.PostJsonAsync<IdentityWithTokenDto>(url, credentials);
var response = await Client.PostAsync(url, credentials);
if (response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync();
// decode Json with System.Text.Json
}
else
{
// handle errors / unauth
if (response.StatusCode == HttpStatusCode.Unauthorized) { ... }
}