9.1 KiB
JWT Authentication Architecture
Last Updated: January 28, 2026
Status: ✅ Implemented (Step 9 Complete)
Overview
Pokedex.Online uses JWT (JSON Web Token) authentication for admin access to protected features like the Gamemaster Manager. The system provides:
- Token-based authentication (stateless)
- 7-day token expiration with refresh capability
- Permission-based access control
- Secure localStorage token storage
- Automatic Bearer token injection in API requests
Architecture Diagram
┌─────────────────────────────────────────────────────────────┐
│ Client (Browser) │
├─────────────────────────────────────────────────────────────┤
│ │
│ AdminLogin View │
│ ↓ (password) │
│ useAuth Composable (login → POST /auth/login) │
│ ↓ (token received) │
│ localStorage.setItem('auth_token', token) │
│ ↓ │
│ api-client.setDefaultHeader('Authorization', Bearer ...) │
│ ↓ │
│ Router Guards (requiresAdmin check) │
│ ↓ │
│ Protected Routes (GamemasterManager, etc.) │
│ │
└─────────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────────┐
│ Server (Express) │
├─────────────────────────────────────────────────────────────┤
│ │
│ Auth Routes (server/routes/auth.js) │
│ ↓ /auth/login (POST) → verifyPassword │
│ ↓ createToken(payload, secret, expiration) │
│ ↓ return { token, user, expiresIn } │
│ │
│ Auth Middleware (server/middleware/auth.js) │
│ ↓ authMiddleware() → verifyToken(token) │
│ ↓ requirePermission() → check permissions │
│ ↓ requireAdmin() → check isAdmin flag │
│ │
│ Protected Routes │
│ ↓ /api/gamemaster/* (with @authMiddleware) │
│ ↓ /api/feature-flags/* (with @requirePermission) │
│ │
└─────────────────────────────────────────────────────────────┘
File Structure
Frontend:
├── src/
│ ├── composables/
│ │ └── useAuth.js # Auth state management
│ ├── views/
│ │ └── AdminLogin.vue # Login page
│ ├── router/
│ │ └── guards.js # Route protection
│ └── utilities/
│ └── api-client.js # Enhanced with header mgmt
│
Backend:
├── server/
│ ├── utils/
│ │ └── jwt-utils.js # Token creation/validation
│ ├── middleware/
│ │ └── auth.js # Auth middleware
│ └── routes/
│ └── auth.js # Auth endpoints
│
Tests:
└── tests/unit/
├── composables/
│ └── useAuth.test.js # Auth composable tests
└── views/
└── AdminLogin.test.js # Login page tests
API Endpoints
POST /auth/login
Authenticate with password to receive JWT token.
Request:
{
"password": "admin-password"
}
Response (Success):
{
"success": true,
"token": "eyJhbGc...",
"expiresIn": 604800,
"user": {
"isAdmin": true,
"permissions": ["admin", "gamemaster-edit"]
}
}
Response (Error):
{
"error": "Invalid password",
"code": "INVALID_PASSWORD"
}
POST /auth/verify
Verify that a token is valid and get its metadata.
Request:
{
"token": "eyJhbGc..."
}
Response:
{
"valid": true,
"user": {
"isAdmin": true,
"permissions": ["admin", "gamemaster-edit"]
},
"expiresIn": 3600,
"expiresAt": "2026-02-04T17:48:00Z"
}
POST /auth/refresh
Extend token expiration.
Request:
{
"token": "eyJhbGc..."
}
Response:
{
"success": true,
"token": "eyJhbGc...",
"expiresIn": 604800
}
GET /auth/user
Get current user information (requires valid token).
Headers:
Authorization: Bearer eyJhbGc...
Response:
{
"user": {
"isAdmin": true,
"permissions": ["admin", "gamemaster-edit"],
"loginTime": "2026-01-28T17:48:00Z"
}
}
POST /auth/logout
Logout endpoint (token invalidation happens client-side).
Response:
{
"success": true,
"message": "Logged out successfully"
}
Frontend Integration
Using useAuth in Components
import { useAuth } from '../composables/useAuth.js';
export default {
setup() {
const {
login, // login(password)
logout, // logout()
token, // Reactive token ref
user, // Reactive user ref
isAuthenticated, // Boolean computed
isAdmin, // Boolean computed
hasPermission // (perm) => boolean
} = useAuth();
return {
login,
logout,
token,
user,
isAuthenticated,
isAdmin,
hasPermission
};
}
};
Protected Routes
import { setupAuthGuards } from './router/guards.js';
const router = createRouter({ ... });
setupAuthGuards(router);
// In route definitions:
{
path: '/gamemaster-manager',
component: GamemasterManager,
meta: { requiresAdmin: true } // Protected route
}
Setting Up Auth on App Startup
import { useAuth } from './composables/useAuth.js';
export default {
async setup() {
const { initializeAuth, setupAuthInterceptor } = useAuth();
// Initialize from localStorage
await initializeAuth();
// Set up automatic token injection
setupAuthInterceptor();
}
};
Security Considerations
✅ Implemented
- JWT tokens with HMAC-SHA256 signature
- 7-day expiration with refresh capability
- Bearer token in Authorization header
- Token stored in localStorage (browser-side)
- Permission-based access control
- Server-side token validation
⚠️ Recommendations
For production deployment:
- HTTPS Only: Ensure all authentication over HTTPS
- Environment Secrets: Store JWT_SECRET in environment variables
- Password Hashing: Use bcrypt for password hashing on server
- CSRF Protection: Add CSRF tokens for state-changing requests
- Rate Limiting: Implement rate limiting on /auth/login endpoint
- Secure Cookies: Consider httpOnly cookies for sensitive tokens
- Token Rotation: Implement token rotation for long-lived sessions
- Logout Blacklist: Maintain blacklist of revoked tokens (if needed)
Current Limitations
- Passwords stored in environment/config (suitable for single admin scenario)
- No multi-user support yet (planned for future)
- Tokens valid until expiration (no immediate revocation)
- Single secret key (key rotation not yet implemented)
Testing
Tests verify:
✅ useAuth composable state management
✅ AdminLogin component rendering and interaction
✅ Login/logout flow
✅ Token state in localStorage
✅ Permission checking logic
✅ Router guard integration
Run tests:
npm test # Watch mode
npm test -- AdminLogin # Specific test
npm run test:coverage # Coverage report
Next Steps (Phase 3: Steps 10-12)
- Feature flags system leveraging JWT permissions
- Secure backend configuration management
- Environment-based flag toggleput
- Flag caching and invalidation
- Admin panel for flag management
Related Documentation:
- PROGRESS.md - Full refactoring progress
- REFACTORING-PLAN.md - Complete 68-step plan