Skip to main content

API Auth: OAuth 2.0 vs API Keys vs JWT 2026

·APIScout Team
Share:

API Auth: OAuth 2.0 vs API Keys vs JWT 2026

Every API needs authentication — but which method? API keys are simple. JWTs are stateless. OAuth 2.0 handles delegated access. mTLS secures service-to-service communication. The right choice depends on who's consuming your API and what level of security is appropriate.

TL;DR

  • API keys are the right default for developer-facing APIs — simple to implement, easy to revoke, per-key rate limiting and analytics included; store only the hash, never the plaintext key
  • JWTs are the right choice for user authentication in SPAs and microservices — stateless verification without a database lookup, but require careful handling of the alg field and revocation
  • OAuth 2.0 Authorization Code + PKCE is the correct flow for user-delegated access; Client Credentials is correct for machine-to-machine; never use Implicit flow in 2026
  • mTLS is the strongest machine authentication but requires PKI infrastructure — use it for zero-trust internal service networks or compliance-mandated environments
  • Most production APIs combine methods: API keys for external consumers, JWTs internally, OAuth for third-party integrations

The Four Methods

API Keys

What: A unique string passed in a header (X-API-Key: sk_live_abc123) or query parameter.

How it works: Server generates a key, client includes it in every request, server validates the key against a database.

Strengths:

  • Simplest to implement and use
  • Easy to rotate and revoke
  • Per-key rate limiting and usage tracking
  • Good for server-to-server communication

Weaknesses:

  • No user context — the key represents an application, not a user
  • If leaked, anyone can use it (no binding to a client)
  • Often sent in plaintext (must use HTTPS)
  • No built-in expiration (manual rotation required)
  • No scoping mechanism (all-or-nothing access)

Best for: Server-to-server APIs, public data APIs, developer-facing APIs (Stripe, OpenAI, Twilio all use API keys).

JWT (JSON Web Tokens)

What: A signed, base64-encoded token containing claims (user ID, roles, expiration).

How it works: Server issues a JWT after authentication. Client includes it in the Authorization: Bearer <token> header. Server validates the signature without a database lookup.

Strengths:

  • Stateless — no server-side session storage
  • Contains user context (claims: user ID, email, roles)
  • Verifiable without database lookup (signature check only)
  • Cross-service — any service with the public key can validate
  • Built-in expiration (exp claim)

Weaknesses:

  • Can't be revoked without additional infrastructure (blocklist)
  • Token size grows with claims (can be large)
  • If the signing key is compromised, all tokens are compromised
  • Short expiration + refresh token pattern adds complexity
  • Storing secrets in JWTs is dangerous (base64 is not encryption)

Best for: Microservices (cross-service identity), SPAs, mobile apps, any system where stateless verification matters.

OAuth 2.0

What: An authorization framework for delegated access. Users grant applications limited access to their resources without sharing passwords.

How it works: Client redirects user to authorization server → user consents → authorization server issues access token → client uses token to access resources.

Strengths:

  • Delegated access — users grant specific permissions (scopes)
  • Standard protocol — wide tooling and library support
  • Scope-based access control (read:users, write:orders)
  • Refresh tokens for long-lived sessions without re-authentication
  • Supports multiple grant types (authorization code, client credentials, PKCE)

Weaknesses:

  • Complex — multiple grant types, redirect flows, token management
  • Implementation errors create security vulnerabilities
  • Requires an authorization server (build or use Auth0/Clerk/etc.)
  • Redirect-based flows don't work for CLIs or IoT without device flow
  • Token management (refresh, revoke, storage) adds complexity

Best for: Third-party integrations ("Sign in with Google"), APIs where users grant access to their data, B2B SaaS with organization-level permissions.

mTLS (Mutual TLS)

What: Both client and server present TLS certificates, mutually authenticating each other.

How it works: Client has a certificate signed by a trusted CA. Server validates the client certificate during the TLS handshake before any application data is exchanged.

Strengths:

  • Strongest authentication — cryptographic identity at the transport layer
  • No tokens or keys in application layer — authentication happens before the request
  • Can't be phished or leaked (certificate is bound to the client)
  • Certificate rotation and revocation via standard PKI

Weaknesses:

  • Certificate management is complex (issuing, distributing, rotating)
  • Not suitable for browser/mobile clients
  • Requires PKI infrastructure
  • Debugging is harder (certificate errors are opaque)
  • Not developer-friendly for external consumers

Best for: Service-to-service in zero-trust architectures, financial/healthcare APIs, internal microservices with strict security requirements.


Comparison Table

