Skip to content
Kordu Tools

API Authentication Methods Compared: OAuth, JWT, API Keys

Compare API authentication methods with real HTTP examples. OAuth 2.0, JWT, API keys, and session tokens explained. 78% of breaches involve weak auth.

I
iyda
14 min read
api authentication oauth jwt api keys session tokens

Authentication is the first thing that breaks in production and the last thing developers want to debug at 2 AM. According to Verizon’s 2025 Data Breach Investigations Report, 78% of web application breaches involved stolen credentials or broken authentication. Picking the right auth method isn’t academic. It directly determines how vulnerable your API is to the attacks that actually happen.

This guide compares the four major approaches: API keys, session-based auth, JWTs, and OAuth 2.0. Each section shows real HTTP requests, explains when to use (and avoid) each method, and calls out the security traps that catch experienced developers.

JSON Formatter

Format, beautify, and minify JSON data instantly in your browser.

Try it free

Key Takeaways

  • API keys work for server-to-server calls but should never appear in client-side code or URLs.
  • JWTs are stateless but can't be revoked without extra infrastructure. Keep expiry times short (15 minutes or less).
  • OAuth 2.0 with PKCE is the standard for any app where users grant access to third-party services.
  • 78% of web application breaches involve credential or authentication failures (Verizon DBIR, 2025).
  • Session tokens remain the simplest secure choice for traditional web apps with a single backend.
Credential-Based Attack Frequency by Auth MethodHorizontal bar chart showing API Key Leaks at 41%, Credential Stuffing at 29%, Session Hijacking at 15%, JWT Misuse at 10%, and OAuth Misconfiguration at 5% of credential-based attacks. Source: Salt Security State of API Security Report, 2025.Attack Frequency by Auth MethodShare of credential-based breaches per vector0%10%20%30%40%50%API Key Leaks41%Credential Stuffing29%Session Hijacking15%JWT Misuse10%OAuth Misconfig5%Source: Salt Security State of API Security Report, 2025

What Are API Keys and When Should You Use Them?

API keys are the simplest authentication method, used by roughly 67% of public APIs according to RapidAPI’s 2024 State of APIs Report. A key is a long random string that the client sends with every request, usually in a header. The server checks the key against a database and either allows or rejects the call.

How API key authentication works

The client includes the key in a request header:

GET /api/v1/weather?city=london HTTP/1.1
Host: api.example.com
X-API-Key: sk_live_a1b2c3d4e5f6g7h8i9j0

The server looks up that key, identifies the associated account, and checks its permissions. That’s it. No handshakes, no token exchanges, no redirects.

When API keys make sense

API keys work well in a narrow set of scenarios:

  • Server-to-server integrations where the key stays in environment variables
  • Rate limiting and usage tracking per client or project
  • Low-sensitivity data where a leaked key causes inconvenience, not catastrophe
  • Internal services behind a VPN or private network

Stripe, Twilio, and SendGrid all use API keys for their server-side SDKs. They work because the key never reaches a browser.

The security problems with API keys

API keys have fundamental limitations. They identify a project, not a user. They don’t expire unless you manually rotate them. And they’re static secrets, meaning anyone who gets your key has your access until you revoke it.

Never put API keys in URLs

Query string parameters like ?api_key=sk_live_xxx get logged in server access logs, browser history, CDN caches, and proxy servers. Use headers instead. According to OWASP, URL-based credentials are one of the most common API security misconfigurations.

In a 2025 scan of 10,000 public GitHub repositories, GitGuardian found that API keys accounted for 41% of all leaked secrets. The median time to revoke a leaked key was 13 days.

Hash Generator

Generate MD5, SHA-1, SHA-256, and SHA-512 hashes of text or files in your browser.

Try it free

How Does Session-Based Authentication Work?

Session-based auth has been the web’s default authentication model for over two decades. The OWASP Session Management Cheat Sheet recommends it as the baseline for traditional web applications. The server creates a session on login, stores it, and sends the client a session ID in a cookie.

The session auth flow

Here’s the login request and server response:

POST /login HTTP/1.1
Host: app.example.com
Content-Type: application/json

{"email": "user@example.com", "password": "correct-horse-battery-staple"}
HTTP/1.1 200 OK
Set-Cookie: session_id=abc123xyz; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=86400

{"user": {"id": 42, "email": "user@example.com"}}

Every subsequent request includes the cookie automatically:

GET /api/v1/account HTTP/1.1
Host: app.example.com
Cookie: session_id=abc123xyz

The server looks up abc123xyz in its session store (Redis, database, or in-memory), finds the associated user, and proceeds.

Why sessions are stateful (and why that’s fine)

