Passing tokens in headers is a common requirement in many web applications and APIs that require authentication and authorization. By passing a token in the header, the server can validate the request and allow access to protected resources and endpoints. In C#, there are a few different ways to add a token to the header of an HTTP request.
What is an Authorization Token?
An authorization token is a string of characters that acts as an identifier for a user or service. It is generated on the server and then passed to the client, which then passes it back to the server on subsequent requests. This allows the server to validate that the request is coming from an authorized source.
Some common examples of tokens include:
- JSON Web Tokens (JWT)
- OAuth access tokens
- Session IDs
These tokens contain information encoded into them such as the user ID, permissions, and expiration time. They are digitally signed to prevent tampering.
Benefits of Using Tokens
There are several benefits to using tokens for authentication instead of other mechanisms like cookies:
- Stateless – The server does not need to store session data for each client. The token contains all the necessary information to identify the user.
- More Secure – Tokens can be encrypted and digitally signed to prevent tampering.
- Easier Scaling – Stateless auth makes it easier to add more servers without worrying about session replication.
- Decoupled – The authorization logic is separate from session management.
- Standardized – Standard token formats like JWT are widely supported.
Methods for Passing a Token in C#
Here are some common ways to pass an authorization token in the header of an HTTP request in C#:
1. Add Header Directly to HttpClient
The HttpClient class allows you to set a default header value that will be sent with every request. You can set the Authorization header with the Bearer token when creating the HttpClient:
// Create HttpClient
HttpClient client = new HttpClient();
// Set default Authorization header
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
// Send request
HttpResponseMessage response = await client.GetAsync(url);
This is useful if you want to apply the same authorization token to all requests sent through this client instance.
2. Add Header to Individual HttpRequestMessage
You can also add the Authorization header to individual HttpRequestMessage objects when making each request:
// Create request message
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
// Add Authorization header
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token);
// Send request
HttpResponseMessage response = await client.SendAsync(request);
This is useful if you want to use different tokens for each request.
3. Use HttpClientHandler
The HttpClientHandler allows modifying requests before they are sent. You can add the Authorization token in the ServerCertificateCustomValidationCallback method:
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
{
message.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token);
return true;
};
HttpClient client = new HttpClient(handler);
// Send request
HttpResponseMessage response = await client.GetAsync(url);
This allows setting a default token that will apply to all requests, while keeping the HttpClient token-agnostic.
4. Use HttpClient Factory
If you are using the IHttpClientFactory to manage HttpClient instances, you can create a typed client with a delegating handler to add the token:
// Token handler
public class TokenHandler : DelegatingHandler
{
private string token;
public TokenHandler(string token)
{
this.token = token;
}
protected override Task SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
// Add token
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token);
// Call next handler
return base.SendAsync(request, cancellationToken);
}
}
// Register typed client
services.AddHttpClient("api")
.AddHttpMessageHandler(token);
This allows injecting the typed client where needed and the token will automatically be applied.
Passing the Token in POST Requests
When making POST, PUT, or PATCH requests, it is common to pass the token in the request body rather than the header. For example, when calling an API you would structure the request like:
POST /api/endpoint
{
"token": "TOKEN_VALUE",
"some": "payload"
}
To do this in C#, you need to serialize the token into the request content:
// Create request
var request = new HttpRequestMessage(HttpMethod.Post, url);
// Set request content
request.Content = new StringContent(JsonConvert.SerializeObject(new {
token = token,
some = "payload"
}), Encoding.UTF8, "application/json");
// Send request
var response = await client.SendAsync(request);
This will JSON serialize the payload with the token included.
Validating the Token in the API
On the server-side, the API will need to implement token validation logic to authenticate requests. Here are some typical steps:
- Extract the token from the header or request body
- Verify the token format and signature
- Decode the token and validate fields like issuer, expiry time, etc
- Lookup the user by the user id contained in the token
- Check that user has required permissions
Common ways to implement token validation in ASP.NET Core:
JWT Middleware
Use the JWT middleware to automatically validate the token and populate ClaimsPrincipal:
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseJwtBearerAuthentication(options => {
options.TokenValidationParameters = GetValidationParameters();
});
}
public TokenValidationParameters GetValidationParameters()
{
return new TokenValidationParameters {
// Configure parameters like issuer, audience, clock skew, etc
};
}
Custom Middleware
Implement custom middleware to decode and validate the token:
public class CustomJwtMiddleware
{
public async Task Invoke(HttpContext context)
{
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
if (token != null)
{
var principal = ValidateToken(token);
context.User = principal;
}
await _next(context);
}
private ClaimsPrincipal ValidateToken(string token)
{
// Implement token validation and decoding
}
}
Action Filters
Decode and validate token in an authorization filter:
public class JwtAuthorizationFilter : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var token = context.HttpContext.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
if (token != null)
{
var principal = ValidateToken(token);
context.HttpContext.User = principal;
}
}
}
The principal will then be available for authorization policies and identity checks.
Refreshing Expired Tokens
Tokens issued by authorization servers often have a short expiry time. Once expired, requests with that token will start failing.
To avoid interruption, the client can proactively refresh the token before it expires by making a request to the token endpoint:
// Request new access token
var refreshToken = GetSavedRefreshToken();
var request = new HttpRequestMessage(HttpMethod.Post, "https://auth.example/token");
request.Content = new FormUrlEncodedContent(new Dictionary {
["grant_type"] = "refresh_token",
["refresh_token"] = refreshToken
});
var response = await client.SendAsync(request);
// Extract and save new access token
var newToken = ExtractToken(response);
SaveToken(newToken);
The client should request a new access token in this way once the old token expires or is close to expiry.
To handle expiry seamlessly, the client could:
- Store the expiry time of the access token on retrieve
- Set a timer to refresh at a certain time before expiry (e.g. 5 minutes)
- Retry requests with a new access token if they fail due to expiry
Common Issues
Here are some common issues faced when passing tokens in headers and how to resolve them:
401 Unauthorized Response
A 401 error indicates the request was unauthorized, which generally means the token is invalid, expired or not being passed correctly.
Things to check:
- Verify the token value is correct
- Make sure the token is not expired and refresh if needed
- Confirm the token is passed in the correct Authorization header format
- Check the API accepts the given token type (e.g. Bearer)
403 Forbidden Response
A 403 forbidden error means the token is valid but the user does not have permission to access the requested resource.
To resolve, you need to:
- Check the user has the required roles/claims
- Confirm the scopes in the token allow access to the resource
- Request the token again with sufficient access scopes
No Authorization Header
If the token is not being passed at all, the API will not see an Authorization header.
Make sure to check:
- The header name is spelled correctly
- The header is being set on request messages
- The token value is resolving correctly
Invalid Token Format
If an incorrectly formatted token is passed, the API will fail to decode and validate it.
Verify:
- The token is generated correctly by the server
- Special characters are escaped properly
- The client is not accidentally corrupting the token format
Conclusion
Passing authentication and authorization tokens in HTTP headers is a common practice for securing web APIs and enabling client applications to access user data.
In C#, tokens can be added to requests using various approaches including setting default headers on HttpClient, adding headers to individual messages, using handlers and filters, and serializing tokens in request bodies.
The token should be validated on the server before allowing access to protected resources. Common validation steps include verifying the signature, decoding the claims, and checking permissions.
Following best practices around token usage and validation allows building robust, secure systems decoupled from session state.