Bearer token authentication is the most common way to secure API requests, but implementing it correctly in production requires more than just slapping a token in a header. This guide walks through the complete bearer token lifecycle, shows you idiomatic implementation patterns across TypeScript, Python, and Go, and covers the production pitfalls that cause authentication failures.
You'll learn how bearer tokens flow through API requests, why they're different from API keys, common mistakes that break authentication in production, and a systematic approach to debugging when things go wrong.
Quick skim summary: This guide explains what bearer token authentication is, shows you how to use it with the Authorization
header, and provides code examples in several languages. We'll cover how the token lifecycle works, common mistakes to avoid in production, and a checklist for debugging when things go wrong.
What is bearer token authentication?
Bearer token authentication is an HTTP authentication scheme where API access is granted to whoever holds, or "bears," the token. You include this token in the Authorization
header of your request, prefixed with the word Bearer
and a single space, like Authorization: Bearer <your_token>
. Because this method is so common, it's critical to implement it securely, which always means transmitting it over HTTPS.
Unlike session-based cookies, bearer tokens are stateless. The server doesn't need to store any session information; instead, it validates the token on each request, making it highly scalable for modern APIs. This is different from a simple API key, as bearer tokens, especially JSON Web Tokens (JWTs), can contain claims or metadata about the user and their permissions.
Using a well-built client library simplifies this process. For example, SDKs created with the Stainless SDK generator can be configured with a single authToken
option, and they automatically handle formatting and attaching the correct Authorization
header to every API call for you.
How bearer tokens move through an API request
Understanding the token's journey is key to using it correctly. The lifecycle is a simple, repeating loop.
Obtain: The client application first obtains a bearer token, usually through an authentication flow like OAuth 2.0 where the user logs in.
Store: The client then needs to store this token securely. For a server-side application, this is often an environment variable; for a web browser, it might be in memory or a secure cookie.
Send: On every subsequent request to a protected API endpoint, the client includes the token in the
Authorization
header.Validate: The API server receives the request, extracts the token from the header, and validates it to ensure it's authentic and not expired.
Expire/Refresh: Tokens are typically short-lived. Once a token expires, the client must use a refresh token or re-authenticate to get a new one, starting the cycle over.
Here is what a raw API call looks like using fetch
in JavaScript:
An SDK abstracts this boilerplate away, letting you focus on the API's logic, not the auth implementation details—because your API isn't finished until the SDK ships.
Bearer token patterns for production APIs
While the underlying Authorization: Bearer ...
header is universal, idiomatic usage differs across programming languages. A good SDK generator provides a consistent, high-level pattern that feels natural in each language, so you don't have to worry about the low-level details, and integrating SDK snippets with API docs ensures developers see the right patterns for their language.
TypeScript patterns
In TypeScript and JavaScript, initializing the client is straightforward. You pass the token during instantiation, and it's often read from environment variables for security.
Python patterns
Python SDKs follow a similar pattern, supporting both synchronous and asynchronous clients. The token is passed as an argument to the client's constructor.
Go patterns
In Go, it's idiomatic to use functional options for configuration. This allows for a clean and extensible way to set the auth token and other client settings.
Common bearer token mistakes in production
Bearer tokens are powerful, but they come with responsibilities. Here are some common pitfalls developers encounter when deploying applications that use them.
Unsafe token transmission
The Mistake: Sending the token over an unencrypted HTTP connection. This exposes the token to anyone listening on the network, allowing them to impersonate the user.
The Fix: Always use HTTPS for any endpoint that handles authentication or transmits bearer tokens. Enforcing HTTP Strict Transport Security (HSTS) is also a best practice to prevent downgrade attacks.
Insecure storage patterns
The Mistake: Storing tokens in insecure locations. In a browser, this means using
localStorage
, which is vulnerable to Cross-Site Scripting (XSS) attacks. On a server, it means hardcoding tokens directly in the source code.The Fix: In browsers, store tokens in memory for the duration of a session or in a secure,
HttpOnly
cookie. On the server, always use environment variables or a dedicated secrets management service.
Token lifecycle failures
The Mistake: Using tokens that never expire or having no mechanism to refresh them. Long-lived tokens increase the risk if they are compromised, while a lack of a refresh mechanism leads to a poor user experience, forcing users to log in again frequently.
The Fix: Implement short-lived access tokens (e.g., 15-60 minutes) and a robust refresh token flow. Your client should be able to detect a 401 Unauthorized response, use the refresh token to get a new access token, and then retry the original request.
Why bearer tokens fail in production
When an authenticated request fails, debugging can be frustrating. The issue usually falls into one of a few categories.
Failure Category | Common Causes | How to Debug |
---|---|---|
Header Formatting | Missing | Use |
Server Validation | Invalid signature (for JWTs), expired token, clock skew between client and server, token intended for a different audience ( | Check your API server logs. A good API will log the specific reason for a 401 error. |
Client-Side Issues | Token is | Enable verbose logging in your SDK to inspect the exact request headers. Verify the token value just before the request is made. |
Header formatting issues
This is the most common and easiest problem to fix. The Authorization
header value must be Bearer
followed by a single space, followed by the token.
Authorization: Bearer my-token-123
(Correct)Authorization: Bearer my-token-123
(Incorrect, double space)Authorization: bearer my-token-123
(Incorrect, lowercase 'b')Authorization: my-token-123
(Incorrect, missing 'Bearer ' prefix)
A quick cURL
command can help you isolate the issue from your application code:curl -H "Authorization: Bearer your-token-here" https://api.example.com/v1/me
Server-side validation gaps
If the header is formatted correctly, the issue is likely on the server. For JWTs, validation failure can happen for many reasons:
Expired Signature: The
exp
claim in the token is in the past.Invalid Signature: The token was tampered with or signed with the wrong key.
Clock Skew: The server's clock is out of sync with the client's, causing valid tokens to appear expired or not yet valid.
Your first step should always be to check the API server's logs, which should provide a specific error message for why authentication failed.
Client-side debugging steps
If the server logs show no incoming request, the problem is on the client. First, confirm your code is actually including the token. Many SDKs, including those generated by Stainless, allow you to enable verbose logging to see the exact HTTP request being sent, headers and all. This helps you confirm that the Authorization
header is present and correctly formatted before it even leaves your machine.
Frequently asked questions about bearer tokens in production
How do I pass a bearer token in the authorization header?
You pass it in the Authorization
HTTP header with the scheme Bearer
followed by a space and then the token itself. For example: Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
.
Why does capitalization of Bearer matter?
While the official RFC 7235 specification states the scheme name is case-insensitive, many web frameworks and gateways implement a strict, case-sensitive check. To ensure compatibility, you should always use the capitalized Bearer
.
Is an API key the same as a bearer token?
No, they serve similar purposes but are conceptually different. An API key is typically a static, long-lived secret, whereas a bearer token is often short-lived, dynamically generated, and may contain user-specific permissions within it (as a JWT).
How do SDKs refresh expired tokens?
Most SDKs do not handle token refreshing automatically, as the logic is specific to your auth provider. However, good SDKs provide hooks or middleware where you can implement this logic yourself, such as a beforeRequest
function that checks token expiry and refreshes it if needed, using custom code that persists through regenerated code.
Where should I store bearer tokens in frontend apps?
For Single-Page Applications (SPAs), the safest place is in your application's memory. Avoid localStorage
or sessionStorage
as they are accessible via JavaScript and vulnerable to XSS attacks. If you must persist the token, use a secure, HttpOnly
cookie managed by your backend.
Ready to simplify authentication and more for your API users? After creating OpenAPI specs, you can generate high-quality, idiomatic SDKs from them in minutes. Get started for free.