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 download the source code for this article, you can visit our GitHub repository.

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.

Example Login Action – User WebApi

Our User AAPI 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("[email protected]"));
        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.

Wanna join Code Maze Team, help us produce more awesome .NET/C# content and get paid? >> JOIN US! <<

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.

Wanna join Code Maze Team, help us produce more awesome .NET/C# content and get paid? >> JOIN US! <<

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. 

Wanna join Code Maze Team, help us produce more awesome .NET/C# content and get paid? >> JOIN US! <<

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.