HTTP Authentication

How credentials are passed in HTTP requests - the Authorization header and its schemes

The Authorization header

HTTP has a built-in mechanism for passing credentials: the Authorization request header. Its format is always:

Authorization: <scheme> <credentials>

The scheme tells the server how to interpret what follows. The most common schemes are Basic and Bearer. The server reads the scheme first and then parses the credentials accordingly.

When a server requires authentication and the client hasn't provided it, it responds with 401 Unauthorized and a WWW-Authenticate header telling the client which scheme it expects:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"

# or for Basic auth:
WWW-Authenticate: Basic realm="Admin area"

The realmis just a label describing the protected area - it's shown to the user in Basic auth browser dialogs but is otherwise informational.

Basic auth

The oldest scheme. The client takes username:password, base64-encodes it, and sends it in the header on every request:

# "user:password" base64-encoded = "dXNlcjpwYXNzd29yZA=="
Authorization: Basic dXNlcjpwYXNzd29yZA==

Base64 is not encryption- it's encoding. Anyone who can read the header can decode it instantly. Basic auth is only safe over HTTPS, which ensures the header is encrypted in transit. Over plain HTTP it's equivalent to sending your password in plaintext.

Because credentials are sent on every request, there's no token to expire or revoke - to change access you change the password. This is a limitation.

When Basic auth is acceptable: internal tools, simple scripts calling your own APIs, or service-to-service calls within a trusted network where simplicity matters more than fine-grained control. Most modern public APIs have moved away from it.

Bearer tokens

The scheme used by OAuth 2.0 and JWT. "Bearer" means whoever holds this token is granted access - like cash, possession is the claim. No username, no password:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

The token was issued by an authentication server after credentials were verified. It carries identity and permissions inside it (if it's a JWT) or the server looks them up from a database (if it's an opaque token). Either way, the raw credentials never travel over the wire again after login.

Advantages over Basic auth: tokens can expire, be revoked, be scoped to specific permissions, and be issued to third parties without sharing the underlying credentials.

The risk: a leaked bearer token grants access immediately to whoever has it, with no further verification. Keep them out of URLs, logs, and client-side storage. HTTPS is non-negotiable.

API keys

API keys are not an official HTTP auth scheme - they're a convention. Different APIs put them in different places:

# As a Bearer token (most common for modern APIs)
Authorization: Bearer sk_live_abc123

# As a custom header (OpenAI, Stripe use this pattern too)
X-API-Key: sk_live_abc123

# As a query parameter (avoid - ends up in logs and browser history)
GET /api/data?api_key=sk_live_abc123

API keys are typically long-lived credentials issued to developers or services, not end users. They identify the calling application, not a person. A key might have scopes (read-only vs read-write) and can be revoked independently without changing any passwords.

Never put API keys in query parameters in production - they'll appear in server logs, browser history, and referrer headers. Header is always preferable.

Comparing the schemes

BasicBearerAPI Key
Credentials sent each request✓ (encoded)✗ (token only)✓ (key only)
Can expireUsually not
Can be revoked✗ (change password)
Scoped permissionsSometimes
Safe over HTTP
User vs serviceEitherUserService / developer

In practice

  • Calling a third-party API (Stripe, OpenAI, GitHub) - almost always Authorization: Bearer <api-key>.
  • OAuth flow - Authorization: Bearer <access-token> on every API call after the token exchange.
  • Your own Django REST API - DRF's TokenAuthentication uses Authorization: Token <token> (a custom scheme); JWTAuthentication uses Bearer.
  • Quick internal scripts - Basic auth is fine if it's over HTTPS and you control both ends.
  • Never query params for secrets in production.