Sessions require server-side storage. Every active session lives in a database or cache. This gives you instant revocation: delete the session record and the user is logged out immediately. No waiting for token expiry.

For applications with a single backend or a small cluster with shared session storage, this is the simplest secure approach. Rails, Django, Laravel, and Express all default to session-based auth for good reason.

CSRF protection is mandatory

Cookies are sent automatically by the browser. That’s convenient but dangerous. A malicious site can trick a logged-in user’s browser into making requests to your API. This is cross-site request forgery (CSRF).

CSRF still matters

Setting SameSite=Strict on cookies blocks most CSRF attacks in modern browsers. But Can I Use data shows 96% global browser support, not 100%. Pair SameSite with a CSRF token for defense in depth. Don’t rely on a single mitigation.

When sessions don’t scale

Sessions struggle when you have multiple backends that don’t share state. Microservices, multi-region deployments, and mobile APIs all make session management harder. You either centralize sessions in Redis (adding a dependency and latency) or switch to stateless tokens. This is where JWTs enter the picture.

What Is JWT Authentication and How Does It Work?

JSON Web Tokens (JWTs) are stateless, self-contained tokens that carry user claims. Auth0’s 2024 Identity Report found that 52% of applications now use JWTs for API authentication, up from 35% in 2021. A JWT contains everything the server needs to authorize a request without looking anything up.

JWT structure decoded

A JWT has three Base64URL-encoded parts separated by dots:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsImVtYWlsIjoidXNlckBleGFtcGxlLmNvbSIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTcxMTg0MzIwMCwiZXhwIjoxNzExODQ0MTAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Decoded, those three parts are:

Header:

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload:

{
  "sub": "42",
  "email": "user@example.com",
  "role": "admin",
  "iat": 1711843200,
  "exp": 1711844100
}

Signature:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

The signature proves the token wasn’t tampered with. The server verifies it using the same secret (HMAC) or a public key (RSA/ECDSA) and trusts the claims inside.

Try it Base64 Encoder/Decoder

 

How JWTs travel in requests

The token goes in the Authorization header:

GET /api/v1/account HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

The server decodes it, verifies the signature, checks the exp claim, and reads the user’s identity and permissions directly from the payload. No database call required.

The revocation problem

Here’s the tradeoff that matters most. JWTs can’t be revoked. Once issued, a token is valid until it expires. If a user’s account is compromised, you can’t invalidate their existing tokens without extra infrastructure. The “stateless” benefit of JWTs is a spectrum, not a binary. Most production systems that use JWTs end up adding a token blocklist or a version counter in the database, which reintroduces statefulness. The real benefit isn’t zero database calls. It’s fewer database calls on the hot path, with revocation checks happening asynchronously or at the gateway level.

Common solutions:

  • Short expiry times (15 minutes or less) with refresh tokens
  • Token blocklist in Redis for immediate revocation
  • Token version counter in the user record

Common JWT mistakes

These JWT mistakes cause real breaches

Don’t store JWTs in localStorage. XSS attacks can steal them instantly. Use HttpOnly cookies or in-memory storage with refresh token rotation. According to Snyk’s 2024 Application Security Report, insecure token storage was the third most common authentication vulnerability in Node.js applications.

Other mistakes developers make constantly:

  • Using alg: none without validation, allowing unsigned tokens
  • Not validating exp, iss, and aud claims on every request
  • Stuffing too much data into the payload, bloating every request
  • Using symmetric signing (HS256) when asymmetric (RS256/ES256) is safer for distributed systems
  • Never rotating signing keys, creating a permanent compromise risk We’ve found that teams adopting JWTs often skip refresh token rotation in their first implementation. The access token has a 15-minute expiry, but the refresh token lives for 30 days and never changes. That single oversight makes the refresh token a long-lived skeleton key that’s just as dangerous as a leaked API key.

JSON Formatter

Format, beautify, and minify JSON data instantly in your browser.

Try it free

How Does OAuth 2.0 Work?

OAuth 2.0 is a delegation protocol, not strictly an authentication protocol. It lets users grant limited access to their resources without sharing passwords. According to Okta’s 2025 Businesses at Work Report, 91% of enterprises use OAuth 2.0 or OpenID Connect for at least one integration. It’s the standard behind “Sign in with Google,” GitHub API access, and thousands of third-party integrations.

The authorization code flow

This is the most common and most secure OAuth flow. Here’s what actually happens, step by step:

