Welcome to today’s post.
I will be discussing how to troubleshoot issues when implementing and testing JWT authentication in a .NET Core Web API service.
In a previous post I showed how to implement and test JWT authentication within our .NET Core Web API service.
Most of the details on how I obtain the JWT token and how we setup postman requests are shown in our post on JWT implementation.
Build and run your Web API within visual studio.
The web API will run under localhost as a blank page.
Open the POSTMAN utility.
Create an HTTP POST request to your API token provider.
For example:
http://localhost/BookLoan.Identity.API/api/Users/Authenticate
Obtain the JWT token from the JSON response.
Create a HTTP GET request to your Web API service:
http://localhost/BookLoan.Catalog.API/api/Book/List
Add an Authorization key to the Headers collection containing the JWT token.
Hit SEND.
Depending on the following responses we act accordingly:
Issue: No authentication header populated in Request.Headers
Below we see an empty request header during debugging:
To fix this issue we will need to enable the JWT middleware by adding:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).
to ConfigureServices() in startup.cs.
Issue: 401 Unauthorized : WWW-Authenticate: Bearer
For this issue we have two possibilities:
- The token is invalid or expired.
In this case we generate a new JWT bearer token and pass it into the HTTP GET or POST request Header collection.
2. The authorization attribute is misconfigured.
We will get the above error when we declare our web API method as follows:
[HttpGet("api/[controller]/List")]
[Authorize]
public async Task<List<BookViewModel>> List()
{
…
}
To rectify the authentication problem, we declare the method as follows:
[HttpGet("api/[controller]/List")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public async Task<List<BookViewModel>> List()
{
…
}
Another resolution is to ensure that the issuer signing key set in the startup.cs method ConfigureServices() is converted from Base 64. This is mentioned in the next issue.
Issue: www-authenticate -> Bearer error=”invalid_token”, error_description=”The signature is invalid”
To resolve, we check we have set the issuer signing key:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(
options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(
Convert.FromBase64String(
Configuration.GetSection("AppSettings:Secret").Value)),
ValidateIssuer = false,
ValidateAudience = false
};
});
If we have used a string encoding different from our JWT identity provider then the token will be invalid.
Issue: www-authenticate ->Bearer error=”invalid_token”, error_description=”The token is expired”
With this error we know what the problem is: our token has expired.
In this case we just obtain a new access token by calling our authentication method on our identity service.
To debug and parse errors from HTTP responses in your C# code here are some handy tips:
Add breakpoints to your client.SendAsync() and client.PostAsync() calls.
Add a watch for the property:
response.Headers.WwwAuthenticate.ToString()
Check the response result response.IsSuccessStatusCode
If response.IsSuccessStatusCode is false then parse out the error response and act accordingly.
The response string will be similar to:
"Bearer error=\"invalid_token\", error_description=\"The token is expired\""
Parsing out the error information can be achieved as follows:
if (response.IsSuccessStatusCode)
{
// process the results
. . .
}
else
{
// process the error
string errorString = response.Headers.WwwAuthenticate.ToString().Replace("Bearer","");
if (!errorString.StartsWith("{") || !errorString.EndsWith("}"))
errorString = "{ " + errorString + " }";
errorString = errorString.Replace("=", ":");
ApiErrorResponse apiErrorResponse =
JsonConvert.DeserializeObject<ApiErrorResponse>(errorString);
throw new AppException(apiErrorResponse.error_description);
}
Issue: Getting the 404 error code ‘Not Found’
There are two possible causes for this issue:
Firstly, check the request URI and ensure that it calls an existing API method.
Next, check the startup code in the API service.
In the ConfigureServices(IServiceCollection services) method look for the code block that defines the JWT authentication:
.AddJwtBearer(x =>
{
…
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
x.Audience = "http://localhost/";
x.Authority = "http://localhost/";
});
If the value for the Audience server (the client application server) specifies a server that is not identical to the server hosting the client application, then all Web API methods will be inaccessible to the client application. Alternatively, leaving out the Audience and setting ValidateAudience to false will not validate the server of the client application.
In a future post I will show how to utilize our JWT token within our Web API service.
That’s all for this post.
I hope you have found this useful and informative.
Andrew Halil is a blogger, author and software developer with expertise of many areas in the information technology industry including full-stack web and native cloud based development, test driven development and Devops.