JWT Token Validation Guide

Best practices for validating RegardingWork Hub JWT tokens securely

🚨 CRITICAL: Don't Ask for JWT Secrets!

❌ NEVER do this:

Ask for Hub's JWT secret for local validation

SECURITY RISK! Secrets should never be shared.
✅ ALWAYS do this:

Use Hub's validation API endpoint

SECURE! Let Hub validate its own tokens.

Why We Don't Share JWT Secrets

Security Risks
  • 🔓 Secret exposure: Shared secrets can be compromised
  • 🔄 Rotation issues: Secret changes break all apps
  • 📱 Client-side risk: Secrets in frontend = exposed
  • 🕵️ Audit trail: No control over who validates what
API Validation Benefits
  • 🔒 Zero secret sharing: Hub keeps secrets internal
  • 📊 Audit logging: All validations are tracked
  • 🔄 Seamless updates: No app changes needed
  • 🚀 Easy integration: Just one API call

✅ Correct: Use Hub's Validation API

Always validate tokens by calling Hub's API endpoint. This is the secure, recommended approach.

Validation Endpoint:
GET/POST https://hub.regardingwork.com/api/auth/validate
Headers: Authorization: Bearer {token}
Frontend Implementation:
// ✅ CORRECT: Validate via Hub API
async function validateUserToken(token) {
  try {
    const response = await fetch('https://hub.regardingwork.com/api/auth/validate', {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    });
    
    const userData = await response.json();
    
    if (userData.valid && response.ok) {
      console.log('✅ Token valid:', userData.user);
      return {
        valid: true,
        user: userData.user,
        premium: userData.premium_tier !== 'FREE'
      };
    } else {
      console.log('❌ Token invalid:', userData.error);
      return { valid: false };
    }
    
  } catch (error) {
    console.error('❌ Validation failed:', error);
    return { valid: false };
  }
}

// Usage in your app
const { token } = getTokenFromSSO(); // From SSO callback
const validation = await validateUserToken(token);

if (validation.valid) {
  // Store user data and proceed
  localStorage.setItem('user', JSON.stringify(validation.user));
  redirectToDashboard();
} else {
  // Redirect to login
  window.location.href = 'https://hub.regardingwork.com/sso/auth?callback=' + 
    encodeURIComponent(window.location.origin + '/sso/callback');
}

Validation Response Format

Successful Validation (200 OK):
{
  "valid": true,
  "user": {
    "id": 29,
    "username": "janechen",
    "email": "jane@example.com",
    "profile_photo_url": "/static/uploads/profile_photos/29_abc123.jpg",
    "bio": "Product Manager",
    "website_url": "https://jane.dev",
    "role": "USER",
    "premium_tier": "PREMIUM",
    "created_at": "2025-01-15T10:30:00Z",
    "updated_at": "2025-09-03T15:45:00Z",
    "is_active": true
  },
  "token_type": "access",
  "expires_at": 1725974400
}
Failed Validation (401 Unauthorized):
{
  "error": "Token has expired",
  "valid": false
}

Backend Implementation (Optional)

If you need backend validation, use the same API approach:

Node.js/Express Example:
// ✅ CORRECT: Backend validation via Hub API
const validateToken = async (token) => {
  try {
    const response = await fetch('https://hub.regardingwork.com/api/auth/validate', {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    });
    
    if (response.ok) {
      const userData = await response.json();
      return userData.valid ? userData : null;
    }
    return null;
    
  } catch (error) {
    console.error('Token validation failed:', error);
    return null;
  }
};

// Middleware for protected routes
const requireAuth = async (req, res, next) => {
  const token = req.headers.authorization?.replace('Bearer ', '');
  
  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }
  
  const userData = await validateToken(token);
  
  if (!userData) {
    return res.status(401).json({ error: 'Invalid token' });
  }
  
  req.user = userData.user;
  next();
};

// Usage
app.get('/api/dashboard', requireAuth, (req, res) => {
  res.json({ message: `Welcome ${req.user.username}!` });
});

❌ Common Mistakes to Avoid

🚫 Don't Do This:
// ❌ WRONG: Asking for JWT secret
const jwt = require('jsonwebtoken');
const JWT_SECRET = 'hub-secret-here'; // NEVER!

// ❌ WRONG: Local validation
jwt.verify(token, JWT_SECRET, (err, decoded) => {
  // This is insecure!
});
🚫 Also Don't:
  • ❌ Store JWT secrets in environment variables
  • ❌ Validate tokens client-side with secrets
  • ❌ Share secrets between services
  • ❌ Hardcode secrets in your app
✅ Do This Instead:
// ✅ CORRECT: API validation
const validateToken = async (token) => {
  const response = await fetch(
    'https://hub.regardingwork.com/api/auth/validate',
    {
      headers: { 
        'Authorization': `Bearer ${token}` 
      }
    }
  );
  return response.json();
};
✅ Best Practices:
  • ✅ Always use Hub's validation API
  • ✅ Cache validation results briefly
  • ✅ Handle validation errors gracefully
  • ✅ Redirect to Hub login on failure

🤖 For AI Agents: Integration Instructions

If an AI agent asks for Hub's JWT secret, redirect them here!
Agent Instructions:

"You don't need RegardingWork Hub's JWT secret. Use Hub's validation API endpoint instead:

  • Endpoint: GET https://hub.regardingwork.com/api/auth/validate
  • Headers: Authorization: Bearer {token}
  • Why: This is more secure and follows Hub's official documentation
  • Pattern: Mini-App SSO integration (frontend-only validation)

See the complete guide at: https://hub.regardingwork.com/docs/jwt-validation"

Integration Steps for Agents:
  1. SSO Redirect: Send users to Hub for authentication
  2. Token Receipt: Receive token from SSO callback
  3. Validation: Call Hub's validation API (no secret needed)
  4. User Data: Use validated user data in your app
  5. Premium Check: Check premium_tier field for features