Advanced25 min read

API Authentication Patterns

Master API authentication strategies including API keys, OAuth 2.0, JWT tokens, and understand when to use each pattern.

Authentication vs Authorization

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: Simple but Limited

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:

  • Easy to implement on both client and server
  • Easy for developers to use (just copy-paste the key)
  • Works perfectly for server-to-server communication
  • Can be scoped to specific permissions (read-only key, write key, etc.)

Cons:

  • If the key leaks, anyone can use it until you revoke it
  • No built-in expiration — keys are valid forever unless manually revoked
  • Cannot represent individual users — only applications
  • If stored in frontend code, they are visible to anyone who inspects the page

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: Delegated Authorization

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:

  1. User action: You visit CoolApp and click "Connect to Google Drive"
  2. Redirect: CoolApp redirects you to Google's authorization page
  3. User consent: Google asks: "Do you want to let CoolApp read your Drive files?" You click "Yes"
  4. Authorization code: Google redirects you back to CoolApp with a temporary code
  5. Token exchange: CoolApp sends that code to Google's server (along with a secret key) and receives an access token
  6. API calls: CoolApp uses that access token to call Google Drive API on your behalf

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):

  • Authorization Code — the full flow described above, used for web apps where there is a backend server
  • Client Credentials — for server-to-server communication (no user involved), the app authenticates as itself
  • Implicit — (deprecated) was used for single-page apps, now replaced by Authorization Code with PKCE
  • Refresh Token — lets clients get new access tokens without asking the user to log in again

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: Stateless Token-Based Authentication

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)

json
{ "alg": "HS256", "typ": "JWT" }

Payload — The actual data (claims) about the user

json
{ "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).

Session-Based vs Token-Based Authentication

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:

  • Easy to revoke (just delete the session from storage)
  • Can store unlimited data server-side
  • Familiar pattern, works great for traditional web apps

Cons:

  • Requires server-side storage (memory, Redis, database)
  • Hard to scale horizontally (sessions must be shared across servers)
  • Not ideal for APIs consumed by mobile apps or third parties

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:

  • Stateless — no server-side storage required
  • Scales easily (any server can verify the token)
  • Works seamlessly across domains and services
  • Perfect for mobile apps and SPAs

Cons:

  • Harder to revoke (token is valid until expiration)
  • Token size matters (sent on every request)
  • Requires careful secret management

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?

Ready to practice?

Create your free account to access the interactive code editor, run challenges, and track your progress.