API Security Essentials: OAuth 2.0, mTLS, and Rate Limiting
Cold open: the day the API hit 100% CPU
It started with a login spike. Tokens were flying. The auth server got busy. Then the gateway queues grew. Soon the token endpoint and search API fought for the same threads. CPU hit 100%. Latency went past seconds. Users gave up. The cause was simple, not rare: weak limits, broad scopes, and no strong client checks.
This is why three controls matter most in API work today: OAuth 2.0 for who can do what, mutual TLS (mTLS) for who you let in at all, and rate limiting for how fast they can try. Each tool fixes a different class of risk. None is magic alone. Together, they turn a shaky edge into a safer one.
Field notes: what each control solves (and what it does not)
OAuth 2.0 gives delegated access. It lets a user or app act with clear scopes. It shines at “can this client call this API for this user now?”. It does not do network trust. It does not fix bad business rules.
mTLS adds mutual trust on the wire. Your server checks the client’s cert; the client checks yours. It shines at B2B and service-to-service links. It does not replace good app auth. It can add ops load due to cert work.
Rate limiting slows abuse and smooths bursts. It shines against spam, scraping, and noisy partners. It does not fix broken logic or leaked keys. Still, it buys time and keeps your API alive under stress.
For a quick map of top risks and the best fit control, see the table below. Also bookmark the OWASP API Security Top 10 for common attack paths and design gaps.
Decision snapshot: one week to harden
- Edge first: put limits at the gateway and CDN. Start with a token bucket. Return 429 with retry hints.
- OAuth flows: use Authorization Code + PKCE for public clients (web SPA, mobile). Use short-lived access tokens.
- B2B links: enable mTLS for partner APIs and inside your service mesh. Keep certs short-lived and auto-rotated.
- Scopes: make them narrow. Treat refresh tokens with care. Rotate often.
- Logs and graphs: watch 401/403 spikes, 429 rate by identity, token issue per second, mTLS handshake errors.
Deep dive: OAuth 2.0 without fairy dust
OAuth 2.0 is a framework, not a turnkey. You still must pick flows, token life, scopes, and storage. Start with the basics from the spec, the OAuth 2.0 authorization framework. Then add safety nets that reflect the client type.
For mobile and SPA, pick Authorization Code with PKCE. PKCE blocks code theft in the browser or the app. Guidance lives in OAuth 2.0 for native apps. Use it by default for all public clients.
For machine-to-machine, Client Credentials looks neat. It is simple. But keep scope narrow. Bind the client to a single task, not a suite. Store client secrets in a vault. Rotate. Avoid “shared” secrets across teams or partners.
Keep access tokens short. Think minutes, not hours. Keep refresh tokens short too, or one-time use with rotation. If a device is risky, consider DPoP or cert-bound tokens. Note that OAuth 2.1 status rolls best practices into one place. Track it to simplify choices.
Do not put tokens in localStorage in the browser. Favor httpOnly cookies with same-site where it fits your flow. If you must store in memory, keep token life short and add back-end checks for high-risk calls.
Log scope use. If a scope sits idle, cut it. If one scope is hot and wide, split it. Scopes are not set-and-forget; they drift with product changes.
mTLS: where it shines
mTLS adds a strong gate in front of your API. The server asks the client for a cert. The cert ties back to a trusted CA or a pinned root. This stops unknown clients before app code runs. It also helps you carve lanes: this cert can call only these hosts.
Use TLS 1.3 as a base. It has faster handshakes and stronger suites. See TLS 1.3 for the details. Pair mTLS with short cert life and auto-rotation. Let a job mint and roll certs. Track expiry in one place. Alert early.
Want even tighter binding? Check mutual‑TLS client authentication for OAuth. It binds tokens to a client cert. A stolen token is useless if the thief lacks the cert. This is gold for high-risk money moves and partner callbacks.
Where to turn mTLS on first:
- B2B partner APIs and webhooks.
- Admin and back-office APIs.
- Inside the service mesh between core services.
- Payment and KYC flows.
Watch for common pain: stale certs, clock skew, and missing SNI. Reduce toil: use a mesh or gateway that can request and renew certs for you.
Rate limiting that does not hurt users
Not all limits are equal. Fixed windows can be unfair at edges. Sliding windows and token buckets are smoother. Leaky buckets are simple to reason about. Pick what fits your stack and your need.
Always return clear signals. Use 429 Too Many Requests with Retry-After or custom headers like X-RateLimit-Remaining. Teach clients to back off with jitter. Keep error messages short but useful.
Set limits by identity first, not only by IP. Use keys like user ID, client ID, tenant ID, and even client cert hash for mTLS traffic. Have global limits too, to save the platform under a storm. See this rate limiting guide for patterns at the edge.
Place limits at the edge (CDN), the gateway, and at hot routes in the app tier. For gateway config, check NGINX rate limiting or your vendor’s manual. Match limits to cost: a login try is cheap, a search over many joins is not. Protect the costly ones more.
Measure. Watch 429 rate per identity and route. Aim for a steady low rate. If many good users hit 429, tune the burst size or move work to cache. If no one ever hits 429, you may not be blocking abuse.
Three patterns that work in the real world
Public mobile app + backend
Use Authorization Code with PKCE. Keep access tokens short. Use refresh rotation. Put rate limits per user and per IP at the edge. Inside the cluster, turn on mTLS in the mesh. Cache public data to cut read load. If you have a user search route, use a higher cost score and a tighter limit.
Partner/B2B integration
Turn on mTLS between you and the partner. Limit by partner ID. Give narrow scopes. If a partner has batch jobs, set a window that fits their run. Track 4xx by partner to catch bugs early. For zero‑trust ideas across org lines, see Zero Trust Architecture.
Microservices inside a mesh
Default to mTLS between services. Many teams use a mesh for this, like Istio. Their guide to staged rollout helps: mTLS in service mesh. Add per‑route limits at gateways to auth, billing, and data export. Use service identity, not IP, for allow lists.
Reality check: bench test your setup
Make a short test plan and run it in staging.
- mTLS: try a call with a bad cert, an expired cert, and a cert from a wrong CA. Expect a clean TLS failure, not a 200.
- OAuth: try calls with bad scopes. Rotate refresh tokens and make sure the old one dies. Check token introspect if you use it.
- Limits: hit login 20 times fast. Do you get 429? Are headers clear? Back off and see if access comes back smooth.
- Seats and quotas: if you use a managed gateway, read its guardrails. Here is the AWS page on API Gateway quotas and throttling. Test your plan under those caps.
- Logs: send all 401/403/429/mTLS errors to one stream. Build a small board with rate by identity and handshake fail count.
Sidebar: regulated and high‑risk sectors
iGaming, fintech, and health have little room for error. A partner link gone bad can leak PII or money. In review portals and affiliate hubs, bots scrape bonus pages, push fake sign‑ups, and probe payment links. Here, mTLS for partner callbacks and per‑partner limits help a lot. In the Swedish market, users often look for offers under the term casinon med freespins. Sites that review these brands also run partner and KYC APIs. Hardening those APIs with mTLS plus fair limits cuts abuse during big events without hurting real users.
The table you will actually use
Use this map to tie common threats to the right control. Track where to enforce and how to know it works.
| Credential stuffing on login | Per‑identity rate limits | WAF bot rules, device checks, cache | CDN, gateway | 429 rate/user, p95 login latency | CPU spikes, auth DB hot, 5xx at login |
| Token replay | mTLS + cert‑bound tokens | Short token life, rotation, DPoP | Gateway, app | Replay blocks/day, TLS handshake fails | Unseen client IDs start to pass auth |
| BOLA / data scraping | Per‑user limits + narrow scopes | Row‑level access checks, cache, WAF | Gateway, app | 429/user on hot routes, 403 rate | High read QPS with low auth variety |
| Rogue partner abuse | mTLS with partner certs | Per‑partner limits, audit logs | Edge, gateway | Handshake fails/partner, 4xx by partner | Traffic from new nets passes without review |
| L7 DoS bursts | Global token bucket | CDN shielding, cache, queue back‑pressure | CDN, gateway | p95 under load, 5xx rate, queue depth | p95 jumps with no 429 seen |
| Over‑broad service access | OAuth scopes per service | Service accounts per role, ZTA | Auth server, app | Scope use heatmap, failed scope checks | One token can reach many core routes |
| Expired or weak TLS | TLS 1.3 + auto cert rotate | HPKP style checks in CI/CD | Edge, mesh | Cert age, time to renew | Handshake errors spike near expiry |
Implementation checklist
- Use Authorization Code + PKCE for all public clients.
- Give access tokens a short life (5–15 minutes). Rotate refresh tokens.
- Bind high‑risk tokens to client keys (DPoP or cert‑bind).
- Turn on mTLS for B2B, admin, and inside the mesh.
- Adopt TLS 1.3 only. Drop weak ciphers. Monitor handshake errors.
- Set per‑identity limits and a global cap. Return 429 with Retry‑After.
- Log 401/403/429 with identity keys. Build a live board.
- Split and narrow scopes. Delete idle scopes.
- Automate key and cert rotation. Alert 30 days before expiry.
- Run a monthly bench test of auth, mTLS, and limits.
Common traps to avoid
- Long‑lived refresh tokens with no rotation.
- One shared client secret across teams or partners.
- Only IP‑based limits for public APIs.
- Storing tokens in localStorage where XSS can reach them.
- Relying on TLS alone without mTLS for partner links.
- One giant global limit that blocks good users during busy times.
Quick FAQs
Is OAuth the same as OpenID Connect?
No. OAuth handles “can this client act on this resource?”. OpenID Connect adds identity on top of OAuth. It gives you who the user is, via ID tokens.
Do I need PKCE for web apps?
Use PKCE for SPA and mobile. For classic server‑side web apps with a back end, PKCE is nice to have, not a must. Still, it is low cost and helps, so many teams use it anyway.
mTLS vs TLS — what is the real gain?
TLS proves the server. mTLS proves both ends. With mTLS, you can trust the client cert, not just its IP or a header. This is key for B2B, webhooks, and admin tools.
Where should I put rate limits?
Put them at the edge/CDN, at the gateway, and on hot paths in the app. The closer to the edge, the cheaper the block. Still, per‑route app limits help protect scarce parts like search or export.
What status should I send on limit hits?
Send 429. It is the right code per the spec. Include Retry‑After or rate headers so the client can back off.
References
- OWASP API Security Top 10
- OAuth 2.0 authorization framework
- OAuth 2.0 for native apps
- OAuth 2.1 status
- TLS 1.3
- mutual‑TLS client authentication
- 429 Too Many Requests
- Rate limiting guide
- NGINX rate limiting
- Zero Trust Architecture
- API Gateway quotas and throttling
- mTLS in service mesh
Author and review
Author: Security Architecture Editorial Team
Reviewed by: Senior Staff Engineer, Application Security
Last reviewed: May 2026
Changelog
- May 2026: Added TLS 1.3 note, bench test steps, and table KPIs.
- Apr 2026: First version with OAuth PKCE focus and partner mTLS guidance.