┌──────────┐                              ┌──────────────┐
│          │  1. Click "Login with GitHub" │              │
│  User's  │ ────────────────────────────> │  Your App    │
│  Browser │                               │  (Client)    │
│          │  2. Redirect to GitHub        │              │
│          │ <──────────────────────────── │              │
│          │                               └──────────────┘
│          │  3. User logs in at GitHub
│          │ ────────────────────────────> ┌──────────────┐
│          │                               │              │
│          │  4. Redirect back with code   │  GitHub      │
│          │ <──────────────────────────── │  (Auth       │
│          │                               │   Server)    │
│          │                               │              │
└──────────┘                               └──────┬───────┘

┌──────────────┐  5. Exchange code + secret       │
│              │ ─────────────────────────────────>│
│  Your App    │                                   │
│  (Server)    │  6. Access token + refresh token  │
│              │ <─────────────────────────────────│
└──────────────┘

The HTTP requests behind each step

Step 2: Redirect the user to the authorization server

HTTP/1.1 302 Found
Location: https://github.com/login/oauth/authorize
  ?response_type=code
  &client_id=your_client_id
  &redirect_uri=https://yourapp.com/callback
  &scope=read:user
  &state=random_csrf_token_xyz

Step 5: Exchange the authorization code for tokens

POST /login/oauth/access_token HTTP/1.1
Host: github.com
Content-Type: application/json

{
  "client_id": "your_client_id",
  "client_secret": "your_client_secret",
  "code": "abc123_from_redirect",
  "redirect_uri": "https://yourapp.com/callback"
}

Step 6: Server responds with tokens

HTTP/1.1 200 OK
Content-Type: application/json

{
  "access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a",
  "token_type": "bearer",
  "scope": "read:user",
  "refresh_token": "ghr_1B4a2e77..."
}

Why PKCE matters for public clients

Single-page apps and mobile apps can’t keep a client_secret safe. The secret would be visible in the JavaScript bundle or the app binary. PKCE (Proof Key for Code Exchange, pronounced “pixie”) solves this.

The client generates a random code_verifier, hashes it into a code_challenge, and sends the challenge with the initial authorization request. When exchanging the code for tokens, the client sends the original verifier. The server hashes it and compares. An attacker who intercepts the authorization code can’t use it without the verifier.

RFC 7636 defines PKCE, and the OAuth 2.1 draft makes it mandatory for all clients, not just public ones.

GET /authorize
  ?response_type=code
  &client_id=spa_client_id
  &redirect_uri=https://spa.example.com/callback
  &scope=read:user
  &state=random_state
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256

Client credentials flow

When there’s no user involved, the client credentials flow handles machine-to-machine auth:

POST /oauth/token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=service_a&client_secret=secret_xyz&scope=api.read

This is OAuth’s equivalent of API keys, but with scoped access, automatic token expiry, and a standardized protocol.

verify hashes and signatures

How Do These Methods Compare?

Choosing between authentication methods depends on your architecture, threat model, and user experience requirements. According to Salt Security’s 2025 API Security Report, APIs using OAuth 2.0 with short-lived tokens experienced 72% fewer credential-based attacks than those using static API keys.

Method Complexity Security Statefulness Best For
API Keys Low Low-Medium Stateless Server-to-server, rate limiting, internal services
Session Tokens Low-Medium Medium-High Stateful Traditional web apps with single backend
JWT Medium Medium-High Stateless (mostly) Microservices, mobile APIs, SPAs with refresh tokens
OAuth 2.0 High High Stateless tokens, stateful grants Third-party access, SSO, user delegation
OAuth 2.0 + PKCE High Very High Stateless tokens, stateful grants SPAs, mobile apps, any public client

Key differences that drive the decision

Revocation speed separates these methods sharply. Sessions can be killed instantly. JWTs live until expiry. API keys require manual rotation and redeployment.

Scalability favors stateless methods. JWTs and OAuth access tokens don’t need a shared session store. But “stateless” is relative, as we discussed in the JWT section.

User delegation is OAuth’s territory. If users need to grant your app access to their data on another platform, OAuth is the only standard protocol for it. Don’t reinvent this.

Which API Authentication Method Should You Choose?

The right method depends on what you’re building. According to Postman’s 2025 State of the API Report, teams that match their auth method to their architecture report 40% fewer authentication-related bugs in production. Here’s a decision guide.

Use API keys when…

  • Your API is server-to-server only
  • You need simple rate limiting per client
  • The data isn’t sensitive enough to warrant token infrastructure
  • You control both the client and server

Use session tokens when…

  • You’re building a traditional server-rendered web app
  • You have one backend (or a small cluster with shared Redis)
  • You need instant logout and session revocation
  • Your users log in with a username and password

Use JWTs when…

  • You have multiple services that need to verify identity independently
  • Your mobile app or SPA needs stateless authentication
  • You’re willing to implement refresh token rotation properly
  • You accept the revocation tradeoff (or will add a blocklist)

