code flows, Token structure, Spring Security and FastAPI implementations, and production
best practices.
seo_title: 'API Security Authentication: OAuth 2.0 and JWT Practice Guide · 16IDC'
seo_keywords: API security, OAuth2, JWT, API authentication, token authentication,
identity authentication
seo_description: In-depth comparison of OAuth 2.0 and JWT for API security, covering
authorization code flows, Token structure, Spring Security and FastAPI implementations,
and best practices.
published_at: '2026-07-18'
status: active

API Security Authentication: OAuth 2.0 and JWT in Practice

API security authentication is the foundation of modern web applications and microservice architectures. OAuth 2.0 and JWT are the two most popular authentication and authorization technology stacks, yet developers often confuse their respective responsibilities. OAuth 2.0 defines an authorization framework, solving the problem of "who can access what resources," while JWT is a Token format commonly used to pass identity and authorization information within OAuth 2.0 flows. This article systematically covers the selection and integration of these two technologies, from principles to practical implementation.

OAuth 2.0 Authorization Framework Explained

Core Roles and Flow

OAuth 2.0 defines four core roles:

Role Description Real-World Example
Resource Owner The user who owns the protected resource Logged-in user
Client The application accessing resources on behalf of the resource owner Web frontend, Mobile App
Authorization Server Authenticates the resource owner and issues Tokens Auth0, Keycloak, Spring Authorization Server
Resource Server Hosts the protected resources Backend API service

Four Grant Types Comparison

Grant Type Use Case Security Level Token Issuance Method
Authorization Code Web apps, Mobile apps ★★★★★ Token obtained via authorization code exchange
Implicit (deprecated) Pure frontend apps (removed in OAuth 2.1) ★★ Access Token returned directly
Resource Owner Password Highly trusted first-party apps ★★ Token exchanged directly with username/password
Client Credentials Service-to-service, machine-to-machine ★★★★ Token exchanged with Client ID + Secret

Recommended Approach: The 2026 best practice is to uniformly adopt the Authorization Code + PKCE (Proof Key for Code Exchange) flow. PKCE adds a code_challenge parameter to the authorization request, preventing replay attacks even if the authorization code is intercepted, making it secure even in pure frontend apps without a Client Secret.

Authorization Code + PKCE Complete Flow

User → Client: Clicks "Sign in with Google"
Client → Google: Generates code_verifier and code_challenge, initiates authorization request
Google → Browser: Displays login page
User → Google: Enters credentials
Google → Client: Callback URL returns authorization code
Client → Google: Sends authorization code + code_verifier
Google → Client: Verifies code_verifier matches code_challenge, returns Access Token + Refresh Token
Client → API: Includes Access Token in request header
API → Client: Validates Token and returns resource

JWT Structure and Signing Mechanism

Token Composition

JWT consists of three Base64URL-encoded parts, separated by dots (.):

header.payload.signature

Header: Declares the Token type and signing algorithm

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "key-id-2026"
}

Payload: Carries Claims information

{
  "sub": "user_12345",
  "iss": "https://auth.16idc.com",
  "aud": ["api-prod", "api-staging"],
  "exp": 1777888800,
  "iat": 1777802400,
  "scope": "read:orders write:orders",
  "roles": ["admin", "operator"]
}

Signature: The core anti-tampering mechanism

RSASHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  privateKey
)

Signature Algorithm Selection

Algorithm Type Key Management Performance Recommended Use
HS256 Symmetric Single key, hard to distribute Fast Internal microservices
RS256 Asymmetric Public/private key pair, easy to distribute Medium Cross-service/cross-org authentication
ES256 Asymmetric (Elliptic Curve) Shorter keys, same security Faster Mobile apps, IoT devices

Production Advice: Use asymmetric algorithms (RS256 or ES256). The authorization server holds the private key for signing, while resource servers only need the public key for verification. The public key can be dynamically obtained via the /.well-known/jwks.json endpoint, supporting key rotation.

Practical Code Examples

Spring Boot Resource Server Configuration

// build.gradle dependency
// implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'

