REST API 11 ๐ Authentication Methods (API Keys, Basic Auth, Bearer Tokens)
Every API request that touches protected data must answer one question before it does anything else: who is making this request? Authentication is the mechanism that answers it. The three methods in this chapterโAPI keys, HTTP Basic authentication, and Bearer tokensโrepresent three different answers to that question, and they are not interchangeable. An API key identifies an application. Basic authentication sends a username and password on every request. A Bearer token carries an authorization grant that was issued after a separate authentication step. Choosing the wrong one is not a matter of taste; it is a matter of what the request actually needs to prove.
The HTTP protocol provides the framework for all three. The Authorization request header is the standard location for credentials, and its value begins with an authentication scheme nameโBasic, Bearer, or a custom schemeโfollowed by the credentials themselves . The server validates the credentials and, if they are valid, returns the resource. If they are missing or invalid, the server returns 401 Unauthorized with a WWW-Authenticate header describing the scheme it expects . The three methods differ in what goes after the scheme name and in what that value proves.
Key point: API keys identify an application and are simple but long-lived; Basic authentication sends username:password Base64-encoded on every request and is only safe over HTTPS; Bearer tokens are issued after authentication and grant access based on possession alone, so they must be short-lived and transmitted only over TLS.
Why three methods exist
The application-identity problem. A server-to-server integration needs to identify the calling application for rate limiting, usage metering, and billing. It does not need to know which human is behind the request, because there is no humanโthe request is automated. An API key is the minimal credential that answers this question. It is a long random string issued once, sent on every request, and tied to an account . The key does not expire by default, which is its weakness: a leaked key is valid until someone rotates it.
The credential-forwarding problem. Basic authentication is the oldest HTTP authentication scheme. It sends the username and password concatenated with a colon, Base64-encoded, in the Authorization header . Base64 is encoding, not encryption: anyone who captures the header can decode the credentials. This is why Basic authentication is only acceptable over HTTPS, and why it is described as “inherently not secure” in the Microsoft documentation . It is useful for internal tools, quick scripts, and admin endpoints where the simplicity of a username and password outweighs the need for token lifecycle management .
The delegation problem. A Bearer token is issued after a separate authentication stepโa login endpoint or an OAuth 2.0 flowโand then presented on subsequent requests. The name “Bearer” means possession is sufficient: anyone holding the token can use it, without proving possession of a cryptographic key . This is why the token must be protected like a password and transmitted only over TLS . The advantage is that the token is short-lived and decoupled from the user’s password, so a leak has a bounded blast radius .
The authorization problem. API keys provide authorization but not authentication in the strict sense. The Microsoft documentation states the distinction directly: an API key authorizes access to an endpoint, but it does not authenticate a user . An access token, by contrast, is issued after authentication and carries the identity of the user who granted it. For sensitive endpoints, the token is the safer choice.
a. API keys: the application identifier
An API key is a static string, typically generated once and stored server-side. It can be sent in a custom header, the Authorization header, a query parameter, or the request body, but the query parameter is the worst choice because it leaks into server logs, browser history, and referrer headers .
# Send the API key in a custom header
# The header name is a convention; X-API-Key is common
curl -H "X-API-Key: sk_live_abc123" \
https://api.example.com/v1/users
# Send the API key in the Authorization header with a custom scheme
# The scheme name is the API's choice
curl -H "Authorization: ApiKey sk_live_abc123" \
https://api.example.com/v1/users
The server’s validation is a constant-time comparison between the provided key and the stored key. A naive string comparison leaks timing information that an attacker can use to guess the key character by character, which is why the Offensive360 guide uses hmac.compare_digest in its example . The key should be stored hashed, not in plaintext, so that a database leak does not expose every key.
The limitation is the lack of expiry and scoping. A key is valid until it is rotated, and it typically grants whatever the account is provisioned for, with little per-request scoping . For a server-to-server data API, this is acceptable. For anything that acts on behalf of a user, it is not.
b. Basic authentication: the encoded credentials
Basic authentication sends the username and password on every request. The client concatenates them with a colon, Base64-encodes the result, and sends it in the Authorization header with the Basic scheme .
# Send Basic credentials
# The -u flag makes curl encode the username and password
# The header becomes: Authorization: Basic <base64(user:pass)>
curl -u alice:secret \
https://api.example.com/v1/profile
# The equivalent explicit header
# "alice:secret" base64-encoded is "YWxpY2U6c2VjcmV0"
curl -H "Authorization: Basic YWxpY2U6c2VjcmV0" \
https://api.example.com/v1/profile
The server decodes the header, splits on the colon, and verifies the password against the stored hash. The password should be stored with a modern hashing algorithmโbcrypt, scrypt, or Argon2โnever in plaintext or as an unsalted digest . The RFC 7617 security section warns that Basic authentication is vulnerable to spoofing by counterfeit servers, and that the credentials are sent on every request with no built-in expiry or revocation short of changing the password .
The scheme’s only appropriate use is over HTTPS, for internal tools or quick scripts where the simplicity outweighs the lifecycle management . For a public API, a token-based scheme is safer.
c. Bearer tokens: the possession-based credential
A Bearer token is a string presented in the Authorization header with the Bearer scheme. The token is issued after a login or an OAuth 2.0 flow, and it may be opaque (a random ID the server looks up) or a self-contained JWT .
# Send a Bearer token
# The token was obtained from a login endpoint
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
https://api.example.com/v1/orders
The RFC 6750 specification is explicit about the security requirements: confidentiality protection MUST be applied using TLS, and clients MUST validate the TLS certificate chain when making requests . A Bearer token must not be stored in cookies that can be sent in the clear . The token is a bearer credentialโpossession is sufficientโso a leaked token is as dangerous as a leaked password until it expires .
The JWT variant carries signed claims that the server can verify without a database lookup. The Offensive360 guide lists the common mistakes: allowing the none algorithm, not validating the exp, nbf, iss, and aud claims, and using a symmetric secret for tokens issued by a third party . The algorithm must be specified explicitly in the verification call, never auto-detected.
// Verify a JWT with an explicit algorithm
// The algorithms array prevents algorithm confusion attacks
const decoded = jwt.verify(token, secret, {
algorithms: ['HS256'],
});
// The exp, nbf, iss, and aud claims are validated by the library
The token’s short lifetime is the mitigation for the possession risk. A stolen access token typically expires in minutes, which limits the attacker’s window . The refresh token, which is longer-lived, is the credential that must be protected most carefully.
Complete Example Session
The following session demonstrates all three methods against the same API, showing the header format and the server’s response.
# ============================================
# PART 1: API KEY AUTHENTICATION
# ============================================
# Send the key in a custom header
# The server looks up the key and identifies the application
curl -H "X-API-Key: sk_live_abc123" \
https://api.example.com/v1/data
# Response: 200 OK with the data
# The key does not expire; rotation is manual
# ============================================
# PART 2: BASIC AUTHENTICATION
# ============================================
# Send the username and password
# curl encodes them as Base64
curl -u alice:secret \
https://api.example.com/v1/profile
# Response: 200 OK with the profile
# The password is sent on every request
# ============================================
# PART 3: BEARER TOKEN AUTHENTICATION
# ============================================
# First, obtain the token from the login endpoint
curl -X POST https://api.example.com/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "alice", "password": "secret"}'
# Response: {"access_token": "eyJhbGci...", "token_type": "Bearer"}
# Then send the token on subsequent requests
curl -H "Authorization: Bearer eyJhbGci..." \
https://api.example.com/v1/orders
# Response: 200 OK with the orders
# The token expires after a short period
The three parts cover the API key, Basic, and Bearer flows. What the session demonstrates is that the API key is a static string sent on every request, Basic sends the password on every request, and Bearer requires a separate login step and a short-lived token.
Quick Reference
Method Comparison
| Method | Credential | Identifies | Expires |
|---|---|---|---|
| API key | Static string | Application | No (manual rotation) |
| Basic | username:password | User | No (password change) |
| Bearer | Token from login | User or app | Yes (short-lived) |
Header Formats
| Method | Header |
|---|---|
| API key | X-API-Key: <key> or Authorization: ApiKey <key> |
| Basic | Authorization: Basic <base64(user:pass)> |
| Bearer | Authorization: Bearer <token> |
Security Requirements
| Method | TLS Required | Storage | Revocation |
|---|---|---|---|
| API key | Yes | Hashed, server-side | Manual rotation |
| Basic | Yes (mandatory) | Hashed password | Password change |
| Bearer | Yes (mandatory) | Hashed or JWT | Expiry or revocation list |
When to Use
| Situation | Method |
|---|---|
| Server-to-server data API | API key |
| Internal tool, quick script | Basic (over HTTPS) |
| User-delegated access | Bearer (OAuth 2.0) |
| First-party SPA or mobile | Bearer (JWT) |
Best Practices
โ Do This:
# Use HTTPS for every authenticated request
curl https://api.example.com/...
# Send API keys in a header, not a query string
curl -H "X-API-Key: ..." https://...
# Use Bearer tokens for user-scoped access
curl -H "Authorization: Bearer ..." https://...
โ Don’t Do This:
# Send credentials over plain HTTP
curl http://api.example.com/...
# Put an API key in the URL
curl "https://api.example.com/data?api_key=..."
# Accept a JWT with alg=none
jwt.verify(token, secret, { algorithms: ['none'] })
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| API key in URL | Convenience | Use a header |
| Basic auth over HTTP | Testing | TLS is mandatory |
| JWT algorithm confusion | Auto-detection | Specify algorithms explicitly |
| Token in localStorage | Browser convenience | Use httpOnly cookies or memory |
| Key not rotated | Manual process | Automate rotation |
Real-World Examples
1. API key header
curl -H "X-API-Key: sk_live_abc" https://api.example.com/data
2. Basic auth with curl
curl -u alice:secret https://api.example.com/profile
3. Bearer token header
curl -H "Authorization: Bearer eyJ..." https://api.example.com/orders
4. Login to get a token
curl -X POST https://api.example.com/auth/login -d '{"username":"alice","password":"secret"}'
5. JWT verification with explicit algorithm
jwt.verify(token, secret, { algorithms: ['HS256'] })
6. Constant-time API key comparison
hmac.compare_digest(api_key, os.environ['VALID_API_KEY'])
7. Basic auth password hashing
bcrypt.hashpw(password, bcrypt.gensalt())
8. Bearer token over TLS
curl --tlsv1.3 -H "Authorization: Bearer ..." https://...
9. API key in environment variable
api_key = os.environ['API_KEY']
10. Token expiry check
jwt.verify(token, secret, { algorithms: ['HS256'], clockTolerance: 5 })
Visual
The Authorization Header
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Authorization: <scheme> <credentials> โ
โ โ
โ Basic โ base64(username:password) โ
โ Bearer โ token (opaque or JWT) โ
โ ApiKey โ static string (custom scheme) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
API Key vs Bearer Token
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ API KEY โ
โ โ static, long-lived โ
โ โ identifies application โ
โ โ no separate login โ
โ โ
โ BEARER TOKEN โ
โ โ short-lived, issued after login โ
โ โ identifies user or app โ
โ โ possession is sufficient โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
TLS Requirement
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Basic: Base64 is encoding, not encryption โ
โ โ HTTPS is mandatory โ
โ โ
โ Bearer: possession is sufficient โ
โ โ HTTPS is mandatory [RFC 6750] โ
โ โ
โ API key: static secret โ
โ โ HTTPS is mandatory โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| API key | Static string, identifies application |
| Basic | username:password Base64, identifies user |
| Bearer | Token from login, possession-based |
| Header | Authorization: <scheme> <credentials> |
| Missing credentials | 401 Unauthorized |
| TLS | Mandatory for all three |
| API key placement | Header, not query string |
| JWT verification | Explicit algorithms array |
Key takeaways:
- The three methods answer different questions. An API key identifies an application. Basic authentication sends a user’s password on every request. A Bearer token carries an authorization grant issued after authentication .
- Base64 is not encryption. Basic authentication encodes the credentials but does not protect them. HTTPS is the only thing that prevents an interceptor from reading them .
- Bearer tokens are possession-based. Anyone holding the token can use it, which is why the RFC requires TLS and why the tokens are short-lived .
- API keys do not expire by default. A leaked key is valid until it is rotated, which makes rotation a manual discipline rather than an automatic one .
- The
Authorizationheader is the standard location. API keys can go in a custom header, but the query string is a leak vector and should be avoided . - JWT verification must specify the algorithm. Allowing the library to auto-detect opens the door to algorithm confusion attacks .
Remember: Authentication is the first gate of every API request, and the three methods in this chapter are not interchangeable. Use an API key when the caller is an application and the access is coarse-grained. Use Basic authentication only over HTTPS and only when the simplicity is worth the lack of lifecycle management. Use Bearer tokens when the access is user-scoped and the credential needs to expire. The Authorization header is the standard location for all three, and the 401 Unauthorized response with a WWW-Authenticate header is the standard rejection. The method you choose is determined by what the request needs to prove, not by which is easiest to implement.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!