JWT Authentication System Explained
Building secure APIs requires robust authentication mechanisms. In this guide, weโll explore how to implement a production-grade JWT (JSON Web Token) authentication system using Golang, complete with access tokens, refresh tokens, and comprehensive security measures.
๐ฐ The Castle Analogy
- Signup = Creating a new citizen account
- Login = Getting your daily entry pass (Access Token) + monthly pass (Refresh Token)
- Access Token = 15-minute temporary badge to enter castle rooms
- Refresh Token = 7-day voucher to get new temporary badges without re-login
- Logout = Throwing away your passes (client-side)
Why Two Tokens?
- Short-lived for security
- If stolen, attacker only has 15 minutes
- Sent with every API request
- Long-lived for convenience
- Used ONLY to get new access tokens
- Stored securely (httpOnly cookie in production)
- If stolen, attacker canโt access API directly, only get new access tokens
๐ Sequence Diagrams
1. Signup Flow

2. Login Flow

3. Refresh Token Flow

4. Protected API Request Flow

๐ JWT Security Deep Dive
JWT Structure
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzIiwiZW1haWwiOiJqb2huQGV4YW1wbGUuY29tIiwiZXhwIjoxNzAwMDAwMDAwfQ.signature_here
Header (Base64) Payload (Base64) Signature (HMAC-SHA256)
1. Header
{
"alg": "HS256", // HMAC with SHA-256
"typ": "JWT"
}
2. Payload (Claims)
{
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"email": "john.doe@example.com",
"exp": 1732560000, // Expiry timestamp
"iat": 1732559100 // Issued at timestamp
}
3. Signature
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret_key
)
๐ก๏ธ Security Measures in This Implementation
// bcrypt with cost factor 12
// Input: "SecurePass123!"
// Output: "$2a$12$KIXxPz8vjT7lQ.zF3rF5h.BqV8..."
// - Salt automatically generated (random per password)
// - Computationally expensive (prevents brute force)
// - One-way hash (can't reverse)Why bcrypt cost 12?
- Cost 10 = 77ms to hash
- Cost 12 = 250ms to hash โ Sweet spot
- Cost 14 = 1 second to hash
Higher cost = slower login BUT much harder to crack if database leaked.
Access Token: 15 minutes
- Stolen token? Attacker has max 15 min
- User stays logged in via refresh tokens
- Sent with every request (higher exposure risk)
Refresh Token: 7 days
- Used rarely (only to refresh)
- Lower exposure risk
- If stolen, attacker can keep generating access tokens
- Should be stored in httpOnly cookie (future improvement)
// Step 1: Parse token structure
token, err := jwt.ParseWithClaims(...)
// Step 2: Verify signature (prevents tampering)
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return ErrInvalidToken
}
// Step 3: Check expiry
if errors.Is(err, jwt.ErrTokenExpired) {
return ErrExpiredToken
}
// Step 4: Validate claims
claims, ok := token.Claims.(*Claims)
if !ok || !token.Valid {
return ErrInvalidToken
}// Not just validating token...
// Also checking if user still active in DB
user, err := s.userRepo.FindByID(ctx, claims.UserID)
if !user.IsActive {
return ErrUnauthorized // User deactivated? No refresh!
}๐จ Attack Scenarios & Protections
Attack: Hacker gets user table with password hashes
Protection:
- bcrypt hashes canโt be reversed
- Cracking one password takes hours/days
- Each password has unique salt
Attack: Malicious JS steals token from localStorage
Protection:
- Only valid for 15 minutes
- After expiry, attacker is locked out
- User can change password โ invalidates refresh token
Attack: Attacker intercepts refresh token
Protection (Current):
- Must know when to use it (only when access expires)
- User can logout โ delete their tokens client-side
Future Improvement:
- Token rotation: New refresh token with each use
- Token families: Detect if old refresh token reused
- httpOnly cookies: JS canโt access
Attack: Intercept HTTP requests
Protection:
- HTTPS/TLS in production (Render auto-provides)
- Tokens encrypted in transit
Attack: Modify token payload (change user_id)
Protection:
- HMAC signature verification
- Any change โ signature invalid โ rejected
๐ Token Lifecycle
Day 1, 9:00 AM - User logs in
โโ Access Token: Valid until 9:15 AM
โโ Refresh Token: Valid until Day 8, 9:00 AM
Day 1, 9:14 AM - Access token about to expire
โโ Client checks expiry
โโ Calls /refresh with refresh token
Day 1, 9:14 AM - Server issues new tokens
โโ NEW Access Token: Valid until 9:29 AM
โโ NEW Refresh Token: Valid until Day 8, 9:14 AM (rotation)
Day 1, 9:28 AM - Again, access token expiring
โโ Repeat refresh flow
Day 8, 9:00 AM - Refresh token expired
โโ User must login again (full authentication)
๐ Code Flow Summary
Signup:
- Validate email/phone not exists
- Hash password (bcrypt cost 12)
- Save user to database
- Generate access + refresh tokens
- Return tokens to client
Login:
- Find user by email
- Compare password hash
- Check user is active
- Generate tokens
- Update last_login
- Return tokens
Refresh:
- Validate refresh token signature
- Check token not expired
- Find user by ID from token
- Check user still active
- Generate NEW access token
- Generate NEW refresh token (rotation)
- Return new tokens
Protected Request:
- Extract token from header
- Validate token
- Extract user ID from claims
- Inject into request context
- Handler accesses user ID
- Process request
๐ฏ Key Takeaways
This implementation provides a production-grade JWT authentication system with:
- โ Short-lived access tokens (15 min) for security
- โ Long-lived refresh tokens (7 days) for convenience
- โ bcrypt password hashing (cost 12) for protection
- โ HMAC-SHA256 signatures to prevent tampering
- โ User status validation on every refresh
- โ Comprehensive error handling for all edge cases
- โ Context-based user injection for clean handler code
๐ฎ Future Enhancements
While this system is production-ready, here are some improvements to consider:
- Token Rotation: Issue new refresh token on each refresh
- Token Families: Track refresh token lineage to detect reuse
- httpOnly Cookies: Store refresh tokens in httpOnly cookies
- Rate Limiting: Prevent brute force attacks on login/refresh
- Device Tracking: Associate tokens with devices for better security
- Token Blacklisting: Ability to revoke tokens before expiry
- Multi-Factor Authentication: Add 2FA for additional security
๐ Further Reading
- JWT.io - JWT specification and debugger
- OWASP Authentication Cheat Sheet
- Go JWT Library Documentation
This authentication system demonstrates best practices for securing APIs with JWT tokens in Golang. The dual-token approach balances security and user experience, while the comprehensive validation ensures your API remains protected against common attack vectors.