CriteriaAPI KeysJWTOAuth 2.0mTLS
Complexity✅ Low⚠️ Medium❌ High❌ High
User context❌ No✅ Yes✅ Yes (via scopes)❌ No (cert identity)
Stateless❌ No (DB lookup)✅ Yes⚠️ Depends✅ Yes
Revocable✅ Easy❌ Hard (need blocklist)✅ Yes✅ Via CRL/OCSP
Delegated access❌ No❌ No✅ Yes (scopes)❌ No
Browser-friendly⚠️ Not ideal✅ Yes✅ Yes❌ No
Server-to-server✅ Yes✅ Yes✅ Client credentials✅ Best

Decision Flowchart

  1. Third-party access to user data? → OAuth 2.0
  2. Service-to-service (internal)? → mTLS or API keys (or OAuth client credentials)
  3. User authentication for your own app? → JWT (with refresh tokens)
  4. Developer-facing API? → API keys (with optional OAuth for user-context endpoints)
  5. Zero-trust microservices? → mTLS + JWT (mTLS at transport, JWT at application)

Common Combinations

Most production systems combine methods:

  • API key + JWT: API key identifies the application, JWT identifies the user
  • OAuth 2.0 + JWT: OAuth issues JWTs as access tokens
  • mTLS + API key: mTLS for transport security, API key for application-level identity
  • API key + webhook signatures: API key for requests, HMAC signatures for webhook verification

API Keys in Depth

The simplicity of API keys masks several implementation decisions that significantly affect security. The most important: never store API keys in plaintext.

Treat API keys like passwords. When a developer creates a key through your dashboard, generate a cryptographically random string (at minimum 32 bytes from a CSPRNG), show it to the user once, and store only a secure hash in your database. Use bcrypt or Argon2 with appropriate cost factors, or — for high-throughput key validation — use a fast hash like BLAKE3 with a random salt stored alongside the hash. When a request arrives with an API key, hash the provided key and compare it to the stored hash.

import crypto from 'crypto';

// Generate a new API key
function generateApiKey(prefix: string): { key: string; hash: string } {
  const rawKey = crypto.randomBytes(32).toString('base64url');
  const key = `${prefix}_${rawKey}`; // e.g., sk_live_abc123...
  const hash = crypto.createHash('sha256').update(key).digest('hex');
  return { key, hash }; // Show key to user, store hash in DB
}

// Validate an incoming key
async function validateApiKey(providedKey: string): Promise<ApiKey | null> {
  const hash = crypto.createHash('sha256').update(providedKey).digest('hex');
  return await db.apiKeys.findUnique({ where: { hash } });
}

The prefix pattern (Stripe uses sk_live_, pk_test_, etc.) serves two purposes: developers can immediately identify what type of key they're looking at, and secret scanning tools can use the prefix as a pattern to detect accidentally committed keys in git repositories. GitHub's secret scanning can alert on your key format if you register the pattern. This is free protection against one of the most common API security failures.

Scoped keys reduce the blast radius when a key is compromised. A read-only key that can only call GET endpoints cannot be used to delete or modify data. Administrative keys should be separate from standard API keys and rate-limited more aggressively. Key rotation should be non-disruptive: allow two active keys simultaneously with overlapping validity so consumers can rotate without downtime.

For comprehensive key management at scale, Unkey provides key generation, validation, rate limiting, and analytics as a managed service — worth considering if you're building a developer-facing API and don't want to build key management infrastructure.

OAuth 2.0 Flows in Practice

OAuth 2.0 defines four grant types, but in 2026 you only need to know three, and one of them (Implicit) you should avoid entirely.

Authorization Code + PKCE is the correct flow for any case where a user is present and grants access to their data. The flow: your app redirects the user to the authorization server with a code_challenge (a hash of a random verifier you generate locally), the user authenticates and consents, the authorization server redirects back to your app with a one-time code, your app exchanges the code for tokens by presenting the original code_verifier. PKCE (Proof Key for Code Exchange) prevents authorization code interception attacks — even if an attacker intercepts the code, they can't exchange it without the verifier. PKCE is required for public clients (SPAs, mobile apps) and recommended for all clients.

Client Credentials is the correct flow for machine-to-machine (M2M) communication. There's no user involved. Your service presents its client_id and client_secret to the authorization server and receives an access token directly. Use this for service-to-service API calls, scheduled jobs that call external APIs, and backend systems that need authorized access to resources. The token has a limited lifetime (typically 1 hour) and should be cached until near expiration rather than fetched on every request.

Device Flow handles the case where a device can't easily handle redirects — CLI tools, smart TVs, IoT devices. The device displays a user code and a URL. The user visits the URL on a different device (their phone or laptop), enters the code, and authorizes. The device polls the authorization server until the user completes authorization or the code expires. This is the correct approach for CLI tools that need user-delegated API access.

The difference between access tokens and refresh tokens is important to understand. Access tokens are short-lived (15 minutes to 1 hour) and are sent with every API request. They can't practically be revoked because any server with the public key can validate them without a database lookup. Refresh tokens are long-lived (days to months) and are used only to obtain new access tokens. They can be revoked, which is why they exist — when a user revokes access or signs out, the refresh token is invalidated. The short-lived access token will expire naturally.

