API authentication is the first line of defense for any web service. API keys are static credentials passed in headers or query strings — simple but brittle. OAuth 2.0 is a delegated authorization framework defined in IETF RFC 6749 that lets a user grant scoped access to a third party without sharing credentials. JWTs (JSON Web Tokens) are signed, self-contained tokens that carry claims and can be verified without a database lookup. For Indian developers building fintech, B2B SaaS, or compliance-heavy APIs, picking the wrong scheme is not just a technical inconvenience — it is an exploitable security gap.
What Are API Keys and When Should You Use Them
An API key is a long, random string — typically 32 to 64 hex characters — issued by a service and passed in an Authorization: Bearer <key> header or an X-API-Key header. The server validates it by looking it up in a database row.
Use API keys when:
- The caller is a server-to-server integration (no human in the loop)
- You need simplicity: one key, one service account, tightly scoped permissions
- Revocation speed matters (invalidating a row is instant)
- You are exposing a public data API (weather, currency, stock ticker) to paying customers
- The client is a mobile app or browser-side JavaScript — the key will be extracted
- You need to delegate access on behalf of a human user (OAuth 2.0 is the right tool)
- You need fine-grained per-user scopes
How OAuth 2.0 Works
OAuth 2.0 defines four flows (grant types). The most important for web and mobile applications is the Authorization Code Flow with PKCE (Proof Key for Code Exchange), which prevents authorization code interception on mobile devices.
graph TD
A[User opens app] --> B[App redirects to Auth Server]
B --> C[User logs in and consents]
C --> D{Auth Server issues
Authorization Code}
D --> E[App exchanges code
for Access Token]
E --> F[App calls API
with Access Token]
F --> G{API validates token}
G --> H[Success - return data]
G --> I[Failure - 401 Unauthorized]
style A fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0
style B fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0
style C fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0
style D fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0
style E fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0
style F fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0
style G fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0
style H fill:#1e3d2f,stroke:#10B981,color:#e2e8f0
style I fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0The key advantage of OAuth 2.0 is delegation with minimal privilege. A user can grant your payroll integration read-only access to their accounting software without handing over their password. Access tokens expire (typically 15–60 minutes). Refresh tokens allow silent renewal. Revoking a refresh token cuts off access entirely.
The four standard grant types:
| Grant Type | Use Case | Token Lifetime |
|---|---|---|
| Authorization Code + PKCE | Web/mobile apps acting on behalf of a user | Short-lived access + long-lived refresh |
| Client Credentials | Server-to-server (no user involved) | Short-lived, non-refreshable |
| Device Code | Smart TV / CLI with no browser | Short-lived |
| Implicit (deprecated) | Legacy SPAs — do not use | Short-lived, no refresh |
How JWT Authentication Works
A JSON Web Token consists of three base64url-encoded parts separated by dots: header.payload.signature. The header specifies the signing algorithm (RS256, ES256, HS256). The payload contains claims: sub (subject/user id), iss (issuer), aud (audience), exp (expiry Unix timestamp), iat (issued at), and any custom claims your API needs. The signature is computed over the header and payload.
Your API server validates the JWT by:
- Decoding the header to get the algorithm
- Fetching the public key from the issuer's JWKS endpoint (for asymmetric schemes)
- Verifying the signature
- Checking
exp(reject if expired),iss, andaud - Extracting claims from the payload to make authorization decisions — without a database hit
Common JWT Mistakes Indian Developers Make
1. Accepting alg: none
The JWT spec originally allowed a none algorithm, meaning no signature. An attacker crafts a token with any payload and sets alg: "none". Naive libraries accepted it. Always use a whitelist: RS256 or ES256 for production, never accept none.
2. Using HS256 with a weak or shared secret
HS256 is an HMAC shared secret. If your auth server and API server share the same secret, and that secret leaks (e.g., environment variable in a Docker Hub public image), every token can be forged. Prefer RS256 — the auth server holds the private key, the API only needs the public key.
3. Storing JWT in localStorage
localStorage is accessible to any JavaScript on the page. An XSS vulnerability anywhere in your app — including third-party widgets, analytics SDKs, or ad scripts — can exfiltrate the token. Store access tokens in memory (a React state variable or module-level variable). Store refresh tokens in HttpOnly; Secure; SameSite=Strict cookies that JavaScript cannot read.
4. No expiry or excessively long expiry
A JWT with exp set to 30 days is effectively a password that never rotates. If an attacker obtains it (logs, referrer headers, XSS), they have persistent access. Best practice: access token TTL of 15 minutes, refresh token TTL of 7–30 days stored server-side and revocable.
5. Not validating aud and iss
If your platform issues JWTs for multiple services, always validate the aud (audience) claim. A token issued for your analytics API should not be accepted by your payments API. Skipping audience validation enables cross-service token replay.
alg: none acceptance and missing exp checks — directly maps to this finding and is the most common root cause in API penetration tests performed on Indian SaaS platforms.Know your vulnerabilities before attackers do
Run a free VAPT scan — takes 5 minutes, no signup required.
Book Your Free ScanComparing the Three Approaches
xychart-beta
title "Use-Case Fit Score by Authentication Scheme"
x-axis ["Server-to-Server", "User Delegation", "Stateless Scale", "Mobile Apps", "Token Revocation"]
y-axis "Fit Score" 0 --> 10
bar [9, 2, 5, 2, 9]
bar [5, 10, 7, 9, 7]
bar [6, 6, 10, 7, 4]Bars left to right: API Keys, OAuth 2.0, JWT. Higher is better fit for that use case.
Token Storage Best Practices
| Token Type | Correct Storage | Never Store Here |
|---|---|---|
| JWT access token | In-memory (JS variable / React state) | localStorage, sessionStorage, non-HttpOnly cookie |
| JWT refresh token | HttpOnly; Secure; SameSite=Strict cookie | localStorage, URL params |
| API key (server side) | Environment variable, secrets manager (AWS SSM / GCP Secret Manager) | .env committed to git, application DB in plain text |
| OAuth access token (mobile) | OS keychain (Android Keystore / iOS Secure Enclave) | SharedPreferences (Android), NSUserDefaults (iOS) |
Refresh Tokens and Rotation
A refresh token is a long-lived credential used to obtain new access tokens after they expire. The correct pattern is refresh token rotation: every time you use a refresh token, the server issues a new refresh token and invalidates the old one. If an old refresh token is presented (replay attempt), the server invalidates the entire token family — triggering re-authentication for the user.
Implement this server-side with a token family table:
token_family: { family_id, current_refresh_token_hash, user_id, created_at, expires_at, revoked }On each refresh, hash the new token, update current_refresh_token_hash. On replay detection (hash mismatch), set revoked = true for the entire family.
Securing Fintech and B2B APIs in India
Indian fintech APIs operate under specific regulatory frameworks: RBI's Payment Aggregator guidelines, SEBI's cybersecurity circular for registered intermediaries, and the upcoming DPDP Act enforcement (visit /dpdp-compliance for details on personal data protection obligations). These frameworks share common authentication requirements:
- Mutual TLS (mTLS) for high-value transaction APIs — both client and server present certificates
- Short-lived tokens — 15-minute maximum for payment initiation
- Audit logging — every token issuance, refresh, and revocation must be logged with timestamp, IP, and user agent
- Scoped access — payment initiation and payment status query must be separate scopes; read scope must not imply write
- Rate limiting per token — not just per IP; limit 100 requests per minute per access token
Choosing the Right Scheme: A Decision Framework
- Internal microservice-to-microservice calls → Client Credentials OAuth 2.0 or a short-lived signed JWT (service mesh pattern). API keys are acceptable if the environment is fully private and keys are rotated monthly.
- Third-party developer API (like Razorpay's API) → API keys with scoped permissions, key-level rate limiting, and a key management dashboard.
- Web/mobile app acting on behalf of a logged-in user → Authorization Code + PKCE for issuance; JWT for stateless validation at the API layer; refresh token rotation for session continuity.
- Single-page app accessing your own backend → JWT in memory, refresh token in HttpOnly cookie. Never use Implicit flow.
- Open Banking / Account Aggregator integration → OAuth 2.0 as specified by the AA Technical Specification. Consent artefacts are JWTs signed with the AA's key. Validate every consent JWT's
exp,iss, andconsentId.
alg whitelisting. Fix the implementation first.Frequently Asked Questions
Can I use API keys for a mobile app?
What is the safest way to store JWT tokens in a React app?
What is the difference between OAuth 2.0 and OpenID Connect?
How do I revoke a JWT if it is stateless?
jti (JWT ID) values with a TTL matching the token lifetime. Check the blocklist on every request. This adds one Redis/cache lookup per request — a worthwhile trade for high-value APIs.