Now.js Framework Documentation
TokenService - JWT Token Management
TokenService - JWT Token Management
เอกสารสำหรับ TokenService ซึ่งเป็น Low-level JWT Token Management Service ของ Now.js Framework
📋 สารบัญ
- ภาพรวม
- การติดตั้งและนำเข้า
- การเริ่มต้นใช้งาน
- Configuration
- JWT Token Operations
- Token Validation
- Storage Management
- Cookie Operations
- User Data Extraction
- ตัวอย่างการใช้งาน
- API Reference
- Best Practices
- Common Pitfalls
- Security Considerations
ภาพรวม
TokenService เป็น Low-level service ที่จัดการ JWT tokens ทั้งหมด รวมถึง parsing, validation, storage, และ extraction ของข้อมูลจาก token payload
ฟีเจอร์หลัก
- ✅ JWT Parsing: แยก JWT token และ decode payload
- ✅ Token Validation: ตรวจสอบความถูกต้องและ expiry
- ✅ Storage Management: จัดการ token storage (Cookie/localStorage)
- ✅ Cookie Operations: เขียน/อ่าน httpOnly cookies
- ✅ User Data Extraction: ดึงข้อมูล user จาก token payload
- ✅ Expiry Checking: ตรวจสอบว่า token หมดอายุหรือไม่
- ✅ Base64URL Decoding: รองรับ Base64URL encoding standard
- ✅ Multiple Storage Methods: Cookie, localStorage
เมื่อไหร่ควรใช้ TokenService
✅ ใช้ TokenService เมื่อ:
- ต้องการ parse JWT token manually
- ต้องการตรวจสอบ token validity
- ต้องการ extract ข้อมูล user จาก token
- ต้องการจัดการ token storage โดยตรง
- สร้าง custom authentication logic
❌ ไม่ควรใช้เมื่อ:
- ใช้งานผ่าน AuthManager แล้ว (AuthManager ใช้ TokenService internally)
- ไม่ต้องการจัดการ tokens ในระดับ low-level
การติดตั้งและนำเข้า
TokenService โหลดมาพร้อมกับ Now.js Framework:
<!-- Load TokenService -->
<script src="/Now/js/TokenService.js"></script>หลังจากโหลด TokenService จะพร้อมใช้งานผ่าน:
window.TokenService- Class definitionwindow.tokenService- Default instance (pre-initialized)
// Use default instance
console.log(window.tokenService); // TokenService instance
// Create custom instance
const customTokenService = new TokenService({
storageMethod: 'cookie',
cookieName: 'my_token'
});การเริ่มต้นใช้งาน
Using Default Instance
// Default instance is ready to use
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
// Parse token
const payload = tokenService.parseToken(token);
console.log('User ID:', payload.sub);
console.log('Expiry:', new Date(payload.exp * 1000));
// Check if expired
const expired = tokenService.isTokenExpired(token);
console.log('Token expired:', expired);Creating Custom Instance
// Create custom instance with options
const myTokenService = new TokenService({
storageMethod: 'cookie', // 'cookie' or 'localStorage'
cookieName: 'auth_token', // Cookie name for access token
refreshCookieName: 'refresh_token', // Cookie name for refresh token
localStorageKey: 'auth', // localStorage key
cookieOptions: {
path: '/',
secure: true, // HTTPS only
sameSite: 'Strict' // CSRF protection
}
});
// Use custom instance
const payload = myTokenService.parseToken(token);Configuration
Constructor Options
const tokenService = new TokenService({
// Storage method: 'cookie' or 'localStorage'
storageMethod: 'cookie',
// Cookie names
cookieName: 'auth_token',
refreshCookieName: 'refresh_token',
// localStorage key
localStorageKey: 'auth',
// Cookie options
cookieOptions: {
path: '/', // Cookie path
secure: false, // HTTPS only (auto-detect from location.protocol)
sameSite: 'Lax' // 'Strict', 'Lax', or 'None'
}
});Storage Methods
1. Cookie Storage (Default)
const tokenService = new TokenService({
storageMethod: 'cookie',
cookieName: 'auth_token',
cookieOptions: {
path: '/',
secure: true, // HTTPS only
sameSite: 'Strict' // Prevent CSRF
}
});
// Store token in cookie
tokenService.store(token, { user: userData });
// Note: For httpOnly cookies, the cookie must be set by the server
// JavaScript cannot read httpOnly cookies (this is a security feature)Advantages:
- ✅ Can use httpOnly flag (server-side)
- ✅ Automatically sent with requests
- ✅ CSRF protection with SameSite
Disadvantages:
- ❌ Size limit (~4KB)
- ❌ httpOnly cookies cannot be read by JavaScript
2. localStorage Storage
const tokenService = new TokenService({
storageMethod: 'localStorage',
localStorageKey: 'auth'
});
// Store token in localStorage
tokenService.store(token, { user: userData });
// Get token
const storedToken = tokenService.getToken();
console.log('Token:', storedToken);Advantages:
- ✅ Larger storage (~5-10MB)
- ✅ Can be accessed by JavaScript
- ✅ Persists across browser sessions
Disadvantages:
- ❌ Vulnerable to XSS attacks
- ❌ Not automatically sent with requests
- ❌ Shared across tabs
Cookie Options
{
path: '/', // Cookie available on all paths
secure: true, // Send over HTTPS only
sameSite: 'Strict', // 'Strict', 'Lax', or 'None'
maxAge: 3600, // Max age in seconds
expires: new Date() // Expiration date
}SameSite Values:
| Value | Description | Use Case |
|---|---|---|
Strict |
Cookie only sent to same-site requests | Recommended - Strongest CSRF protection |
Lax |
Cookie sent on top-level navigation | Good balance between security and usability |
None |
Cookie sent on all requests (requires secure: true) |
Cross-site requests, APIs |
Example:
const tokenService = new TokenService({
storageMethod: 'cookie',
cookieOptions: {
path: '/',
secure: location.protocol === 'https:', // Auto-detect HTTPS
sameSite: 'Strict', // Prevent CSRF
maxAge: 15 * 60 // 15 minutes
}
});JWT Token Operations
1. Parse JWT Token
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
// Parse token
const payload = tokenService.parseToken(token);
console.log(payload);
// {
// sub: "1234567890",
// name: "John Doe",
// iat: 1516239022,
// exp: 1516242622
// }Token Structure:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 <- Header (Base64URL)
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwi... <- Payload (Base64URL)
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJ... <- Signature (Base64URL)Payload Example:
{
"sub": "1234567890", // Subject (User ID)
"name": "John Doe", // User name
"email": "john@example.com",
"roles": ["user", "admin"],
"permissions": ["read:posts", "write:posts"],
"iat": 1516239022, // Issued at (timestamp)
"exp": 1516242622 // Expiry (timestamp)
}2. Validate Token Format
// Check if token is valid JWT format
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
const payload = tokenService.parseToken(token);
if (payload) {
console.log('Valid JWT token');
console.log('User:', payload.name);
} else {
console.log('Invalid JWT token');
}
// Invalid tokens return null
const invalidToken = 'not-a-jwt-token';
const result = tokenService.parseToken(invalidToken);
console.log(result); // null3. Get Token Expiry
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
// Get expiry timestamp (seconds)
const expiry = tokenService.getTokenExpiry(token);
if (expiry) {
const expiryDate = new Date(expiry * 1000);
console.log('Token expires at:', expiryDate);
// Calculate time remaining
const now = Date.now();
const remaining = (expiry * 1000) - now;
const minutes = Math.floor(remaining / 1000 / 60);
console.log('Time remaining:', minutes, 'minutes');
} else {
console.log('Token has no expiry or is invalid');
}Token Validation
1. Check Token Expiry
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
// Check if token is expired
const expired = tokenService.isTokenExpired(token);
if (expired) {
console.log('Token has expired');
// Need to refresh or re-login
} else {
console.log('Token is still valid');
// Can use token for requests
}Behind the scenes:
// How isTokenExpired works:
const payload = tokenService.parseToken(token);
if (!payload || !payload.exp) {
return true; // No expiry = treat as expired
}
const now = Math.floor(Date.now() / 1000);
return now >= payload.exp;2. Check Token Near Expiry
// Check if token expires within 5 minutes
function isTokenNearExpiry(token, minutes = 5) {
const expiry = tokenService.getTokenExpiry(token);
if (!expiry) {
return true;
}
const now = Math.floor(Date.now() / 1000);
const remaining = expiry - now;
const threshold = minutes * 60;
return remaining <= threshold;
}
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
if (isTokenNearExpiry(token, 5)) {
console.log('Token expires soon - refresh recommended');
// Trigger token refresh
}3. Validate Token Structure
// Comprehensive token validation
function validateToken(token) {
// Check token format
if (!token || typeof token !== 'string') {
return { valid: false, reason: 'Invalid token format' };
}
// Check JWT structure (3 parts)
const parts = token.split('.');
if (parts.length !== 3) {
return { valid: false, reason: 'Invalid JWT structure' };
}
// Parse token
const payload = tokenService.parseToken(token);
if (!payload) {
return { valid: false, reason: 'Cannot parse token' };
}
// Check required fields
if (!payload.sub && !payload.id) {
return { valid: false, reason: 'Missing user ID' };
}
// Check expiry
if (tokenService.isTokenExpired(token)) {
return { valid: false, reason: 'Token expired' };
}
return { valid: true, payload };
}
// Usage
const result = validateToken(token);
if (result.valid) {
console.log('Token is valid');
console.log('User ID:', result.payload.sub);
} else {
console.error('Token validation failed:', result.reason);
}Storage Management
1. Store Token
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
const userData = {
id: 1,
name: 'John Doe',
email: 'john@example.com',
roles: ['user']
};
// Store token with user data
const stored = tokenService.store(token, { user: userData });
if (stored) {
console.log('Token stored successfully');
} else {
console.error('Failed to store token');
}Storage Behavior:
Cookie Storage:
// Stores token in cookie (not httpOnly from JS)
// Structure: auth_token=eyJhbGc...
tokenService.setCookie('auth_token', token, {
path: '/',
secure: true,
sameSite: 'Strict',
maxAge: 900 // 15 minutes
});localStorage Storage:
// Stores token and user data in localStorage
// Structure: { token: '...', user: {...} }
localStorage.setItem('auth', JSON.stringify({
token: token,
user: userData
}));2. Get Token
// Get stored token
const token = tokenService.getToken();
if (token) {
console.log('Token found:', token);
// Validate token
if (!tokenService.isTokenExpired(token)) {
console.log('Token is valid');
} else {
console.log('Token expired');
}
} else {
console.log('No token found');
}Note: For httpOnly cookies, getToken() will return null because JavaScript cannot access httpOnly cookies. The server must validate the token from the cookie header.
3. Get User Data
// Get stored user data (localStorage only)
const user = tokenService.getUser();
if (user) {
console.log('User:', user.name);
console.log('Email:', user.email);
console.log('Roles:', user.roles);
} else {
console.log('No user data found');
}4. Remove Token
// Remove token from storage
const removed = tokenService.remove();
if (removed) {
console.log('Token removed successfully');
} else {
console.error('Failed to remove token');
}
// Verify removal
const token = tokenService.getToken();
console.log('Token after removal:', token); // null5. Clear All Data
// Clear all authentication data
const cleared = tokenService.clear();
if (cleared) {
console.log('All data cleared successfully');
}
// This will clear:
// - Access token
// - Refresh token
// - User data
// - Any other stored auth dataCookie Operations
1. Set Cookie
// Set cookie with options
tokenService.setCookie('auth_token', token, {
path: '/',
secure: true,
sameSite: 'Strict',
maxAge: 900 // 15 minutes
});
// Set cookie with expiry date
tokenService.setCookie('auth_token', token, {
path: '/',
secure: true,
sameSite: 'Strict',
expires: new Date(Date.now() + 15 * 60 * 1000)
});2. Get Cookie
// Get cookie value
const token = tokenService.getCookie('auth_token');
if (token) {
console.log('Token:', token);
} else {
console.log('Cookie not found');
}
// Note: Cannot read httpOnly cookies
const httpOnlyToken = tokenService.getCookie('httponly_token');
console.log(httpOnlyToken); // null (if httpOnly flag is set)3. Delete Cookie
// Delete cookie by setting maxAge to 0
tokenService.setCookie('auth_token', '', {
path: '/',
maxAge: 0
});
// Or set expiry to past date
tokenService.setCookie('auth_token', '', {
path: '/',
expires: new Date(0)
});
// Verify deletion
const token = tokenService.getCookie('auth_token');
console.log('Token after deletion:', token); // nullUser Data Extraction
1. Extract User ID
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
// Get user ID from token
const userId = tokenService.getUserId(token);
console.log('User ID:', userId);
// Standard JWT uses 'sub' field
// Falls back to 'id' field if 'sub' not found2. Extract User Roles
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
// Get user roles from token
const roles = tokenService.getUserRoles(token);
console.log('User roles:', roles);
// Returns: ['user', 'admin']
// Returns: [] if no roles found
// Check specific role
if (roles.includes('admin')) {
console.log('User is admin');
}3. Extract Custom Claims
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
// Parse token to get all claims
const payload = tokenService.parseToken(token);
if (payload) {
// Standard claims
console.log('User ID:', payload.sub);
console.log('Name:', payload.name);
console.log('Email:', payload.email);
console.log('Issued at:', new Date(payload.iat * 1000));
console.log('Expires at:', new Date(payload.exp * 1000));
// Custom claims
console.log('Roles:', payload.roles);
console.log('Permissions:', payload.permissions);
console.log('Subscription:', payload.subscription);
console.log('Organization:', payload.org);
// Any other custom fields in your JWT
console.log('Custom field:', payload.customField);
}ตัวอย่างการใช้งาน
1. Basic Token Validation
// Validate and use token
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
// Parse token
const payload = tokenService.parseToken(token);
if (!payload) {
console.error('Invalid token format');
return;
}
// Check expiry
if (tokenService.isTokenExpired(token)) {
console.error('Token has expired');
return;
}
// Extract user data
const userId = tokenService.getUserId(token);
const roles = tokenService.getUserRoles(token);
console.log('Valid token!');
console.log('User ID:', userId);
console.log('Roles:', roles);
// Use token for API request
fetch('/api/users', {
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(response => response.json())
.then(data => console.log('API response:', data));2. Token Storage with Cookie
// Initialize with cookie storage
const tokenService = new TokenService({
storageMethod: 'cookie',
cookieName: 'auth_token',
cookieOptions: {
path: '/',
secure: true,
sameSite: 'Strict'
}
});
// Login and store token
async function handleLogin(credentials) {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
});
const data = await response.json();
if (data.success) {
// Store token in cookie
tokenService.store(data.token, { user: data.user });
console.log('Login successful!');
console.log('Token stored in cookie');
// Redirect to dashboard
window.location.href = '/dashboard';
}
}
// Check authentication on page load
function checkAuth() {
const token = tokenService.getToken();
if (!token) {
console.log('No token found - user not logged in');
window.location.href = '/login';
return;
}
if (tokenService.isTokenExpired(token)) {
console.log('Token expired - please login again');
tokenService.clear();
window.location.href = '/login';
return;
}
console.log('User is authenticated');
}
// Run on page load
checkAuth();3. Token Storage with localStorage
// Initialize with localStorage
const tokenService = new TokenService({
storageMethod: 'localStorage',
localStorageKey: 'auth'
});
// Login and store token
async function handleLogin(credentials) {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
});
const data = await response.json();
if (data.success) {
// Store token in localStorage
tokenService.store(data.token, { user: data.user });
console.log('Login successful!');
console.log('Token stored in localStorage');
// Get stored data
const storedToken = tokenService.getToken();
const storedUser = tokenService.getUser();
console.log('Stored token:', storedToken);
console.log('Stored user:', storedUser);
}
}
// Get user data on page load
function loadUserData() {
const token = tokenService.getToken();
const user = tokenService.getUser();
if (!token || !user) {
window.location.href = '/login';
return;
}
if (tokenService.isTokenExpired(token)) {
tokenService.clear();
window.location.href = '/login';
return;
}
// Display user info
document.getElementById('userName').textContent = user.name;
document.getElementById('userEmail').textContent = user.email;
}
// Run on page load
loadUserData();4. Token Refresh Logic
// Automatic token refresh
async function ensureValidToken() {
let token = tokenService.getToken();
// No token - need to login
if (!token) {
window.location.href = '/login';
return null;
}
// Token expired - try to refresh
if (tokenService.isTokenExpired(token)) {
console.log('Token expired - refreshing...');
// Get refresh token
const refreshToken = tokenService.getCookie('refresh_token');
if (!refreshToken) {
console.log('No refresh token - need to login');
tokenService.clear();
window.location.href = '/login';
return null;
}
try {
// Call refresh endpoint
const response = await fetch('/api/auth/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refresh_token: refreshToken })
});
const data = await response.json();
if (data.success) {
// Store new token
tokenService.store(data.token);
token = data.token;
console.log('Token refreshed successfully');
} else {
console.log('Token refresh failed - need to login');
tokenService.clear();
window.location.href = '/login';
return null;
}
} catch (error) {
console.error('Token refresh error:', error);
tokenService.clear();
window.location.href = '/login';
return null;
}
}
return token;
}
// Use before API requests
async function makeAuthenticatedRequest(url) {
const token = await ensureValidToken();
if (!token) {
return;
}
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${token}`
}
});
return response.json();
}
// Usage
const users = await makeAuthenticatedRequest('/api/users');
console.log('Users:', users);5. Role-Based Access
// Check user permissions from token
function checkPermissions(token, requiredRoles = []) {
// Validate token
if (!token || tokenService.isTokenExpired(token)) {
return false;
}
// Get user roles from token
const userRoles = tokenService.getUserRoles(token);
// No required roles - allow access
if (requiredRoles.length === 0) {
return true;
}
// Check if user has any required role
return requiredRoles.some(role => userRoles.includes(role));
}
// Usage
const token = tokenService.getToken();
// Check admin access
if (checkPermissions(token, ['admin'])) {
console.log('User has admin access');
document.getElementById('adminPanel').style.display = 'block';
}
// Check moderator or admin access
if (checkPermissions(token, ['admin', 'moderator'])) {
console.log('User has moderator or admin access');
document.getElementById('modPanel').style.display = 'block';
}
// Check regular user access
if (checkPermissions(token, ['user', 'admin', 'moderator'])) {
console.log('User has access');
document.getElementById('content').style.display = 'block';
}6. Token Debugging Tool
// Debug token information
function debugToken(token) {
console.group('Token Debug Info');
// Basic info
console.log('Token:', token);
console.log('Token length:', token.length);
// Structure
const parts = token.split('.');
console.log('Parts:', parts.length);
// Parse token
const payload = tokenService.parseToken(token);
if (!payload) {
console.error('❌ Invalid token format');
console.groupEnd();
return;
}
console.log('✅ Valid JWT format');
// Payload info
console.group('Payload');
console.log('User ID:', tokenService.getUserId(token));
console.log('Roles:', tokenService.getUserRoles(token));
console.log('Full payload:', payload);
console.groupEnd();
// Expiry info
const expiry = tokenService.getTokenExpiry(token);
const expired = tokenService.isTokenExpired(token);
console.group('Expiry');
console.log('Expiry timestamp:', expiry);
console.log('Expiry date:', new Date(expiry * 1000));
console.log('Is expired:', expired);
if (!expired) {
const now = Date.now();
const remaining = (expiry * 1000) - now;
const minutes = Math.floor(remaining / 1000 / 60);
console.log('Time remaining:', minutes, 'minutes');
}
console.groupEnd();
// Storage info
console.group('Storage');
const storedToken = tokenService.getToken();
console.log('Stored token:', storedToken ? 'Found' : 'Not found');
const user = tokenService.getUser();
console.log('Stored user:', user ? user.name : 'Not found');
console.groupEnd();
console.groupEnd();
}
// Usage
const token = tokenService.getToken();
if (token) {
debugToken(token);
}API Reference
Constructor
new TokenService(options)
Create a new TokenService instance.
Parameters:
options(Object, optional) - Configuration optionsstorageMethod(string) - 'cookie' or 'localStorage' (default: 'cookie')cookieName(string) - Cookie name for access token (default: 'auth_token')refreshCookieName(string) - Cookie name for refresh token (default: 'refresh_token')localStorageKey(string) - localStorage key (default: 'auth')cookieOptions(Object) - Cookie options
Returns: TokenService instance
Example:
const tokenService = new TokenService({
storageMethod: 'cookie',
cookieName: 'auth_token',
cookieOptions: {
path: '/',
secure: true,
sameSite: 'Strict'
}
});Methods
parseToken(token)
Parse JWT token and extract payload.
Parameters:
token(string) - JWT token
Returns: Object|null - Token payload or null if invalid
Example:
const payload = tokenService.parseToken(token);
if (payload) {
console.log('User ID:', payload.sub);
console.log('Expiry:', payload.exp);
}isTokenExpired(token)
Check if token is expired.
Parameters:
token(string) - JWT token
Returns: boolean - True if expired or invalid
Example:
if (tokenService.isTokenExpired(token)) {
console.log('Token expired');
} else {
console.log('Token valid');
}getTokenExpiry(token)
Get token expiration timestamp.
Parameters:
token(string) - JWT token
Returns: number|null - Expiry timestamp in seconds or null
Example:
const expiry = tokenService.getTokenExpiry(token);
if (expiry) {
const date = new Date(expiry * 1000);
console.log('Expires at:', date);
}getUserId(token)
Extract user ID from token.
Parameters:
token(string) - JWT token
Returns: string|number|null - User ID or null
Example:
const userId = tokenService.getUserId(token);
console.log('User ID:', userId);getUserRoles(token)
Extract user roles from token.
Parameters:
token(string) - JWT token
Returns: Array<string> - Array of roles (empty array if none)
Example:
const roles = tokenService.getUserRoles(token);
console.log('Roles:', roles);getToken()
Get stored token from storage.
Returns: string|null - Token or null
Note: Returns null for httpOnly cookies (cannot be accessed by JavaScript)
Example:
const token = tokenService.getToken();
if (token) {
console.log('Token found');
}get()
Alias for getToken().
Returns: string|null - Token or null
Example:
const token = tokenService.get();store(token, additionalData)
Store token in configured storage.
Parameters:
token(string) - JWT token to storeadditionalData(Object, optional) - Additional data to store (e.g., user object)
Returns: boolean - True if stored successfully
Example:
const stored = tokenService.store(token, {
user: { id: 1, name: 'John' }
});remove()
Remove token from storage.
Returns: boolean - True if removed successfully
Example:
const removed = tokenService.remove();
if (removed) {
console.log('Token removed');
}clear()
Clear all stored authentication data.
Returns: boolean - True if cleared successfully
Example:
tokenService.clear();getUser()
Get stored user data (localStorage only).
Returns: Object|null - User object or null
Example:
const user = tokenService.getUser();
if (user) {
console.log('User:', user.name);
}isAuthenticated()
Check if user is authenticated (has valid token).
Returns: boolean - True if authenticated
Example:
if (tokenService.isAuthenticated()) {
console.log('User is authenticated');
}setCookie(name, value, options)
Set a cookie.
Parameters:
name(string) - Cookie namevalue(string) - Cookie valueoptions(Object, optional) - Cookie optionspath(string) - Cookie pathsecure(boolean) - HTTPS onlysameSite(string) - 'Strict', 'Lax', or 'None'maxAge(number) - Max age in secondsexpires(Date) - Expiration date
Returns: void
Example:
tokenService.setCookie('auth_token', token, {
path: '/',
secure: true,
sameSite: 'Strict',
maxAge: 900
});getCookie(name)
Get a cookie value.
Parameters:
name(string) - Cookie name
Returns: string|null - Cookie value or null
Example:
const token = tokenService.getCookie('auth_token');Best Practices
1. Always Validate Tokens
// ✅ Good - validate before use
const token = tokenService.getToken();
if (token && !tokenService.isTokenExpired(token)) {
// Use token
makeAuthenticatedRequest(token);
} else {
// Token invalid or expired
console.log('Please login');
}
// ❌ Bad - use without validation
const token = tokenService.getToken();
makeAuthenticatedRequest(token); // May fail!2. Use Cookie Storage for Web Apps
// ✅ Good - cookie storage (can be httpOnly on server)
const tokenService = new TokenService({
storageMethod: 'cookie',
cookieOptions: {
path: '/',
secure: true,
sameSite: 'Strict'
}
});
// ❌ Bad - localStorage (vulnerable to XSS)
const tokenService = new TokenService({
storageMethod: 'localStorage'
});3. Handle Parsing Errors
// ✅ Good - handle parsing errors
const payload = tokenService.parseToken(token);
if (!payload) {
console.error('Invalid token format');
return;
}
console.log('User ID:', payload.sub);
// ❌ Bad - assume parsing succeeds
const payload = tokenService.parseToken(token);
console.log('User ID:', payload.sub); // May throw error!4. Check Expiry Before API Calls
// ✅ Good - check expiry first
async function makeRequest(url) {
const token = tokenService.getToken();
if (!token || tokenService.isTokenExpired(token)) {
// Token expired - refresh or login
await refreshToken();
}
return fetch(url, {
headers: { 'Authorization': `Bearer ${token}` }
});
}
// ❌ Bad - don't check expiry
async function makeRequest(url) {
const token = tokenService.getToken();
return fetch(url, {
headers: { 'Authorization': `Bearer ${token}` }
}); // Will get 401 if expired
}5. Clear Data on Logout
// ✅ Good - clear all data
function logout() {
tokenService.clear();
console.log('All data cleared');
window.location.href = '/login';
}
// ❌ Bad - partial cleanup
function logout() {
tokenService.remove(); // Only removes token, not user data
window.location.href = '/login';
}Common Pitfalls
1. Accessing httpOnly Cookies
// ❌ Bad - cannot access httpOnly cookies from JavaScript
const token = tokenService.getCookie('auth_token');
console.log(token); // null (if httpOnly)
// ✅ Good - httpOnly cookies are handled by browser automatically
// Server sets: Set-Cookie: auth_token=...; HttpOnly; Secure
// Browser sends cookie automatically with every request
fetch('/api/users', {
credentials: 'include' // Include cookies
})
.then(response => response.json());2. Not Checking Token Validity
// ❌ Bad - use token without checking
const token = tokenService.getToken();
fetch('/api/users', {
headers: { 'Authorization': `Bearer ${token}` }
});
// ✅ Good - validate token first
const token = tokenService.getToken();
if (!token || tokenService.isTokenExpired(token)) {
console.log('Invalid token');
return;
}
fetch('/api/users', {
headers: { 'Authorization': `Bearer ${token}` }
});3. Storing Sensitive Data in JWT
// ❌ Bad - sensitive data in JWT
// Server should NOT put this in JWT:
{
sub: "123",
name: "John Doe",
password: "secret123", // DON'T DO THIS!
creditCard: "1234-5678", // DON'T DO THIS!
ssn: "123-45-6789" // DON'T DO THIS!
}
// ✅ Good - minimal data in JWT
{
sub: "123",
name: "John Doe",
email: "john@example.com",
roles: ["user"],
iat: 1516239022,
exp: 1516242622
}4. Using Long-Lived Tokens
// ❌ Bad - long-lived access tokens
// Server: JWT expiry = 30 days
const payload = {
sub: "123",
exp: Math.floor(Date.now() / 1000) + (30 * 24 * 60 * 60) // 30 days
};
// ✅ Good - short-lived access tokens
// Server: JWT expiry = 15 minutes
const payload = {
sub: "123",
exp: Math.floor(Date.now() / 1000) + (15 * 60) // 15 minutes
};
// Use refresh token for longer sessions5. Not Handling Malformed Tokens
// ❌ Bad - no error handling
const payload = tokenService.parseToken(malformedToken);
console.log(payload.sub); // Error if token is invalid!
// ✅ Good - handle errors
const payload = tokenService.parseToken(malformedToken);
if (!payload) {
console.error('Invalid token');
return;
}
console.log('User ID:', payload.sub);Security Considerations
1. XSS Protection
// ✅ Use httpOnly cookies (server-side)
// Server: Set-Cookie: auth_token=...; HttpOnly; Secure; SameSite=Strict
// ✅ Sanitize user input
function sanitize(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
const payload = tokenService.parseToken(token);
if (payload) {
document.getElementById('userName').textContent = sanitize(payload.name);
}
// ❌ DON'T use innerHTML with token data
// document.getElementById('userName').innerHTML = payload.name; // XSS risk!2. Token Storage
// ✅ Best - httpOnly cookies (server-side)
// Server: Set-Cookie: auth_token=...; HttpOnly
// ✅ Good - regular cookies with secure flags
const tokenService = new TokenService({
storageMethod: 'cookie',
cookieOptions: {
secure: true,
sameSite: 'Strict'
}
});
// ❌ Avoid - localStorage (XSS vulnerable)
// const tokenService = new TokenService({
// storageMethod: 'localStorage'
// });3. Token Validation
// ✅ Always validate tokens
function useToken(token) {
// Validate format
const payload = tokenService.parseToken(token);
if (!payload) {
return false;
}
// Validate expiry
if (tokenService.isTokenExpired(token)) {
return false;
}
// Validate required fields
if (!payload.sub && !payload.id) {
return false;
}
return true;
}
// ❌ Don't assume tokens are valid
// Just because a token exists doesn't mean it's valid4. Secure Cookie Options
// ✅ Production settings
const tokenService = new TokenService({
storageMethod: 'cookie',
cookieOptions: {
secure: true, // HTTPS only
sameSite: 'Strict', // Prevent CSRF
path: '/' // Accessible on all paths
}
});
// ❌ Insecure settings
// const tokenService = new TokenService({
// cookieOptions: {
// secure: false, // Allow HTTP
// sameSite: 'None' // Allow cross-site
// }
// });5. Token Expiry
// ✅ Check expiry before use
const token = tokenService.getToken();
if (!token || tokenService.isTokenExpired(token)) {
// Refresh or re-login
await refreshToken();
}
// ❌ Don't use expired tokens
// const token = tokenService.getToken();
// makeRequest(token); // May be expired!6. Minimal Token Payload
// ✅ Minimal payload (server-side)
const payload = {
sub: userId, // User ID only
roles: userRoles, // Essential roles
exp: expiry // Expiration
};
// ❌ Large payload
// const payload = {
// sub: userId,
// user: fullUserObject, // Too much data!
// profile: fullProfile, // Too much data!
// settings: allSettings // Too much data!
// };Browser Compatibility
TokenService รองรับ browsers สมัยใหม่:
- ✅ Chrome 90+
- ✅ Firefox 88+
- ✅ Safari 14+
- ✅ Edge 90+
- ❌ IE 11 (ไม่รองรับ)
Required Features:
- ES6+ features (arrow functions, destructuring, etc.)
- Web Storage API (localStorage)
- Base64 encoding/decoding
- Cookies API
Related Documentation
- Authentication.md - Authentication system overview
- AuthManager.md - Core authentication manager
- AuthGuard.md - Route protection
- AuthErrorHandler.md - Error handling
- AuthLoadingManager.md - Loading states