# JWT Token Reference Guide ## 🎯 What's in Your JWT Token Your JWT tokens now contain all essential user information, so you don't need to make database calls for basic user data. --- ## 📦 Token Payload Contents ### Access Token & Refresh Token Include: ```javascript { // User Identification userId: "507f1f77bcf86cd799439011", // MongoDB _id steamId: "76561198012345678", // Steam ID64 // Profile Information (NEW!) username: "YourSteamName", // Display name avatar: "https://avatars.cloudflare.steamstatic.com/...", // Profile picture URL // Permissions staffLevel: 0, // 0=User, 1=Support, 2=Mod, 3=Admin // JWT Standard Claims iat: 1704825600, // Issued at (timestamp) exp: 1704826500, // Expires at (timestamp) iss: "turbotrades", // Issuer aud: "turbotrades-api" // Audience } ``` --- ## 🔍 How to Access Token Data ### Frontend (Browser) The tokens are in httpOnly cookies, so JavaScript can't read them directly. But you can: #### Option 1: Decode from API Response ```javascript // After login or on page load, call this endpoint const response = await fetch('/auth/decode-token', { credentials: 'include' // Send cookies }); const data = await response.json(); console.log(data.decoded); // { // userId: "...", // steamId: "...", // username: "YourName", // avatar: "https://...", // staffLevel: 0, // ... // } ``` #### Option 2: Get from /auth/me Endpoint ```javascript const response = await fetch('/auth/me', { credentials: 'include' }); const data = await response.json(); console.log(data.user); // Full user object from database ``` ### Backend (Server-Side) When you use the `authenticate` middleware, the decoded token data is available: ```javascript import { authenticate } from './middleware/auth.js'; fastify.get('/protected', { preHandler: authenticate }, async (request, reply) => { // Full user object from database console.log(request.user.username); console.log(request.user.avatar); return { message: `Hello ${request.user.username}!` }; }); ``` --- ## 🎨 Frontend Usage Examples ### React Component ```javascript import { useState, useEffect } from 'react'; function UserProfile() { const [user, setUser] = useState(null); useEffect(() => { // Get user data from token fetch('/auth/decode-token', { credentials: 'include' }) .then(res => res.json()) .then(data => { if (data.success) { setUser(data.decoded); } }); }, []); if (!user) return
Loading...
; return (
{user.username}

{user.username}

Steam ID: {user.steamId}

{user.staffLevel > 0 && Staff}
); } ``` ### Vue Component ```vue ``` ### Vanilla JavaScript ```javascript // Get user info on page load async function loadUserInfo() { try { const response = await fetch('/auth/decode-token', { credentials: 'include' }); const data = await response.json(); if (data.success) { const user = data.decoded; // Update UI document.getElementById('username').textContent = user.username; document.getElementById('avatar').src = user.avatar; // Store in memory if needed window.currentUser = user; } } catch (error) { console.error('Failed to load user:', error); } } loadUserInfo(); ``` --- ## 🔐 Security Notes ### Why httpOnly Cookies? ✅ **Prevents XSS attacks** - JavaScript can't access the token ✅ **Automatic sending** - Browser sends cookies automatically ✅ **Secure storage** - Tokens stored securely by browser ### Token Lifetimes - **Access Token:** 15 minutes (short-lived for security) - **Refresh Token:** 7 days (for convenience) When access token expires: 1. Frontend gets 401 error with "TokenExpired" 2. Call `/auth/refresh` to get new tokens 3. Retry the original request --- ## 📡 API Endpoints Reference ### Check Token Contents ```bash GET /auth/decode-token # With cookie (automatic in browser) curl http://localhost:3000/auth/decode-token \ --cookie "accessToken=YOUR_TOKEN" # Response: { "success": true, "decoded": { "userId": "...", "steamId": "...", "username": "...", "avatar": "...", "staffLevel": 0, "iat": 1704825600, "exp": 1704826500 } } ``` ### Get Full User Profile ```bash GET /auth/me curl http://localhost:3000/auth/me \ --cookie "accessToken=YOUR_TOKEN" # Response: { "success": true, "user": { "_id": "...", "username": "...", "steamId": "...", "avatar": "...", "balance": 0, "email": {...}, "staffLevel": 0, ... } } ``` ### Refresh Tokens ```bash POST /auth/refresh curl -X POST http://localhost:3000/auth/refresh \ --cookie "refreshToken=YOUR_REFRESH_TOKEN" # Response: { "success": true, "message": "Tokens refreshed successfully", "accessToken": "new-token", "refreshToken": "new-refresh-token" } ``` --- ## 💡 Best Practices ### ✅ DO - Store tokens in httpOnly cookies (already done) - Use `/auth/decode-token` to get user info for UI - Implement automatic token refresh on 401 errors - Clear tokens on logout - Use HTTPS in production ### ❌ DON'T - Don't store tokens in localStorage (XSS vulnerable) - Don't store sensitive data in tokens (keep them small) - Don't decode tokens client-side if httpOnly (you can't) - Don't use long-lived access tokens --- ## 🔄 Token Refresh Flow ```javascript async function fetchWithAuth(url, options = {}) { // First attempt with existing token let response = await fetch(url, { ...options, credentials: 'include' }); // If token expired, refresh and retry if (response.status === 401) { const error = await response.json(); if (error.error === 'TokenExpired') { // Refresh tokens const refreshResponse = await fetch('/auth/refresh', { method: 'POST', credentials: 'include' }); if (refreshResponse.ok) { // Retry original request response = await fetch(url, { ...options, credentials: 'include' }); } else { // Refresh failed, redirect to login window.location.href = '/login'; } } } return response; } // Usage const data = await fetchWithAuth('/user/profile'); ``` --- ## 📊 Token Size Comparison **Before (without username/avatar):** - Token size: ~200 bytes - Needs database call to get name/avatar **After (with username/avatar):** - Token size: ~350 bytes - No database call needed for basic info - **Still well within JWT size limits (8KB)** --- ## 🎯 Summary **What's in the token:** - ✅ User ID (database reference) - ✅ Steam ID (for Steam API calls) - ✅ Username (display name) - ✅ Avatar URL (profile picture) - ✅ Staff Level (permissions) **How to use it:** - Frontend: Call `/auth/decode-token` or `/auth/me` - Backend: Access via `request.user` (after authenticate middleware) - Automatic: Cookies sent with every request **Benefits:** - No database calls for basic user info - Faster UI rendering - Self-contained authentication - Stateless (can scale horizontally) --- **Your JWT tokens now include everything needed for displaying user information! 🎉**