Master API authentication strategies including API keys, OAuth 2.0, JWT tokens, and understand when to use each pattern.
Before diving into API authentication patterns, it is critical to understand two often-confused concepts: authentication and authorization.
Authentication answers the question: Who are you? It is the process of verifying identity. When you log into a website with your email and password, you are proving you are who you claim to be. Think of it like showing your ID at the airport — you are proving your identity.
Authorization answers the question: What are you allowed to do? It is the process of verifying permissions. Once the system knows who you are, it checks what resources you can access and what actions you can perform. Continuing the airport analogy: your boarding pass (authorization) shows which flight you can board and which seat you can sit in.
In API design, these concepts work together but serve different purposes. A user might successfully authenticate (prove they are user #42) but fail authorization when trying to delete another user's post (they do not have permission for that action).
Real-world example: GitHub's API uses authentication to identify which user is making a request (via access token), then uses authorization to determine whether that user can push to a specific repository. You might be authenticated as yourself, but authorized to push only to repositories you own or collaborate on.
Most API security issues stem from confusing these two. Authentication should happen first (identify the user), then authorization should check permissions for each specific action. Never assume an authenticated user is authorized to do everything — always check permissions explicitly.
API keys are the simplest form of API authentication. An API key is a long random string (like sk_live_4eC39HqLyjWDarjtT1zdp7dc) that identifies an application or user. The client includes this key in every request, typically in a header like Authorization: Bearer YOUR_API_KEY or X-API-Key: YOUR_API_KEY.
API keys are popular because they are dead simple. Generate a random string, save it in your database, give it to the user, and check it on each request. There is no complex flow, no token expiration to manage, no refresh logic. Services like Google Maps, OpenAI, and Stripe all use API keys for their public APIs.
Pros:
Cons:
When to use API keys: Use them for server-to-server communication or for APIs where you want simple authentication without user-specific actions. For example: a weather API (you just want to know who is calling to enforce rate limits), a mapping API (billing based on usage), or internal microservices talking to each other.
When NOT to use API keys: Do not use them when you need to authenticate individual users in a web or mobile app. Do not embed them in client-side code where they can be extracted. Do not use them when you need fine-grained permissions or automatic expiration.
OAuth 2.0 is not an authentication protocol — it is an authorization framework that lets users grant third-party applications limited access to their data without sharing their password. The classic example: when you click "Sign in with Google" on a website, you are using OAuth 2.0.
Here is the flow in plain English:
The magic here is that CoolApp never saw your Google password. You authenticated with Google directly. Google then authorized CoolApp to access specific resources (your Drive files) on your behalf. This is called delegated authorization.
OAuth 2.0 has several grant types (flows):
OAuth 2.0 is complex to implement but solves a hard problem: letting users grant granular permissions to third-party apps without sharing credentials. If you are building "Login with [YourService]" or integrating with third-party APIs (Google, GitHub, Spotify), you will use OAuth 2.0.
JWT (JSON Web Token) is a compact, self-contained token format that encodes information in three parts separated by dots: header.payload.signature.
Let's break down each part:
Header — Metadata about the token (algorithm used, token type)
{ "alg": "HS256", "typ": "JWT" }Payload — The actual data (claims) about the user
{ "userId": 42, "email": "[email protected]", "exp": 1735689600 }Signature — Cryptographic signature to verify the token hasn't been tampered with
HMACSHA256(base64(header) + "." + base64(payload), secret)
When a user logs in, your server creates a JWT, signs it with a secret key, and sends it to the client. The client stores it (usually in localStorage or a cookie) and includes it in the Authorization: Bearer <token> header on every request. The server verifies the signature to ensure the token is valid and hasn't been modified.
The key benefit of JWT is that it is stateless. The server does not need to store active sessions in a database. All the information needed to identify the user is in the token itself. This makes JWTs perfect for microservices and distributed systems — any service can verify the token without talking to a central auth server.
Tokens should have an expiration (exp claim). A common pattern is short-lived access tokens (15 minutes) paired with long-lived refresh tokens (30 days). When the access token expires, the client uses the refresh token to get a new access token without making the user log in again.
Security note: JWTs are signed, not encrypted. Anyone can base64-decode a JWT and read its contents. Never put sensitive information like passwords or credit card numbers in a JWT payload. Only put data you are okay with the client seeing (user ID, roles, expiration).
There are two main approaches to maintaining user authentication across requests: sessions and tokens. Each has trade-offs.
Session-Based Authentication (Traditional)
When a user logs in, the server creates a session object, stores it in memory or a database, and sends the client a session ID (usually in a cookie). On each request, the client sends the session ID, the server looks it up in storage, and loads the user's data.
Pros:
Cons:
Token-Based Authentication (Modern)
When a user logs in, the server generates a JWT, signs it, and sends it to the client. The client stores it and includes it in the Authorization header on every request. The server verifies the signature and extracts user data from the token payload.
Pros:
Cons:
When to use sessions: Traditional server-rendered web apps where the backend and frontend are tightly coupled. Admin panels. Situations where you need instant revocation (e.g., ban a user and they lose access immediately).
When to use tokens: REST APIs. Mobile apps. Single-page applications. Microservices. Any scenario where you need stateless, scalable authentication across multiple services or domains.
Many modern apps use a hybrid approach: sessions for the main web app, and tokens for the mobile API.
What is the main difference between authentication and authorization?