Authentication in Modern APIs (JWT, OAuth2)
Authentication is the process of verifying a user’s identity before allowing access to protected resources. Modern APIs usually rely on token-based authentication systems like JWT (JSON Web Tokens) and OAuth 2.0 because they are secure, scalable, and suitable for microservices and distributed systems.
1. JWT Authentication (JSON Web Token)
JWT is a stateless token-based authentication mechanism used widely in REST APIs. A server issues a signed token to a user after login, and the user sends this token with every request. The server verifies the signature without storing session data.
Why JWT?
- No server-side session storage (stateless)
- Fast verification using digital signatures
- Easily used across microservices
- Can store additional user claims (role, permissions)
Structure of a JWT
A JWT has three base64-encoded parts:
header.payload.signature
Example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJ1c2VyX2lkIjoxMiwicm9sZSI6ImFkbWluIn0
.
4hxZf4MzrgZJx6aZqGM-6Kf8l1LfPzWdq_i2K6pq5Fk
Header Example (JSON)
{
"alg": "HS256",
"typ": "JWT"
}
Payload Example
{
"user_id": 12,
"role": "admin",
"exp": 1712345678
}
Signature
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret_key
)
How JWT Authentication Works (Flow)
- User sends username and password to login.
- Server validates credentials.
- Server generates JWT and sends it to the client.
- Client stores the token (localStorage or mobile secure storage).
- Client sends JWT in Authorization header with every request.
- Server verifies signature and allows or denies access.
Client Request Example:
GET /api/user
Authorization: Bearer
Implementing JWT in Python (Django REST Framework)
Install Required Packages
pip install djangorestframework-simplejwt
Settings
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
),
}
Token Endpoints
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns = [
path("api/token/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
path("api/token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
]
Protected API View
from rest_framework.permissions import IsAuthenticated
class ProfileView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
return Response({"message": "Profile accessed"})
Security Best Practices for JWT
- Use short expiration time (e.g., 5–15 minutes)
- Use refresh tokens for long-term authentication
- Store JWT in HTTP-only cookies or secure storage
- Do not store sensitive data inside token
- Use asymmetric signing for large systems (RS256)
2. OAuth 2.0 (Authorization Framework)
OAuth 2.0 is an authorization framework used to allow third-party applications to access user data without exposing passwords. It is more advanced and secure than simple token-based systems and is used by Google, Facebook, GitHub, and major APIs.
Why OAuth2?
- Allows third-party access without sharing passwords
- Supports scopes and permissions
- Ideal for large-scale and public-facing APIs
- Secure token lifecycle management
OAuth2 Key Concepts
1. Resource Owner
The user who owns the data.
2. Client Application
App requesting access to the user's data.
3. Authorization Server
Server that issues tokens (Google, GitHub, etc.).
4. Resource Server
API that hosts protected data.
OAuth2 Grant Types (Flows)
Different flows depending on application type.
- Authorization Code (most secure; used by web apps)
- PKCE (secure version for mobile / SPA)
- Client Credentials (machine-to-machine)
- Password Grant (deprecated; insecure)
- Refresh Token (used to renew access tokens)
OAuth2 Authorization Code Flow (Most Common)
- Client redirects user to Authorization Server login page.
- User logs in and grants permission.
- Authorization Server redirects back with an authorization code.
- Client exchanges the code for an access token.
- Client uses the access token to access protected APIs.
Example Authorization URL:
https://accounts.google.com/o/oauth2/v2/auth?
client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.com/callback
&scope=email profile
&response_type=code
Implementing OAuth2 in Python (FastAPI Example)
Install
pip install fastapi uvicorn python-jose
OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/users/me")
async def read_users_me(token: str = Depends(oauth2_scheme)):
return {"token": token}
Access Token vs Refresh Token
| Access Token | Refresh Token |
|---|---|
| Short lifetime | Long lifetime |
| Sent with every request | Stored securely (never sent with normal API calls) |
| Used to access protected resources | Used to get new access tokens |
OAuth2 Security Best Practices
- Always use HTTPS
- Use PKCE for mobile and SPA apps
- Rotate and revoke tokens
- Use scopes to limit access
- Keep client secrets private
Conclusion
JWT is ideal for stateless authentication in microservices and REST APIs, while OAuth2 is perfect for large, secure, public-facing APIs that require delegated access. Understanding both helps developers choose the right authentication strategy depending on the architecture and security needs.