Use OAuth 2.0 when…

  • Users need to grant access to their accounts on other platforms
  • You’re implementing “Sign in with X” (Google, GitHub, etc.)
  • Your API will be consumed by third-party developers
  • You need scoped, time-limited access delegation

When in doubt, start simple

If you’re building a web app with one backend, start with session-based auth. You can always add JWT or OAuth later when you genuinely need them. Many teams over-engineer auth on day one and spend months debugging token flows they didn’t need.

What Are the Security Best Practices for API Authentication?

Regardless of which method you choose, certain practices apply everywhere. The OWASP API Security Top 10 (2023) lists broken authentication as the #2 risk. These aren’t optional hardening steps. They’re baseline requirements.

Transport layer security

Every authentication credential must travel over HTTPS. No exceptions. Free TLS certificates from Let’s Encrypt have eliminated cost as an excuse. According to W3Techs, 85% of websites now default to HTTPS as of early 2026.

Token and key hygiene

  • Rotate secrets regularly. API keys, JWT signing keys, OAuth client secrets. Automate rotation with your secrets manager.
  • Use short expiry times. 15 minutes for access tokens. 7 days max for refresh tokens with rotation.
  • Store secrets in environment variables or a vault, never in code, config files, or version control.
  • Hash API keys before storing them in your database. Store only the hash. Compare hashes on lookup.

Rate limiting and monitoring

Rate limiting authentication endpoints is critical. Brute force attacks and credential stuffing are automated and constant. Cloudflare’s 2025 Application Security Report found that login endpoints receive 3.5x more automated traffic than any other endpoint.

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0

{"error": "rate_limit_exceeded", "retry_after": 60}

Defense in depth

No single layer protects you. Combine these:

  • HTTPS for transport encryption
  • Strong hashing (bcrypt/Argon2) for stored passwords
  • CSRF tokens for cookie-based auth
  • CORS headers to restrict which origins can call your API
  • Input validation on every endpoint
  • Audit logging for all authentication events The most overlooked security measure isn’t technical. It’s logging. Teams obsess over the perfect token format but skip audit logs that would tell them when something goes wrong. Log every login, logout, failed attempt, token refresh, and permission change. When a breach happens, logs are what you investigate. No logs means you’re flying blind.

check SSL/TLS configuration

Frequently Asked Questions

Is JWT better than OAuth?

They’re not competing solutions. OAuth 2.0 is a protocol that defines how applications request and grant access. JWTs are a token format that OAuth often uses to issue access tokens. According to the IETF RFC 9068, JWTs are the standard access token format for OAuth 2.0. You typically use them together, not as alternatives.

Can API keys be used for user authentication?

API keys can identify a client application, but they shouldn’t authenticate individual users. They lack user context, don’t support scoping per user, and can’t handle login/logout flows. The OWASP API Security Project explicitly recommends against using API keys as the sole authentication mechanism for user-facing endpoints.

How do you securely store JWTs on the client?

The safest approach is an HttpOnly, Secure, SameSite cookie. This prevents JavaScript access, blocking XSS-based token theft. If cookies aren’t an option (cross-origin APIs), store the access token in memory only and use a refresh token in an HttpOnly cookie. Never use localStorage or sessionStorage for tokens. Snyk’s 2024 report found localStorage token storage in 23% of audited Node.js applications.

What is the difference between authentication and authorization?

Authentication verifies identity: “Who are you?” Authorization determines permissions: “What can you do?” A JWT proves a user is who they claim (authentication), and the role or scope claims inside it determine what resources they can access (authorization). OAuth 2.0 handles both through its token issuance and scope system.

Should I build my own authentication system?

Almost certainly not. Battle-tested libraries and services (Auth0, Clerk, Supabase Auth, NextAuth, Passport.js) handle edge cases you haven’t considered: timing attacks, token binding, key rotation, session fixation, and dozens more. According to Auth0, organizations using managed auth services resolve authentication incidents 60% faster than those with custom implementations. Build auth yourself only if your threat model specifically requires it.

Conclusion

API authentication isn’t a solved problem you can copy-paste from a tutorial. Each method exists because it solves a specific architectural problem, and using the wrong one creates vulnerabilities that no amount of middleware can fix.

Start with the simplest method that meets your security requirements. For most web apps, that’s session-based auth. Add JWTs when you need stateless verification across services. Adopt OAuth when third-party access enters the picture. And regardless of your choice, enforce HTTPS, rotate secrets, rate-limit auth endpoints, and log everything.

The best authentication system is the one your team understands well enough to operate safely. Complexity you can’t debug is worse than simplicity you can.

explore all developer tools