JWT Security Pitfalls

JWTs have several well-documented vulnerabilities that appear in production systems with alarming frequency.

The alg: none attack is the most famous. Early JWT libraries honored an algorithm: none value in the token header, meaning no signature verification was performed. An attacker could forge any JWT by setting alg: none and omitting the signature. Fix: never accept alg: none, and always specify the expected algorithm explicitly when verifying — don't use the algorithm from the token header.

Algorithm confusion (HS256 vs RS256) is a related attack. RS256 uses an RSA key pair — the server signs with the private key and verifies with the public key. HS256 uses a symmetric secret — the same key signs and verifies. If a server accepts both algorithms and an attacker has the public key (which is meant to be public), they can sign a JWT with HS256 using the public key as the symmetric secret, and a vulnerable server may accept it. Fix: explicitly specify and enforce the expected algorithm.

The kid (Key ID) header allows key rotation — the token specifies which key was used to sign it, and the server looks up the key by ID to verify. This is the correct pattern for rotation. The vulnerability: if the kid value is used in a database query without sanitization (e.g., SELECT key FROM keys WHERE id = ${kid}), it's a SQL injection vector. Fix: validate kid against a known allowlist before using it.

JWT storage in the browser has two options: httpOnly cookies (not accessible to JavaScript, protected against XSS) and localStorage (accessible to JavaScript, vulnerable to XSS). httpOnly cookies are the correct choice for tokens that should survive page refresh. The tradeoff is CSRF vulnerability, which is mitigated by using SameSite cookies and CSRF tokens for state-modifying requests. For API authentication patterns involving JWTs, httpOnly cookies with short-lived access tokens and server-side refresh is the production-safe pattern.

JWT revocation is genuinely hard. The stateless nature of JWTs — no database lookup required — means a compromised or "logged out" token remains valid until it expires. Solutions: short expiration (15 minutes) with refresh tokens (revokable), maintain a Redis-based blocklist of revoked token IDs (reintroduces statefulness), or use Clerk/Auth0 which handle this complexity for you.

mTLS for Service-to-Service Auth

Mutual TLS takes standard TLS (where only the server presents a certificate) and extends it so both parties present certificates. From the application's perspective, authentication happens at the network layer before any HTTP request — there is no token to steal, no header to forge.

The practical use case is zero-trust internal networks, where the principle is "never trust, always verify" regardless of network location. In a zero-trust architecture, a service receiving a request from another internal service doesn't trust the source IP address. Instead, it requires the calling service to present a valid certificate. mTLS implements this at the transport layer automatically.

SPIFFE (Secure Production Identity Framework For Everyone) and its implementation SPIRE provide the infrastructure for assigning cryptographic identities to services. Each service gets a SPIFFE Verifiable Identity Document (SVID) — effectively an X.509 certificate identifying the workload. SPIRE handles certificate issuance, rotation, and revocation automatically. This eliminates the hardest part of mTLS in practice: managing certificate lifecycle.

For teams not ready for full SPIFFE/SPIRE deployment, service meshes like Istio and Linkerd implement mTLS automatically between mesh-enrolled services with minimal configuration. Enabling mTLS in Istio is a PeerAuthentication policy — the infrastructure handles certificate management, and your application code is unmodified.

Choosing the Right Method

ScenarioMethodReasoning
Developer-facing APIAPI keysSimple, familiar, per-key analytics
User auth in SPA/mobileJWTStateless, carries claims, portable
Third-party user delegationOAuth 2.0 Auth Code + PKCEStandard, user consents to scopes
M2M internal servicesOAuth 2.0 Client Credentials or API keysDepends on whether token rotation is required
Zero-trust internal networkmTLSTransport-layer identity, no token management
CLI toolsOAuth 2.0 Device FlowHandles non-browser authorization
SOC2 / financial compliancemTLS + audit loggingCryptographic identity required

For compliance contexts, SOC2 Type II and ISO 27001 generally prefer certificate-based authentication for internal service communication over shared secrets. API keys are acceptable for external consumers with proper rotation policies. OAuth 2.0 with short-lived tokens satisfies most auditor requirements for user authentication. If you're operating in a regulated industry, document your authentication mechanism choices and their security rationale — auditors want to see deliberate decisions, not defaults.


Implementing API authentication? Explore authentication APIs and tools on APIScout. Also see our API security checklist and building APIs with TypeScript for a complete security foundation.

The API Integration Checklist (Free PDF)

Step-by-step checklist: auth setup, rate limit handling, error codes, SDK evaluation, and pricing comparison for 50+ APIs. Used by 200+ developers.

Join 200+ developers. Unsubscribe in one click.