
September 11, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Every production REST API needs a clear answer to one question: who is calling, and what are they allowed to do? API Authentication: Keys, JWT, OAuth are the three patterns you will encounter on almost every integration—mobile apps, partner portals, payment webhooks, and internal microservices. Pick the wrong one and you either ship insecure endpoints or add weeks of unnecessary complexity. This guide compares all three with real request flows, security trade-offs, and copy-paste patterns for Laravel API development on PHP 8.3+.
What is API authentication and why do keys, JWT, and OAuth matter?
Authentication proves identity. Authorisation decides permissions. Many teams blur the two and regret it later.
On a legal-tech portal I built, a mobile app needed document upload while a partner CRM needed read-only case status. Same API, different trust models. Keys worked for the CRM. OAuth fit the mobile app because users logged in with their own credentials.
Modern APIs sit behind gateways, load balancers, and CDN edges. Your auth scheme must survive retries, clock skew, token expiry, and leaked credentials. That is why understanding JWT vs session vs API keys before you write middleware saves painful refactors.
Each pattern solves a different trust boundary. Keys trust the holder of the secret. JWT trusts the signature from a known issuer. OAuth trusts an authorization server that the user already knows.
None of these replace transport security. Always terminate TLS at the edge. Never send raw credentials over plain HTTP.
How do API keys work for REST API authentication?
API keys are opaque strings tied to a client record in your database. The client sends the key on every request. Your API looks it up, checks status, and applies scopes.
Common transmission methods
Three header patterns dominate production APIs:
- Authorization header:
Authorization: Bearer sk_live_abc123— familiar to HTTP clients. - Custom header:
X-API-Key: sk_live_abc123— keeps keys separate from user JWTs. - Query string: discouraged; keys leak into logs, referrer headers, and browser history.
Key lifecycle you should implement
- Generate cryptographically random keys (32+ bytes, base64url-encoded).
- Store only a hashed value in the database—never the plaintext key after creation.
- Attach metadata: owner, scopes, rate-limit tier, created date, last-used timestamp.
- Support rotation: issue a new key, overlap grace period, revoke the old one.
- Audit every authentication failure and suspicious usage spike.
GET /api/v1/orders HTTP/1.1
Host: api.example.com
Authorization: Bearer sk_live_7f3a9c2e
Accept: application/json Keys excel when both sides are servers you control. Payment gateway callbacks, internal cron jobs, and partner B2B feeds fit this model well.
Keys fail when you need per-user permissions inside a third-party app. A leaked key grants full client access until rotation. That is why public mobile apps should not embed long-lived API keys.
Pair keys with rate limiting strategies from day one. A stolen key without throttling becomes an expensive denial-of-wallet attack.
How does JWT authentication work in APIs?
A JSON Web Token is a compact, signed payload. The issuer signs it. The resource server verifies the signature without calling the issuer on every request.
That stateless property makes JWT popular in microservices and SPAs. It also creates footguns if you treat JWT like a session cookie with extra steps.
JWT structure
Three base64url segments separated by dots: header, payload, signature.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0Iiwicm9sZSI6ImFkbWluIiwiZXhwIjoxNzM1NjgwMDAwfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c Decoded payload example:
{
"sub": "1234",
"iss": "https://auth.example.com",
"aud": "https://api.example.com",
"exp": 1735680000,
"scope": "orders:read orders:write"
} Always validate iss, aud, exp, and nbf if present. Reject tokens with unexpected algorithms—algorithm confusion attacks are still common. See the JWT specification (RFC 7519) for registered claim definitions.
Access vs refresh tokens
Short-lived access tokens (5–15 minutes) limit blast radius. Refresh tokens live longer but must be stored securely and revocable.
On production Laravel applications I maintain, Redis-backed deny lists handle emergency revocation. You cannot rely on expiry alone when an employee leaves or a device is lost.
Read JWT security common vulnerabilities before shipping. Storing sensitive PII inside JWT payloads is a design smell—claims are base64-decodable by anyone holding the token.
Debug payloads with the JSON formatter tool during development. Never paste production tokens into public tools.
When should you use OAuth 2.0 instead of API keys or JWT?
OAuth solves delegated authorisation. A user grants a third-party app limited access without sharing their password with that app.
Think "Login with Google" or a Shopify app requesting order read access. The user consents. The authorization server issues tokens. Your API trusts that server.
OAuth 2.1 authorization code flow with PKCE
OAuth 2.1 consolidates best practices from years of real-world deployments. For browser and mobile apps, authorization code with PKCE is the default choice. The OAuth 2.0 framework (RFC 6749) defines the core roles: resource owner, client, authorization server, resource server.
- Client generates a code verifier and code challenge (PKCE).
- User redirects to the authorization server login and consent screen.
- Authorization server returns an authorization code to the registered redirect URI.
- Client exchanges the code plus verifier for access and refresh tokens.
- Client calls your API with the access token until expiry.
Machine-to-machine flows use client credentials instead. That pattern resembles API keys but adds standard token expiry and scope negotiation through the authorization server.
OpenID Connect adds an identity layer on top of OAuth. It returns an ID token with user profile claims. Read OAuth 2.1 vs OpenID Connect explained if your product needs single sign-on.
For public API products, OAuth also enables scoped third-party integrations. Partners register clients, you approve scopes, and audit token usage centrally.
How do API keys, JWT, and OAuth compare in practice?
Teams often ask for a single winner. There isn't one. The right choice depends on who holds the credential and whether a human user is in the loop.
| Criteria | API Keys | JWT | OAuth 2.0 |
|---|---|---|---|
| Best for | Server-to-server, webhooks, internal jobs | SPAs, microservices, mobile backends | Third-party apps, SSO, user-delegated access |
| State | Stateful lookup (DB or cache) | Stateless verification | Tokens + authorization server state |
| Revocation | Instant (delete or disable key) | Hard without deny list or short TTL | Refresh token revoke + short access TTL |
| User consent | Not applicable | Usually first-party login | Built-in consent screens |
| Complexity | Low | Medium | High |
| Leaked credential risk | Full client access until rotation | Scoped until expiry | Scoped + revocable refresh tokens |
Many production systems combine all three. A gateway validates API keys for partners. First-party apps get JWT access tokens from your auth service. External integrations use OAuth client registration.
How do you implement API authentication in Laravel?
Laravel 13 (PHP 8.3+) and Laravel 12 (PHP 8.2+, supported to February 2027) ship first-class API tooling. Pick Sanctum for SPA and mobile token auth. Pick Passport when you need a full OAuth 2.0 authorization server.
See Laravel Passport vs Sanctum for the full decision matrix. Most apps I build start with Sanctum and only add Passport when external OAuth clients are a product requirement.
Sanctum for API tokens and SPA auth
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate Issue a personal access token:
$token = $user->createToken('mobile-app', ['orders:read'])->plainTextToken;
return response()->json(['token' => $token]); Protect routes:
Route::middleware('auth:sanctum')->group(function () {
Route::get('/orders', [OrderController::class, 'index']);
}); Full walkthrough: build a REST API with Laravel Sanctum authentication.
Passport for OAuth 2.0 clients
When partners need registered OAuth clients with scoped access, Passport provides authorization codes, refresh tokens, and client credentials grants. Configure token lifetimes in AppServiceProvider or the Passport config file.
On client portals like Mijar Law Associates, Sanctum tokens power document upload from authenticated users. Partner integrations would move to Passport if the firm opened an external API.
Custom API key middleware
For webhook receivers and B2B feeds, a lightweight middleware often beats pulling in OAuth:
public function handle(Request $request, Closure $next)
{
$key = $request->bearerToken() ?? $request->header('X-API-Key');
if (! $key || ! ApiKey::verify($key)) {
return response()->json(['message' => 'Unauthorized'], 401);
}
return $next($request);
} Hash keys with hash('sha256', $plainKey) before storage. Show the plaintext once at creation—same pattern Stripe uses for secret keys.
Follow Laravel API best practices for versioning, pagination, and consistent error envelopes alongside auth.
What are common API authentication security mistakes?
Authentication bugs become breach headlines. These patterns show up repeatedly in audits and post-incident reviews.
- Long-lived tokens in mobile binaries: reverse-engineering exposes them. Use OAuth with PKCE or short-lived tokens plus secure refresh storage.
- Missing scope checks: validating identity without authorisation lets any authenticated user hit admin routes.
- JWT in localStorage: XSS steals tokens. Prefer HttpOnly cookies for browser apps or secure enclave storage on mobile.
- Ignoring clock skew: allow 30–60 seconds leeway on
expandnbfvalidation. - Logging Authorization headers: your log aggregator becomes a credential vault. Strip sensitive headers at the edge.
- No idempotency on mutating endpoints: auth does not prevent duplicate charges. Pair with idempotency keys on POST and PATCH.
Run through the API security complete checklist before every production launch. Test with Base64 encoder/decoder locally when inspecting JWT segments—never in shared channels.
Payment integrations on Nepal-facing platforms often mix API keys (gateway callbacks) with user JWTs (customer dashboards). I've seen Khalti and eSewa webhooks fail because teams rotated keys without updating server config. Treat key rotation as a deployment step, not a database edit.
Document your auth scheme in OpenAPI. External developers integrate faster and misconfigure less. See API documentation with Redoc and Swagger UI and SDK design for your public API for downstream tooling.
For broader auth architecture—including password flows and MFA—read the ultimate guide to building secure authentication systems. For greenfield products, custom software development engagements should define auth requirements before schema design.
The Nepal Divorce Services portal combines session auth for the web UI with token-backed AJAX calls. That hybrid is normal: cookies for browsers, bearer tokens for API consumers.
Key Takeaways
- Use API keys for trusted server-to-server calls; hash at rest, rotate regularly, and never pass keys in query strings.
- Use JWT for stateless first-party access; keep TTLs short, validate iss/aud/exp, and plan revocation with deny lists or refresh-token rotation.
- Use OAuth 2.0 when third-party apps need scoped, user-consented access—authorization code with PKCE for public clients.
- Laravel Sanctum covers most token needs; add Passport only when you must issue OAuth clients to external partners.
- Combine auth with rate limiting, HTTPS, audit logging, and scope checks—authentication alone is not authorisation.
- Document flows in OpenAPI and test failure paths (expired token, wrong scope, revoked key) before launch.
People Also Ask
Is JWT better than API keys?
Neither is universally better. API keys suit fixed server integrations with instant revocation via database lookup. JWT suits distributed services that need stateless verification. Choose based on who holds the credential and how quickly you must revoke access.
Does OAuth replace JWT?
No. OAuth is a framework for obtaining tokens. Access tokens are often JWTs, but they can also be opaque strings. OAuth defines how a client gets a token; JWT defines how you encode and verify token contents.
How long should API tokens last?
Access tokens: 5–15 minutes for high-sensitivity APIs, up to one hour for low-risk read endpoints. API keys: no expiry by default, but schedule rotation every 90 days. Refresh tokens: days to weeks, stored securely and revocable server-side.
What is the difference between authentication and authorisation in APIs?
Authentication answers "who are you?"—via key, JWT signature, or OAuth token. Authorisation answers "what may you do?"—via scopes, roles, or policies applied after identity is established. Both must pass for a request to succeed.
Ship API authentication that survives production
API Authentication: Keys, JWT, OAuth is not a pick-one-and-forget decision. Start with the simplest pattern that matches your trust boundary. Add OAuth when external developers need user-delegated access. Harden every path with TLS, rate limits, and scoped permissions.
If you are designing a new API or untangling auth debt on an existing Laravel or Symfony codebase, I can help you choose and implement the right model. Review related work in the portfolio, explore building RESTful APIs with Laravel, or contact us to discuss your integration requirements.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

