| |

REST API 11 ๐ŸŒ Authentication Methods (API Keys, Basic Auth, Bearer Tokens)

โฑ๏ธ Estimated read: 12 min | |

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

MethodCredentialIdentifiesExpires
API keyStatic stringApplicationNo (manual rotation)
Basicusername:passwordUserNo (password change)
BearerToken from loginUser or appYes (short-lived)

Header Formats

MethodHeader
API keyX-API-Key: <key> or Authorization: ApiKey <key>
BasicAuthorization: Basic <base64(user:pass)>
BearerAuthorization: Bearer <token>

Security Requirements

MethodTLS RequiredStorageRevocation
API keyYesHashed, server-sideManual rotation
BasicYes (mandatory)Hashed passwordPassword change
BearerYes (mandatory)Hashed or JWTExpiry or revocation list

When to Use

SituationMethod
Server-to-server data APIAPI key
Internal tool, quick scriptBasic (over HTTPS)
User-delegated accessBearer (OAuth 2.0)
First-party SPA or mobileBearer (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

PitfallWhy It HappensFix
API key in URLConvenienceUse a header
Basic auth over HTTPTestingTLS is mandatory
JWT algorithm confusionAuto-detectionSpecify algorithms explicitly
Token in localStorageBrowser convenienceUse httpOnly cookies or memory
Key not rotatedManual processAutomate 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

ItemValue
API keyStatic string, identifies application
Basicusername:password Base64, identifies user
BearerToken from login, possession-based
HeaderAuthorization: <scheme> <credentials>
Missing credentials401 Unauthorized
TLSMandatory for all three
API key placementHeader, not query string
JWT verificationExplicit 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 Authorization header 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!