API authentication and security are critical for protecting your services and user data. A single vulnerability can expose sensitive information, allow unauthorized access, or compromise your entire system. Proper testing ensures your authentication mechanisms work correctly and your APIs are secure.
This guide covers the most common authentication methods - JWT, OAuth 2.0, and API keys - along with security best practices and testing strategies. You'll learn what to test, how to test it, and common vulnerabilities to watch for.
JWT (JSON Web Token) Authentication
JWT is one of the most popular authentication methods for REST APIs. It's a self-contained token that carries user information and can be verified without database lookups. But if not implemented correctly, JWTs can be vulnerable to attacks.
Understanding JWT Structure
A JWT consists of three parts: Header, Payload, and Signature, separated by dots.
// JWT Token Structure
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
// Decoded:
// Header
{
"alg": "HS256", // Algorithm
"typ": "JWT" // Token type
}
// Payload
{
"sub": "1234567890", // Subject (user ID)
"name": "John Doe", // Custom claims
"role": "admin",
"iat": 1516239022, // Issued at (timestamp)
"exp": 1516242622 // Expiration (timestamp)
}
// Signature = HMACSHA256(
// base64(header) + "." + base64(payload),
// secret_key
// )✓ Testing JWT Authentication Flow
Test the complete authentication flow: login, token generation, token validation, and token expiration.
// 1. Login - Get JWT Token
POST /api/auth/login
Content-Type: application/json
{
"email": "[email protected]",
"password": "SecurePass123!"
}
Response: 200 OK
{
"token": "eyJhbGc...",
"refresh_token": "eyJhbGc...",
"expires_in": 3600,
"token_type": "Bearer"
}
// 2. Use Token for Protected Endpoint
GET /api/users/me
Authorization: Bearer eyJhbGc...
Response: 200 OK
{
"id": "123",
"name": "John Doe",
"email": "[email protected]"
}
// 3. Try Without Token
GET /api/users/me
Response: 401 Unauthorized
{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required"
}
}
// 4. Try With Invalid Token
GET /api/users/me
Authorization: Bearer invalid_token
Response: 401 Unauthorized
{
"error": {
"code": "INVALID_TOKEN",
"message": "Invalid or malformed token"
}
}JWT Test Cases:
- ✓ Valid credentials return valid JWT
- ✓ Invalid credentials are rejected
- ✓ Token works for protected endpoints
- ✓ Expired tokens are rejected
- ✓ Tampered tokens are rejected
- ✓ Missing tokens return 401
- ✓ Tokens have reasonable expiration time
- ✓ Refresh tokens work to get new access tokens
Critical JWT Security Tests
These vulnerabilities are common in JWT implementations. Always test for them.
Test #1: Algorithm Confusion Attack
Change algorithm from RS256 (asymmetric) to HS256 (symmetric) and sign with public key. Server should reject this.
// Original token uses RS256
{"alg": "RS256", "typ": "JWT"}
// Attacker changes to HS256
{"alg": "HS256", "typ": "JWT"}
// Server must reject this!Test #2: None Algorithm Attack
Set algorithm to "none" (no signature). Server should reject unsigned tokens.
{"alg": "none", "typ": "JWT"}
// Server must require signature!Test #3: Token Tampering
Modify payload (change role from user to admin) without valid signature. Server should reject.
// Original: {"role": "user"}
// Modified: {"role": "admin"}
// Without re-signing = Invalid!Test #4: Expired Token Usage
Try using expired token. Server should check exp claim and reject.
{"exp": 1516239022} // Past timestamp
// Server must check and reject!✓ JWT Best Practices to Test
- ✓Use strong algorithms: RS256 or ES256, not HS256 with weak secrets
- ✓Set reasonable expiration: 15 minutes to 1 hour for access tokens
- ✓Use refresh tokens: Long-lived refresh tokens to get new access tokens
- ✓Don't store sensitive data: JWT payload is readable (base64, not encrypted)
- ✓Validate all claims: Check exp, iss (issuer), aud (audience)
- ✓Use HTTPS only: Never send JWT over unencrypted HTTP
OAuth 2.0 Authentication
OAuth 2.0 is the industry standard for authorization. It allows users to grant third-party applications limited access to their resources without sharing passwords. Testing OAuth flows is more complex due to multiple steps and redirect URLs.
Authorization Code Flow (Most Common)
The most secure OAuth 2.0 flow for web applications. Test each step of the flow.
// Step 1: Redirect user to authorization endpoint
GET /oauth/authorize?
response_type=code&
client_id=YOUR_CLIENT_ID&
redirect_uri=https://yourapp.com/callback&
scope=read:profile,write:posts&
state=random_state_string
// User logs in and approves...
// Step 2: Authorization server redirects back with code
GET https://yourapp.com/callback?
code=AUTH_CODE_HERE&
state=random_state_string
// Step 3: Exchange code for access token
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=AUTH_CODE_HERE&
client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET&
redirect_uri=https://yourapp.com/callback
Response: 200 OK
{
"access_token": "ya29.a0AfH6...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "1//0gJw...",
"scope": "read:profile write:posts"
}
// Step 4: Use access token
GET /api/user/profile
Authorization: Bearer ya29.a0AfH6...
Response: 200 OK
{
"id": "123",
"name": "John Doe",
"email": "[email protected]"
}OAuth 2.0 Test Cases:
- ✓ Authorization endpoint validates client_id
- ✓ Authorization endpoint validates redirect_uri
- ✓ State parameter prevents CSRF attacks
- ✓ Authorization code can only be used once
- ✓ Authorization code expires (5-10 minutes)
- ✓ Token endpoint validates client credentials
- ✓ Access token works for API requests
- ✓ Refresh token can get new access tokens
- ✓ Scopes are properly enforced
OAuth 2.0 Security Tests
Test #1: CSRF Protection (State Parameter)
Try callback without state parameter or with mismatched state. Server should reject.
Test #2: Redirect URI Validation
Try authorization with attacker's redirect_uri. Server should only allow pre-registered URLs.
Test #3: Authorization Code Reuse
Try using the same authorization code twice. Second attempt should be rejected.
Test #4: Scope Enforcement
Try accessing resources beyond granted scopes. Server should return 403 Forbidden.
Other OAuth 2.0 Flows
Implicit Flow (Deprecated)
Directly returns access token in URL fragment. Less secure, avoid if possible. Use Authorization Code with PKCE instead.
Client Credentials Flow
Machine-to-machine authentication. Client directly exchanges credentials for access token. Test that client_id and client_secret are validated.
Password Grant (Not Recommended)
User provides username/password directly to app. Only use for trusted first-party apps. Prefer Authorization Code flow.
PKCE (Proof Key for Code Exchange)
Extension for Authorization Code flow. Prevents authorization code interception. Essential for mobile and SPA apps.
API Key Authentication
API keys are the simplest authentication method - just a secret string that identifies the caller. While easy to implement, they require careful handling to stay secure.
✓ Using API Keys Securely
API keys can be sent in headers, query parameters, or request body. Headers are most secure.
✓ RECOMMENDED - Header:
GET /api/data X-API-Key: sk_live_51H... // or Authorization: ApiKey sk_live_51H... // Pros: // - Not logged in URLs // - Not cached by browsers // - Not visible in history
⚠LESS SECURE - Query:
GET /api/data?api_key=sk_live_51H... // Cons: // - Logged in server logs // - Cached by browsers // - Visible in history // - Shared in URLs
API Key Test Cases:
- ✓ Valid API key grants access
- ✓ Invalid API key is rejected (401)
- ✓ Missing API key is rejected (401)
- ✓ Expired/revoked keys are rejected
- ✓ Rate limits are enforced per key
- ✓ Keys can be rotated/regenerated
- ✓ Different keys have different permissions
✓ API Key Best Practices
Generate Long, Random Keys
Use at least 32 characters from cryptographically secure random source. Don't use predictable patterns.
✓ sk_live_51HxYz... (good)✗ user123_key (predictable)Use Prefix to Identify Key Type
Prefix makes it easy to identify leaked keys and rotate them. Common pattern: environment_purpose_random
sk_live_... (secret key, production)pk_test_... (public key, test)Store Keys Securely
Hash keys before storing in database (like passwords). Only show full key once during creation.
Implement Rate Limiting
Prevent abuse by limiting requests per key. Return 429 Too Many Requests when limit exceeded.
Provide Key Management
Allow users to create, list, revoke, and rotate keys. Never send keys via email or insecure channels.
Monitor for Leaks
Monitor GitHub, logs, and error messages for accidentally exposed keys. Automatically revoke if detected.
General API Security Testing
Always Use HTTPS
Never send authentication credentials over unencrypted HTTP. Test that your API rejects or redirects HTTP requests.
HTTPS Tests:
- ✓ API only accepts HTTPS connections
- ✓ HTTP requests redirect to HTTPS or are rejected
- ✓ HSTS header is set (Strict-Transport-Security)
- ✓ Valid SSL/TLS certificate
- ✓ Strong cipher suites (TLS 1.2+)
✓ Configure CORS Properly
Cross-Origin Resource Sharing (CORS) controls which domains can access your API from browsers.
❌ DANGEROUS:
Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true // Allows ANY domain with credentials! // Major security risk!
✓ SECURE:
Access-Control-Allow-Origin: https://yourapp.com Access-Control-Allow-Credentials: true // Only allows specific domains // Much safer!
✓ Implement Rate Limiting
Prevent brute force attacks and abuse by limiting requests per IP, user, or API key.
// Rate limit exceeded response
Response: 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1641902400
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Retry after 60 seconds."
}
}Rate Limiting Tests:
- ✓ Returns 429 when limit exceeded
- ✓ Includes Retry-After header
- ✓ Resets after time window
- ✓ Different limits for authenticated/anonymous
- ✓ Login endpoints have strict limits (prevent brute force)
✓ Validate and Sanitize All Input
Test that your API properly validates input to prevent injection attacks.
SQL Injection Test
Try: '; DROP TABLE users; --
Should be rejected or escaped properly
XSS (Cross-Site Scripting) Test
Try: <script>alert('XSS')</script>
Should be sanitized in output
Path Traversal Test
Try: ../../etc/passwd
Should not access files outside allowed paths
Command Injection Test
Try: ; rm -rf /
Should never execute system commands from user input
✓ Test Authorization (Not Just Authentication)
Authentication verifies who you are. Authorization verifies what you can do. Test both!
// User A (authenticated) tries to access User B's data
GET /api/users/456/profile
Authorization: Bearer [User A's token]
Response: 403 Forbidden
{
"error": {
"code": "FORBIDDEN",
"message": "You don't have permission to access this resource"
}
}
// Admin can access any user
GET /api/users/456/profile
Authorization: Bearer [Admin token]
Response: 200 OK
{ "id": 456, "name": "User B", ... }
// Test Cases:
// ✓ Users can only access their own data
// ✓ Admins can access all data
// ✓ Roles are properly enforced
// ✓ Can't escalate privileges
// ✓ Horizontal privilege escalation prevented
// ✓ Vertical privilege escalation preventedAPI Security Testing Checklist
✓ Authentication Tests
- ✓Valid credentials grant access
- ✓Invalid credentials are rejected
- ✓Tokens expire properly
- ✓Tampered tokens are rejected
- ✓Refresh tokens work correctly
- ✓Logout invalidates tokens
✓ Authorization Tests
- ✓Users can't access other users' data
- ✓Role-based permissions enforced
- ✓Privilege escalation prevented
- ✓Admin-only endpoints protected
- ✓Scope/permissions are validated
- ✓403 returned for forbidden actions
✓ Security Tests
- ✓HTTPS enforced
- ✓CORS configured properly
- ✓Rate limiting works
- ✓Input validation prevents injection
- ✓No sensitive data in URLs/logs
- ✓Security headers present
✗ Vulnerabilities to Check
- ✗SQL injection
- ✗XSS (Cross-Site Scripting)
- ✗CSRF attacks
- ✗Path traversal
- ✗Mass assignment
- ✗Exposed sensitive data
Related Tools & Resources
External References
Official Documentation & Standards
- JWT Introduction - Official JSON Web Token guide
- OAuth 2.0 - Official OAuth 2.0 specification
- RFC 7519 - JWT Standard - JSON Web Token RFC specification
- RFC 6749 - OAuth 2.0 - OAuth 2.0 authorization framework
- OWASP API Security Project - API security top 10 risks
- REST Security Cheat Sheet - OWASP REST API security guide
Conclusion
API authentication and security are non-negotiable. A single vulnerability can compromise your entire system and expose sensitive user data. By thoroughly testing your authentication mechanisms - whether JWT, OAuth 2.0, or API keys - you protect your API from common attacks and ensure only authorized users can access resources.
Remember to test not just the happy path, but all failure scenarios: expired tokens, tampered credentials, missing authentication, insufficient permissions, and common attack vectors like SQL injection and XSS. Always use HTTPS, implement rate limiting, validate all input, and enforce proper authorization. Security testing should be part of your regular development workflow, not an afterthought. Start testing today and build APIs that users can trust.