API Authentication Methods: API Keys, Sessions, JWTs, OAuth and mTLS
How should clients prove who they are to your API? API keys for server-to-server, session cookies for your own web app, bearer tokens for mobile and SPAs, OAuth for third-party access, HMAC signatures for webhooks, mTLS for service meshes. When to use each and how to do it safely.
Every API needs to answer one question on every request: who is calling? (Then, separately, what may they do? — that's authorisation. Authentication vs authorization.)
There's no single best method. The right one depends on who the client is.
The quick map
| Client | Recommended | Why |
|---|---|---|
| Your own web app (same site) | Session cookie (HttpOnly) | Simple, revocable, not readable by JS |
| Mobile app / desktop app | Bearer token (short-lived access + refresh) | No cookie jar; tokens stored in secure OS storage |
| A customer's server calling your API | API key | Simple for developers, scoped and rotatable |
| A third-party app acting for your users | OAuth 2.0 | Users grant limited access without sharing passwords |
| You calling someone's webhook / them calling yours | HMAC signature | Proves the payload came from the sender, unmodified |
| Internal services in a private network | mTLS or workload identity | Both sides prove identity with certificates |
Session cookies (your own web app)
After login, your server creates a session (in the database or a signed cookie) and sets a cookie:
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax; Path=/
The browser sends it automatically on every request to your API. Revoke it by deleting the session server-side. Protect state-changing requests against CSRF (SameSite helps; tokens for stricter cases). (Session cookies vs JWTs, CSRF explained)
Best when frontend and API share a site. It's the simplest secure option, and many SPAs that store JWTs in localStorage would be safer with it. (localStorage vs cookies)
Bearer tokens (mobile, cross-site SPAs)
The client sends a token in a header:
Authorization: Bearer eyJhbGciOi...
Often a JWT so the API can verify it without a database lookup. Good practice:
- Short-lived access tokens (minutes), plus a refresh token to get new ones.
- Store them in the platform's secure storage (Keychain, Keystore), not plain files.
- Have a way to revoke — rotating refresh tokens, a deny-list, or short lifetimes.
- Validate signature, expiry (
exp), issuer and audience on every request.
API keys (server-to-server)
A long random secret identifying a customer or integration:
Authorization: Bearer sk_live_8f2a…
Doing them well:
- Generate with a cryptographically secure random generator, with a recognisable prefix (
sk_live_) so secret scanners can detect leaks. - Store a hash of the key, not the key itself — like passwords. Show it to the user once. (Hashing vs encryption)
- Scope keys (read-only, specific resources) and allow several per account so they can rotate without downtime.
- Record last used time; let users revoke instantly.
- Never accept keys in URLs (they end up in logs). Headers only.
- Rate-limit per key. (Implementing rate limiting)
API keys are not for browsers or mobile apps — anything shipped to a device can be extracted. (What is an API key?)
OAuth 2.0 (third-party access)
When another app wants to act on behalf of your user ("Let Zapier read your projects"), OAuth lets the user approve a scoped grant without giving the third party their password. The app gets an access token (and refresh token) limited to the scopes approved.
Use the authorization code flow with PKCE for all client types. Implementing an OAuth provider yourself is substantial work; use a library or identity service. (Using OAuth to sign in with Google is the consumer side — Sign in with Google explained.)
HMAC signatures (webhooks)
The sender computes an HMAC of the request body with a shared secret and sends it in a header; the receiver recomputes and compares. Include a timestamp in the signed data to block replays, and compare signatures in constant time. (Handle webhooks reliably)
mTLS (service to service)
Both client and server present TLS certificates, so each proves its identity at the connection level. Common in service meshes and zero-trust internal networks. Heavier to operate (certificate issuance and rotation), so small apps rarely need it.
Rules for every method
- HTTPS only. Every one of these credentials is useless to protect over plain HTTP. (What is HTTPS?)
- Authenticate, then authorise — every request, every resource. Knowing who someone is doesn't mean they may access record 42. (IDOR explained)
- Return 401 for missing/invalid credentials and 403 for valid credentials without permission. (HTTP status codes)
- Log authentication failures and alert on spikes.
- Don't invent your own crypto or token formats.
The summary
- Own web app → HttpOnly session cookie.
- Mobile/cross-site clients → short-lived bearer tokens with refresh.
- Developers' servers → hashed, scoped, rotatable API keys in headers.
- Third-party access → OAuth with PKCE. Webhooks → HMAC. Internal services → mTLS.
- HTTPS always; authorise every request after authenticating it.
EasySpawn runs your API on its own server with HTTPS on your domain, keeping signing secrets and key hashes server-side next to your Postgres database. See how it works or join the waitlist.
Related: Session Cookies vs JWTs · What Is an API Key? · Role-Based Access Control · Designing a REST API
Keep reading
Session Cookies vs JWTs: Which Should Your App Use for Authentication?
Server-side sessions and JWTs both keep users logged in, with very different trade-offs. How each works, revocation and logout, where to store tokens (cookies vs localStorage), the hybrid access/refresh pattern, and a clear default for web apps.
Role-Based Access Control (RBAC) for Your App: A Practical Guide
How to add roles and permissions to a web app without making a mess: roles vs permissions, a simple database schema, checking permissions on the server, multi-tenant roles per organisation, enforcing in the UI and the API, and testing it.