API Authentication: API Keys vs OAuth vs JWT

These three get compared as competing options, but they solve different problems. Picking the wrong one for your actual use case is where most API auth trouble starts.

Single metal key
Photo by Kelly Sikkema on Unsplash

API keys, OAuth, and JWTs get discussed as if choosing between them is a single decision with one right answer. In practice they answer different questions, and most real systems end up using more than one together. Picking the wrong one for a given use case — not picking “the wrong technology” in the abstract — is where most API authentication problems start.

API keys: identifies the caller, not the user

An API key is a static credential that identifies which application or service is making a request. It answers “who is calling this API,” not “which end user authorized this action.” This makes it a reasonable fit for server-to-server integrations and internal tooling, where there’s no individual human user to authenticate — but a poor fit for anything acting on behalf of a specific person, because the key itself carries no concept of user identity or consent.

The main operational risk is that API keys are typically long-lived and broad by default. Unless you specifically build scoping and rotation into how you issue them, a leaked key tends to grant more access, for longer, than a leaked OAuth token would.

OAuth: delegated authorization, not authentication by itself

OAuth 2.0 solves a specific problem: letting a user grant a third-party application limited access to their resources on another service, without handing over their password. The output of an OAuth flow is an access token scoped to specific permissions the user actually approved — this is what makes it right for “let this app read your calendar” scenarios that API keys can’t represent at all, since a key has no concept of user-granted, revocable scope.

It’s worth being precise here: OAuth is an authorization framework, not an authentication protocol. It tells a resource server what the token is allowed to do, not who the underlying user is. OpenID Connect (OIDC), built on top of OAuth, is what adds actual user identity verification via the ID token — if your system needs “who is this person,” you need OIDC on top of OAuth, not OAuth alone.

JWT: a token format, not a protocol

This is the comparison people most often get wrong, because a JWT isn’t in the same category as the other two — it’s a format for representing claims (like user ID, expiration, and scope) in a compact, signed, self-contained way. OAuth access tokens and OIDC ID tokens are frequently implemented as JWTs, but a JWT can also be used entirely outside OAuth, as your own service’s session token format.

The property that matters most: a JWT is self-contained and verifiable without a database lookup, because the signature proves it hasn’t been tampered with. That’s also its biggest operational risk — a JWT can’t be revoked by deleting a server-side record the way a traditional session token can, unless you build a separate revocation mechanism (a blocklist, short expiry with refresh, or similar) on top of it.

Where teams pick the wrong one

  • Using a long-lived API key for something that acts on behalf of individual end users, losing any ability to know or limit what a specific user actually authorized
  • Building a custom OAuth-like flow instead of implementing actual OAuth, reinventing a security-critical protocol that already has a well-audited specification and mature libraries
  • Treating a JWT’s short expiry as sufficient security on its own, without a revocation strategy for the case where a token needs to be invalidated before it naturally expires — a compromised account, a permission downgrade, a logout

A practical way to decide

  • Server-to-server, no individual end user involved → API key, scoped narrowly, rotated regularly
  • A user needs to grant a third-party app limited access to their data → OAuth, with OIDC layered on top if you also need to know who the user is
  • You need a compact, verifiable token to represent a session or claims internally → JWT, with an explicit plan for how revocation works before you need it

The underlying question isn’t “which is more secure” in the abstract — it’s “what does this specific request actually need to represent: an application’s identity, a user’s delegated permission, or a portable set of claims.” Each of the three answers a different one of those.

API SecurityAuthentication