Adding authentication headers to HttpClient
requests in C# is essential for securing your API calls. This guide covers the most common authentication methods with practical examples.
Bearer Token Authentication
Bearer tokens are commonly used with OAuth 2.0, JWT tokens, and many modern APIs.
Method 1: Using AuthenticationHeaderValue (Recommended)
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
public class ApiClient
{
private readonly HttpClient _httpClient;
public ApiClient()
{
_httpClient = new HttpClient();
// Set bearer token
string bearerToken = "your_access_token_here";
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", bearerToken);
}
public async Task<string> GetDataAsync()
{
try
{
HttpResponseMessage response = await _httpClient.GetAsync("https://api.example.com/data");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
catch (HttpRequestException ex)
{
Console.WriteLine($"Request failed: {ex.Message}");
throw;
}
}
public void Dispose()
{
_httpClient?.Dispose();
}
}
Method 2: Per-Request Authorization
using (var client = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.example.com/data");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "your_token");
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
}
Basic Authentication
Basic authentication encodes username and password in Base64 format.
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
public class BasicAuthClient
{
private readonly HttpClient _httpClient;
public BasicAuthClient(string username, string password)
{
_httpClient = new HttpClient();
// Create Basic auth header
string credentials = Convert.ToBase64String(
Encoding.ASCII.GetBytes($"{username}:{password}"));
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", credentials);
}
public async Task<string> GetProtectedDataAsync()
{
var response = await _httpClient.GetAsync("https://api.example.com/protected");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
// Usage
var client = new BasicAuthClient("myusername", "mypassword");
string data = await client.GetProtectedDataAsync();
API Key Authentication
Many APIs use custom headers for API keys.
using System.Net.Http;
using System.Threading.Tasks;
public class ApiKeyClient
{
private readonly HttpClient _httpClient;
public ApiKeyClient(string apiKey)
{
_httpClient = new HttpClient();
// Common API key header patterns
_httpClient.DefaultRequestHeaders.Add("X-API-Key", apiKey);
// or
// _httpClient.DefaultRequestHeaders.Add("Authorization", $"ApiKey {apiKey}");
// or
// _httpClient.DefaultRequestHeaders.Add("X-Auth-Token", apiKey);
}
public async Task<string> FetchDataAsync()
{
var response = await _httpClient.GetAsync("https://api.example.com/endpoint");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
JWT Token with Refresh Logic
For more complex scenarios with token refresh:
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
public class JwtApiClient
{
private readonly HttpClient _httpClient;
private string _accessToken;
private DateTime _tokenExpiry;
public JwtApiClient()
{
_httpClient = new HttpClient();
}
private async Task EnsureValidTokenAsync()
{
if (string.IsNullOrEmpty(_accessToken) || DateTime.UtcNow >= _tokenExpiry)
{
await RefreshTokenAsync();
}
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", _accessToken);
}
private async Task RefreshTokenAsync()
{
// Implement your token refresh logic here
var tokenResponse = await GetNewTokenAsync();
_accessToken = tokenResponse.AccessToken;
_tokenExpiry = DateTime.UtcNow.AddSeconds(tokenResponse.ExpiresIn - 60); // 60s buffer
}
public async Task<string> MakeAuthenticatedRequestAsync(string endpoint)
{
await EnsureValidTokenAsync();
var response = await _httpClient.GetAsync(endpoint);
// Handle 401 and retry once
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
await RefreshTokenAsync();
await EnsureValidTokenAsync();
response = await _httpClient.GetAsync(endpoint);
}
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
// Placeholder for your token acquisition logic
private async Task<TokenResponse> GetNewTokenAsync()
{
// Implement actual token acquisition
throw new NotImplementedException();
}
}
public class TokenResponse
{
public string AccessToken { get; set; }
public int ExpiresIn { get; set; }
}
Best Practices
1. HttpClient Lifecycle Management
// Use HttpClientFactory in ASP.NET Core
services.AddHttpClient<ApiClient>();
// Or use a singleton pattern for console apps
public sealed class HttpClientSingleton
{
private static readonly Lazy<HttpClient> _instance =
new Lazy<HttpClient>(() => new HttpClient());
public static HttpClient Instance => _instance.Value;
}
2. Secure Token Storage
// Never hardcode tokens in source code
string token = Environment.GetEnvironmentVariable("API_TOKEN");
// or use Azure Key Vault, AWS Secrets Manager, etc.
3. Error Handling
public async Task<ApiResponse<T>> SafeApiCallAsync<T>(string endpoint)
{
try
{
var response = await _httpClient.GetAsync(endpoint);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
var data = JsonSerializer.Deserialize<T>(content);
return new ApiResponse<T> { Success = true, Data = data };
}
return new ApiResponse<T>
{
Success = false,
Error = $"HTTP {response.StatusCode}: {response.ReasonPhrase}"
};
}
catch (HttpRequestException ex)
{
return new ApiResponse<T> { Success = false, Error = ex.Message };
}
catch (TaskCanceledException ex)
{
return new ApiResponse<T> { Success = false, Error = "Request timeout" };
}
}
4. Timeout Configuration
_httpClient.Timeout = TimeSpan.FromSeconds(30);
Summary
The key points for adding authentication headers to HttpClient:
- Bearer tokens: Use
AuthenticationHeaderValue("Bearer", token)
- Basic auth: Base64 encode
username:password
- API keys: Add to custom headers like
X-API-Key
- Reuse HttpClient: Create once, use multiple times
- Handle errors: Implement proper exception handling
- Secure storage: Never hardcode credentials
Choose the authentication method that matches your API's requirements and always handle authentication failures gracefully.