Often, in our daily routine, we have to deal with secure APIs and use a BearerToken to make HTTP requests. In this article, we are going to learn the correct way to add a BearerToken to an HttpClient request.
To achieve this result, we are going to need two applications. A JWT secure User API and a Console Application to authenticate and consume the User API methods.
We have a lot to cover, so let’s start it.
VIDEO: Authorize HttpClient Requests With a Bearer Token.
Example Login Action – User WebApi
Our User API is very simple.
First, we have an Auth
controller containing a Login
action:
[HttpPost("login")] public IActionResult Login([FromBody] AuthenticateModel loginModel) { if (loginModel is null) { return BadRequest("Invalid client request."); } if(CheckRegisteredUser(loginModel)) { var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey@345")); var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256); var tokeOptions = new JwtSecurityToken( issuer: "https://localhost:5001", audience: "https://localhost:5001", claims: new List<Claim>(), expires: DateTime.Now.AddMinutes(10), signingCredentials: signinCredentials ); var token = new JwtSecurityTokenHandler().WriteToken(tokeOptions); return Ok(new { token }); } return Unauthorized(); }
We have an article about JWT Authentication if you want to learn more about how to create a JWT Authentication WebApi and its configurations.
Also, we have a User
controller with three routes secured with the Authorize attribute.
The first route, PUT /api/users
to insert a new user into the database. The GET /api/users
retrieves every user from the database and finally, a GET /api/users/{id}
returns a specific user.
As the focus of this article is to add a BearerToken to an HttpClient request, we are not going to lose time with the User WebApi’s implementation, however, we have the full User API implementation here.
Create The Consumer Application
Now that we have the User WebApi ready and protected, let’s create a new console app project using the Visual Studio project wizard (or using the dotnet new console
command) to consume this WebApi and see how we can add a BearerToken to an HttpClient request.
How to Correctly Use HttpClientFactory
As we describe in this article, it is preferred to use HttpClientFactory
instead of instantiating a new HttpClient
object every time.
To achieve it, let’s first create a LoginApiRepository
class:
public class LoginApiRepository : ILoginApiRepository { private readonly HttpClient _httpClient; public LoginApiRepository(HttpClient httpClient) { _httpClient = httpClient; } }
Once we know that this class is going to make HTTP requests, we create the _httpClient
property and initialize it with the HttpClient
instance we receive in the constructor. Let’s not forget to inject the HttpClient
instance using the HttpClientFactory
in the Startup
class and set up the BaseAddress
property:
services.AddHttpClient<ILoginApiRepository, LoginApiRepository>( c => c.BaseAddress = new Uri("https://localhost:5001/"));
User BearerToken Authentication
Now, let’s create an AuthenticateAsync()
method to retrieve the JWT BearerToken from the User API:
public async Task<AccessToken> AuthenticateAsync() { var token = RetrieveCachedToken(); if (!string.IsNullOrWhiteSpace(token)) return new() { Token = token }; var result = await _httpClient.PostAsync("api/auth/login", GenerateBody()); result.EnsureSuccessStatusCode(); var response = await result.Content.ReadAsStringAsync(); var deserializedToken = DeserializeResponse<AccessToken>(response); SetCacheToken(deserializedToken); return deserializedToken; }
In a real-world application, we should store the token in a cache service, then we just retrieve this token. In case we don’t have the token in a cache, we should make an HTTP Post request to the api/auth/login
route, passing as a parameter the user credentials, to retrieve the JWT BearerToken.
Once the result is successful, we deserialize the token, store it in the cache service and return it.
Add BearerToken to an HttpClient Request
Let’s learn two different ways to add a bearer token to an HTTP request.
HttpClient Authorization Header
The first method we can use to add a bearer token to an HTTP request is by adding a header to our HttpClient.
That said, let’s create a method to register a new user into the User WebApi:
public async Task CreateUserAsync(UserModel userModel, string token) { _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); var result = await _httpClient.PostAsync("api/users", GenerateBody(userModel)); result.EnsureSuccessStatusCode(); }
This method receives the UserModel
instance and the JWT BearerToken as parameters. Then, it sets the authorization header for the request by creating a new AuthenticationHeaderValue
object with the token provided as the parameter.
The next step consists of calling the PostAsync()
method to send a request to the api/users
route.
Finally, we call the EnsureSuccessStatusCode()
method on our result
to throw an exception if the HTTP request is not successful.
HttpClient DelegatingHandler
One way to elegantly add a BearerToken to an HttpClient request is to use a DelegatingHandler to intercept the request and insert the bearer token.
Let’s create a LoginHandler
class and inherit from the DelegatingHandler
class:
public class LoginHandler : DelegatingHandler { private readonly ILoginApiRepository _loginApiRepository; public LoginHandler(ILoginApiRepository loginApiRepository) { _loginApiRepository = loginApiRepository; } }
First, we create a _loginApiRepository
property and initialize it with the instance that is injected into the LoginHandler
constructor.
Then, let’s override the SendAsync()
method:
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var token = await _loginApiRepository.AuthenticateAsync(); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Token); return await base.SendAsync(request, cancellationToken); }
This method is responsible for intercepting every HTTP request and making some modifications to it. In this scenario, first, we call the AuthenticateAsync()
method to retrieve a JWT BearerToken from a cache service or from the User API if necessary.
Finally, we use the base.SendAsync()
method to resume the HTTP request flow.
But we aren’t finished yet, we still need to inject this handler into the repositories class we want to use this handler. Let’s use the Startup
class to configure it:
services.AddHttpClient<IUserApiRepository, UserApiRepository>() .AddHttpMessageHandler<LoginHandler>();
Send an Http Request
Now that we setup the DelegatingHandler
, let’s create a GetUserAsync()
method to make an HTTP request to the GetUserById()
method under the User API:
public async Task<UserModel> GetUserAsync(int userId) { var result = await _httpClient.GetAsync($"api/users/{userId}"); result.EnsureSuccessStatusCode(); var response = await result.Content.ReadAsStringAsync(); return DeserializeResult<UserModel>(response); }
First, we receiveuserId
of the specific user as a parameter.
Then we make an HTTP Get request to the api/users/{userId}
route. Once the result is successful, we store the content in a response
variable.
Finally, we deserialize the response
into a UserModel
instance and return it.
Note that, this time we don’t need to set the BearerToken in the header of the HTTP request because the DelegatingHandler
will do it.
Conclusion
In this article, we have created two applications. A secure User WebApi that requires authentication and a Console Application to authenticate and retrieve data from this WebApi.
We have learned how to properly inject the HttpClient
into repository classes using HttpClientFactory
, as well as two methods for adding a BearerToken to an HttpClient request. The first approach involves using DedefaultRequestHeaders
property of the HttpClient
instance, while the second approach involves using a DelegatingHandler
.