API Authentication Best Practices: OAuth, Keys, mTLS, JWT
Before an API does anything useful with a request, it has to settle one question: who is calling, and do they belong here? If that answer is wrong, every other control you've built (rate limits, input validation, encryption) ends up protecting a system that has already admitted the wrong caller.
This guide compares the four main authentication methods (API keys, OAuth, JWTs, and mTLS), lays out eight practices for applying them consistently, and looks at how agentic workflows and LLM-driven traffic complicate authentication decisions that teams have been making for REST APIs for years.
What Is API Authentication?
API authentication is how an API verifies the identity of the client calling it, whether that client is a person, an application, or another service. Its job is to answer "who is making this call?" That's a different question from authorization, which asks "what is this caller allowed to do?"
In practice, an authenticated request carries some kind of credential (an API key, a bearer token, or a client certificate), and the API validates it before doing any work. Without that step, the API has no way to tell a legitimate partner integration apart from an anonymous script hitting the same endpoint.
API Keys vs. OAuth vs. JWT vs. mTLS: Comparing Your Options
Most production APIs land on one of four approaches, and each suits a different kind of trust relationship between client and server. They aren't rungs on a single security ladder. A JWT is a token format, OAuth is a delegation protocol, mTLS authenticates a TLS connection, and how secure an API key is depends almost entirely on how you implement it. So rather than ranking them, it's more useful to look at what each one is, where it fits, and how it usually goes wrong:
| Method | What It Is | Best For | Common Failure Mode |
|---|---|---|---|
| API Keys | A static credential the caller sends with every request | Internal tools, low-risk read access, fast partner onboarding | Easy to leak, and doesn't expire unless you configure an expiry |
| OAuth 2.0 / 2.1 | A delegation protocol that issues scoped, time-limited access tokens | User-delegated access, third-party integrations, mobile and SPA clients | Scopes granted too broadly at authorization time |
| JWT | A signed token format (RFC 7519), not a protocol in itself | Carrying claims for a session or API call after a token is issued | Incomplete validation: checking the signature but skipping issuer, audience, or expiry |
| mTLS | A TLS-layer mechanism that authenticates the client's certificate during the handshake | Service-to-service and machine-to-machine traffic in regulated environments | Certificate lifecycle overhead; doesn't by itself bind an application-layer bearer token |
API Keys
An API key is a static string that a client includes with each request to identify itself. Keys are simple to issue and simple to revoke, so they work well for internal tooling and low-sensitivity read endpoints.
The catch is that a key is a long-lived shared secret. Whoever holds it can use it. If you don't rotate keys and scope them tightly, a key that slips into a public repository or a client-side app becomes a lasting liability. The UK National Cyber Security Centre calls this out, warning that keys and similar static credentials are frequently compromised through weak secrets management and often grant wide access with no expiry date.
OAuth 2.0 / 2.1
OAuth is a delegation protocol. It lets a client get a scoped, time-limited access token so it can act on a resource owner's behalf without ever seeing that owner's password.
The core OAuth 2.0 framework is defined in RFC 6749, published by the IETF in October 2012. It sets out the roles (client, resource owner, authorization server, and resource server), the grant types, and how tokens are exchanged. A companion spec, RFC 6750, defines how those tokens are sent as bearer credentials in the HTTP Authorization header.
OAuth 2.1 is best understood as a consolidation of OAuth 2.0 rather than a new protocol. It's still an IETF Internet-Draft, not a published RFC, and it rolls up security practices the ecosystem adopted one by one over the past decade. The headline changes are that PKCE becomes mandatory for authorization code exchanges, and the implicit and password grants are removed, since both proved hard to use safely. Think of "OAuth 2.1" as shorthand for how OAuth should be implemented today, not as a separate spec with its own number.
JWT (JSON Web Tokens)
A JWT is a compact, signed token format, defined in RFC 7519, that packages claims as a JSON object: who the token is for, when it expires, and which scopes it carries. OAuth frequently uses JWTs as its access token format, and RFC 9068 standardizes exactly that profile. Even so, the two aren't interchangeable. OAuth describes how a token gets issued and exchanged, while JWT describes one shape that token can take.
Because a JWT is self-contained, an API can check its signature and claims locally without calling back to the issuer. That's what makes stateless authorization practical at scale, and it's also where the risk lies. A validation routine that verifies the signature but ignores the expiry, audience, or issuer claims will happily accept a token that was never intended for that API.
Proper validation takes more than a few if-statements, so don't write it yourself. Use a maintained JWT library that follows the JSON Web Token Best Current Practices in RFC 8725, and configure it to:
- Verify the signature against the issuer's published keys, never a key embedded in the token.
- Allowlist the exact signing algorithm you expect instead of trusting whichever one the token declares.
- Confirm the issuer and the audience. The audience can be a single value or an array, so check for membership rather than exact equality.
- Require the expected token type.
- Reject tokens past their expiry, with only a small, intentional clock-skew allowance.
Finally, treat the token's scopes as an input to authorization, not as the authorization decision itself. Echoing scopes back to the caller doesn't authorize anything. Checking what a given operation actually permits is a separate step that comes after validation.
Missing any one of these checks is a common cause of the broken authentication issues that OWASP lists as API2:2023 in its API Security Top 10.
mTLS
Mutual TLS extends ordinary TLS so the server verifies the client during the handshake too. The client proves it holds the private key that matches a certificate issued by a trusted authority.
That's a different kind of proof from a bearer token. A certificate is public material presented while the connection is being set up. It isn't a secret carried in a header the way an API key or bearer token is. On its own, mTLS authenticates the TLS connection, not any particular application-layer credential sent over it. So if you also send OAuth access tokens over an mTLS connection, those tokens don't become tied to the certificate just because the transport uses mTLS. RFC 8705 defines the mechanism that does this: it binds an OAuth access token to the client certificate used to obtain it, so a stolen token is worthless without the matching private key.
You'll most often find mTLS in service-to-service traffic inside a service mesh and in regulated industries where certificate-based identity is already part of the infrastructure. It's also the most demanding option to run. It needs a certificate authority, processes for issuing and rotating certificates, and client tooling that can present a certificate, which is why it's uncommon for public APIs aimed at third-party developers.
Authentication vs. Authorization: Know the Difference
Authentication establishes identity. Authorization decides what that identity is allowed to do. A valid API key or token proves the caller is who they claim to be, but it says nothing about whether they should be able to delete a resource, read another tenant's data, or call an admin endpoint.
Treating a successful login as permission to do anything is exactly the gap behind broken object-level and function-level authorization, which the OWASP API Security Top 10 ranks as separate risks. Authentication and authorization should be enforced as two distinct checks on every request.
8 API Authentication Best Practices
1. Always Use TLS
Credentials such as API keys, bearer tokens, and client certificates are only as safe as the channel carrying them. Without TLS, any credential in a header or query string can be read by anyone on the network path. This is the baseline, and it's also the first point in the NCSC's API guidance, which notes that unencrypted transport and outdated schemes like base64-encoded basic auth don't stand up to current threats.
2. Use a Central OAuth/OIDC Server
Issue tokens from a dedicated OAuth or OpenID Connect server instead of having each API build its own login and token handling. That keeps credential storage, rotation, and revocation in one audited place, so individual teams aren't reimplementing (and potentially weakening) the same mechanisms. It also gives you a single point to enforce multi-factor authentication and a single place to look during an incident.
3. Store Credentials Securely (Secrets Management)
API keys, client secrets, and signing keys belong in a secrets manager or vault. They don't belong in source code, in configuration files committed to version control, or in environment variables that a build pipeline logs. It sounds obvious, yet it's broken all the time. The NCSC is explicit that sending or storing sensitive credentials in code or URLs is a recurring root cause of API breaches.
4. Enforce Short-Lived, Replay-Resistant Credentials
Long-lived tokens and permanent API keys widen the window an attacker has once a credential leaks. Short expiry times, combined with a refresh flow, keep that window small.
This is a separate concern from PKCE, which protects a different part of the flow. PKCE prevents an attacker who intercepts an authorization code from completing the token exchange. It does nothing for an access token that has already been issued and then leaks. For that, shorter token lifetimes help, and where the risk justifies it, sender-constrained tokens go further. Both the certificate-bound tokens in RFC 8705 and DPoP (RFC 9449) tie the token to a key the client holds, so the token alone isn't enough to use it.
5. Scope and Claim-Based Access Control
A token should carry only the scopes or claims the client needs for its task, not blanket access to every endpoint the API exposes. Tight scoping means a compromised token can only reach what it was allowed to touch, rather than the whole API surface. The OAuth2 security scheme object in the OpenAPI Specification 3.1.1 is where those per-operation scopes are declared.
6. Combine Mechanisms Deliberately, Don't Let a Weaker One Bypass the Others
OAuth, JWT, and mTLS aren't mutually exclusive. A JWT access token can be issued through an OAuth flow and bound to a client certificate at the same time, and many production APIs run exactly that combination.
The goal isn't to pick one method and ban the rest. It's to document, for each operation, which schemes apply and whether they're alternatives or required together. The OpenAPI Specification's Security Requirement Object already supports this, so a weaker option (say, an unscoped API key accepted alongside a properly scoped OAuth token) can't be used to get around a control that should be mandatory for that endpoint. Enforce that mapping consistently at the gateway so every request is checked against the rule that's actually meant to apply to it.
7. Protect Every Endpoint (Not Only "Sensitive" Ones)
Teams often lock down the endpoints that obviously handle sensitive data and leave quieter ones, such as health checks with verbose output, internal admin routes, and older API versions, on weak authentication or none at all. That's precisely the gap attackers look for. Every endpoint needs an explicit authentication decision, including a conscious choice to make it public, rather than an accidental one left by an endpoint nobody reviewed.
8. Audit and Monitor Auth Events
Log every authentication attempt, successful or failed, with enough context (timestamp, source, identity, and outcome) to reconstruct events afterward. The NCSC's logging guidance points the same way: record authentication and authorization events specifically, including failed attempts and permission changes, and keep the credentials themselves out of the logs. Watching that data for spikes in failed logins or unusual access patterns is what turns logging into real detection, instead of an audit trail nobody opens until after an incident.
Common API Authentication Mistakes
A small set of mistakes is behind most of the API authentication incidents that make headlines:
- Relying on API keys alone for anything beyond low-risk internal use. Keys issued without an expiry never expire on their own, don't carry fine-grained scope by default, and leak easily into client-side code or public repositories.
- Skipping rate limiting on authentication endpoints. Login and token endpoints are the main targets of credential stuffing and brute-force attacks, and OWASP names this directly under broken authentication.
- Validating a JWT's signature but not its expiry or audience claims. This one is subtle enough to slip through a casual code review.
- Treating authentication as a one-time gate instead of a per-request check. Caching a "this user is fine" decision rather than re-validating the token on each call keeps a compromised session alive longer than necessary. Re-validation isn't a revocation strategy on its own, though. Checking a token's signature and expiry locally can't detect a revocation that happened after the token was issued. Catching that takes a server-side status check, such as token introspection or a revocation list, with a short token lifetime limiting how long a revoked token stays usable before it expires naturally.
How WSO2 Secures API and AI Traffic
Authenticating a REST API call and authenticating LLM or agent traffic are related problems, but they travel different request paths, and it's worth keeping them apart. A client calling your API authenticates inbound at the gateway, before the request reaches your backend. When the gateway relays that request (or makes a separate one) to an LLM provider such as Azure AI, OpenAI, or AWS Bedrock, it authenticates outbound with its own credentials.
These are independent authentication events, not consecutive steps in one chain, and the same rule applies to each: document which mechanism a given call must satisfy, and don't let a weaker fallback stand in for it. That means consistent enforcement inbound, and consistent, centrally managed credentials outbound.
The WSO2 API Platform centralizes policy definition and identity management for both paths from a single control plane. On the inbound side, the API Gateway validates OAuth 2.0 access tokens issued by the platform's built-in security token service (STS), Asgardeo, Azure AD, or another external key manager, and enforces the scopes each operation requires. One caveat: API Platform STS tokens are for client authentication only, so APIs that need end-user authentication should use an external identity provider such as Asgardeo.
Gateways from other vendors, including AWS, Azure, Kong, and Envoy, can be federated into the same control plane. The control plane discovers their APIs, lists them in one developer portal, and monitors them for governance compliance. Runtime authentication on those APIs is still enforced by each federated gateway, so check per gateway what's actually verified on the request path.
On the outbound side, calls to an LLM provider carry their own scoped credentials, managed like any other outbound service call. The AI Gateway also applies semantic prompt validation and PII masking, but those are content-level guardrails, not authentication mechanisms, and they're best treated as a separate concern from who is allowed to call what.
Agent-to-tool calls over MCP form a third, separate surface, and the platform extends its authentication controls there as well. WSO2 API Platform can proxy an existing remote MCP server by its URL. The proxied server is secured through the gateway by default, with rate limiting applied out of the box, and reaching it requires subscribing through an application and presenting a valid OAuth2 token, the same access model used for any other governed API.
Finer-grained controls come from the AI Gateway's MCP policies. MCP Authorization supports per-tool, per-resource, and per-prompt rules based on token claims and scopes, and the MCP Access Control List provides allow and deny lists. Every agent call to a governed tool goes through the same authentication, authorization, and throttling checks this guide recommends for any API, just applied to a newer kind of caller.
The reason for centralizing might be running several gateways that need one consistent auth policy, needing to attribute AI spend and access back to teams, or handling regulated data that now flows through prompts as well as request bodies. Either way, the underlying decision is the one this guide covers for every API: document which mechanism applies where, enforce it everywhere that type of traffic flows, and log what happened.
Conclusion
Document which authentication mechanisms apply to each operation, enforce that mapping consistently at the gateway, and make sure a weaker fallback can never replace a control that's meant to be mandatory. That discipline heads off most of the failures covered in this guide, from unscoped API keys to uneven enforcement across endpoints.
As API traffic increasingly mixes LLM calls and agent-to-tool requests in with traditional REST calls, the same discipline carries over rather than starting from scratch. A multi-gateway control plane that applies consistent authentication, authorization, and logging across REST, AI, and MCP traffic is how that consistency holds up as the traffic mix changes.
Frequently Asked Questions
Should I use JWT or OAuth for my API?
They solve different problems. OAuth governs how a client obtains a token, and JWT is a format that token can take. Most production APIs use both: OAuth for the authorization flow, and JWT for the resulting access token.
Is API key authentication secure enough for production?
For low-risk internal tools, often yes. For anything that handles user data or is exposed to third parties, an API key on its own usually isn't enough, because keys don't expire by default and are easy to leak. Pair keys with rate limiting and scoping, or move to OAuth for anything beyond low-sensitivity access.
What is the best API authentication method?
There's no single best method independent of context. OAuth 2.0 suits user-delegated and third-party access, mTLS suits service-to-service traffic in regulated environments, and API keys suit low-risk internal use. The right choice is the one that matches who is calling and what's at stake.
How do you handle authentication in APIs at the gateway level?
A gateway in front of your APIs centralizes authentication, so individual services don't each implement their own token validation. It verifies credentials on every request, applies consistent policy across endpoints, and gives you one place to enforce rate limits and log auth events.