@Configuration
@EnableWebSecurity
public class ResourceServerConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public/**").permitAll()
                .requestMatchers("/api/admin/**").hasAuthority("SCOPE_admin")
                .requestMatchers("/api/orders/**").hasAuthority("SCOPE_read:orders")
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt
                    .jwkSetUri("https://auth.16idc.com/.well-known/jwks.json")
                    .jwtAuthenticationConverter(jwtAuthenticationConverter())
                )
            );
        return http.build();
    }

    // Custom role mapping
    private JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
        converter.setAuthorityPrefix("ROLE_");
        converter.setAuthoritiesClaimName("roles");
        JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
        jwtConverter.setJwtGrantedAuthoritiesConverter(converter);
        return jwtConverter;
    }
}

FastAPI + PyJWT Example

from fastapi import FastAPI, Depends, HTTPException, Security
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt
from datetime import datetime, timedelta

app = FastAPI()
security = HTTPBearer()

# Configuration (should be read from environment variables in production)
SECRET_KEY = "your-rs256-private-key"
ALGORITHM = "RS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

def create_access_token(data: dict):
    to_encode = data.copy()
    expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire, "iat": datetime.utcnow()})
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

def verify_token(credentials: HTTPAuthorizationCredentials = Security(security)):
    token = credentials.credentials
    try:
        payload = jwt.decode(
            token, SECRET_KEY, algorithms=[ALGORITHM],
            audience="api-prod"
        )
        return payload
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="Token has expired")
    except jwt.InvalidAudienceError:
        raise HTTPException(status_code=401, detail="Token Audience mismatch")
    except jwt.PyJWTError:
        raise HTTPException(status_code=401, detail="Token validation failed")

@app.get("/api/orders")
async def get_orders(payload: dict = Depends(verify_token)):
    # Extract user info from payload to query orders
    user_id = payload.get("sub")
    # Query order logic...
    return {"user_id": user_id, "orders": [...]}

Common Security Attacks and Prevention

Attack Type Principle Prevention
Token Leakage Token intercepted by a man-in-the-middle Enforce HTTPS, short Token validity, Refresh Token rotation
CSRF (Cross-Site Request Forgery) Exploits an authenticated user session to send malicious requests Use SameSite Cookie, CSRF Token, OAuth state parameter
JWT None Attack Tamper with the alg field to "none" to bypass validation Server-side algorithm whitelist, reject "none"
JWT Key Confusion Sign with the public key, tricking the server into verifying with it Strictly separate signing and verification keys, use JWK Set URL
Replay Attack Intercept a valid Token and reuse it Use jti (JWT ID) one-time Token, short validity
Missing PKCE Authorization code can be stolen if intercepted Always use PKCE extension, validate code_challenge

Refresh Token and Token Lifecycle Management

In production, relying solely on a single Access Token is not enough. A well-designed Token lifecycle strategy balances security and user experience:

Access Token validity: 15 ~ 30 minutes
Refresh Token validity: 7 ~ 30 days
Refresh Token rotation: Issue a new Refresh Token after each refresh; the old Refresh Token is immediately invalidated

Token Refresh Flow

@app.post("/auth/refresh")
async def refresh_token(refresh_token: str):
    try:
        payload = jwt.decode(
            refresh_token, SECRET_KEY, algorithms=[ALGORITHM],
            options={"verify_exp": True}
        )
        # Check if the Refresh Token has been revoked
        if is_token_revoked(payload["jti"]):
            raise HTTPException(status_code=401, detail="Token has been revoked")

        # Issue new tokens
        new_access = create_access_token({"sub": payload["sub"]})
        new_refresh = create_refresh_token({"sub": payload["sub"]})

        # Revoke the old Refresh Token
        revoke_token(payload["jti"])

        return {
            "access_token": new_access,
            "refresh_token": new_refresh,
            "token_type": "Bearer",
            "expires_in": 1800
        }
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="Refresh Token has expired, please log in again")

What's New in OAuth 2.1

As of 2026, the OAuth 2.1 specification has gradually become the industry-recommended standard. Compared to OAuth 2.0, the key changes include:

Change OAuth 2.0 OAuth 2.1
Implicit Flow Supported Removed
Password Flow Supported Removed (except for highly trusted scenarios)
PKCE Optional Required
Refresh Token Rotation Optional Recommended
Redirect URI Exact Match Recommended Required

Conclusion

The OAuth 2.0 + JWT combination has become the de facto standard for API security authentication. For technology stack selection in 2026, the following production-grade configuration is recommended:

  1. Authorization Protocol: Adopt OAuth 2.1 principles, uniformly use Authorization Code + PKCE flow
  2. Token Format: JWT with RS256 or ES256 asymmetric signing algorithm
  3. Key Management: Dynamically manage public keys via JWKS Endpoint, support key rotation
  4. Lifecycle: Access Token 15~30 minutes, Refresh Token with rotation mechanism
  5. Security Hardening: Enforce HTTPS, validate aud/iss claims, monitor abnormal Token usage patterns

Whether you are building microservice APIs, SPA frontend applications, or mobile apps, the above solution provides enterprise-grade security for your API.