Files
vip-coordinator/backend-old-20260125/dist/services/authService.js
kyle 868f7efc23
Some checks failed
CI/CD Pipeline / Backend Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Tests (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
Major Enhancement: NestJS Migration + CASL Authorization + Error Handling
Complete rewrite from Express to NestJS with enterprise-grade features:

## Backend Improvements
- Migrated from Express to NestJS 11.0.1 with TypeScript
- Implemented Prisma ORM 7.3.0 for type-safe database access
- Added CASL authorization system replacing role-based guards
- Created global exception filters with structured logging
- Implemented Auth0 JWT authentication with Passport.js
- Added vehicle management with conflict detection
- Enhanced event scheduling with driver/vehicle assignment
- Comprehensive error handling and logging

## Frontend Improvements
- Upgraded to React 19.2.0 with Vite 7.2.4
- Implemented CASL-based permission system
- Added AbilityContext for declarative permissions
- Created ErrorHandler utility for consistent error messages
- Enhanced API client with request/response logging
- Added War Room (Command Center) dashboard
- Created VIP Schedule view with complete itineraries
- Implemented Vehicle Management UI
- Added mock data generators for testing (288 events across 20 VIPs)

## New Features
- Vehicle fleet management (types, capacity, status tracking)
- Complete 3-day Jamboree schedule generation
- Individual VIP schedule pages with PDF export (planned)
- Real-time War Room dashboard with auto-refresh
- Permission-based navigation filtering
- First user auto-approval as administrator

## Documentation
- Created CASL_AUTHORIZATION.md (comprehensive guide)
- Created ERROR_HANDLING.md (error handling patterns)
- Updated CLAUDE.md with new architecture
- Added migration guides and best practices

## Technical Debt Resolved
- Removed custom authentication in favor of Auth0
- Replaced role checks with CASL abilities
- Standardized error responses across API
- Implemented proper TypeScript typing
- Added comprehensive logging

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-31 08:50:25 +01:00

168 lines
6.6 KiB
JavaScript

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const jwt = require('jsonwebtoken');
const google_auth_library_1 = require("google-auth-library");
const unifiedDataService_1 = __importDefault(require("./unifiedDataService"));
// Simplified authentication service - removes excessive logging and complexity
class AuthService {
constructor() {
this.jwtExpiry = '24h';
// Middleware to check authentication
this.requireAuth = async (req, res, next) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'Authentication required' });
}
const decoded = this.verifyToken(token);
if (!decoded) {
return res.status(401).json({ error: 'Invalid or expired token' });
}
// Get fresh user data
const user = await unifiedDataService_1.default.getUserById(decoded.id);
if (!user) {
return res.status(401).json({ error: 'User not found' });
}
req.user = user;
next();
};
// Middleware to check role
this.requireRole = (roles) => {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
};
// Auto-generate a secure JWT secret if not provided
if (process.env.JWT_SECRET) {
this.jwtSecret = process.env.JWT_SECRET;
console.log('Using JWT_SECRET from environment');
}
else {
// Generate a cryptographically secure random secret
const crypto = require('crypto');
this.jwtSecret = crypto.randomBytes(64).toString('hex');
console.log('Generated new JWT_SECRET (this will change on restart)');
console.log('To persist sessions across restarts, set JWT_SECRET in .env');
}
// Initialize Google OAuth client
this.googleClient = new google_auth_library_1.OAuth2Client(process.env.GOOGLE_CLIENT_ID);
}
// Generate JWT token
generateToken(user) {
const payload = { id: user.id, email: user.email, role: user.role };
return jwt.sign(payload, this.jwtSecret, { expiresIn: this.jwtExpiry });
}
// Verify Google ID token from frontend
async verifyGoogleToken(credential) {
try {
// Verify the token with Google
const ticket = await this.googleClient.verifyIdToken({
idToken: credential,
audience: process.env.GOOGLE_CLIENT_ID,
});
const payload = ticket.getPayload();
if (!payload || !payload.email) {
throw new Error('Invalid token payload');
}
// Find or create user
let user = await unifiedDataService_1.default.getUserByEmail(payload.email);
if (!user) {
// Auto-create user with coordinator role
user = await unifiedDataService_1.default.createUser({
email: payload.email,
name: payload.name || payload.email,
role: 'coordinator',
googleId: payload.sub
});
}
// Generate our JWT
const token = this.generateToken(user);
return { user, token };
}
catch (error) {
console.error('Token verification error:', error);
throw new Error('Failed to verify Google token');
}
}
// Verify JWT token
verifyToken(token) {
try {
return jwt.verify(token, this.jwtSecret);
}
catch (error) {
return null;
}
}
// Google OAuth helpers
getGoogleAuthUrl() {
if (!process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_REDIRECT_URI) {
throw new Error('Google OAuth not configured. Please set GOOGLE_CLIENT_ID and GOOGLE_REDIRECT_URI in .env file');
}
const params = new URLSearchParams({
client_id: process.env.GOOGLE_CLIENT_ID,
redirect_uri: process.env.GOOGLE_REDIRECT_URI,
response_type: 'code',
scope: 'email profile',
access_type: 'offline',
prompt: 'consent'
});
return `https://accounts.google.com/o/oauth2/v2/auth?${params}`;
}
async exchangeGoogleCode(code) {
const response = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
code,
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
redirect_uri: process.env.GOOGLE_REDIRECT_URI,
grant_type: 'authorization_code'
})
});
if (!response.ok) {
throw new Error('Failed to exchange authorization code');
}
return response.json();
}
async getGoogleUserInfo(accessToken) {
const response = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
headers: { Authorization: `Bearer ${accessToken}` }
});
if (!response.ok) {
throw new Error('Failed to get user info');
}
return response.json();
}
// Simplified login/signup
async handleGoogleAuth(code) {
// Exchange code for tokens
const tokens = await this.exchangeGoogleCode(code);
// Get user info
const googleUser = await this.getGoogleUserInfo(tokens.access_token);
// Find or create user
let user = await unifiedDataService_1.default.getUserByEmail(googleUser.email);
if (!user) {
// Auto-create user with coordinator role
user = await unifiedDataService_1.default.createUser({
email: googleUser.email,
name: googleUser.name,
role: 'coordinator',
googleId: googleUser.id
});
}
// Generate JWT
const token = this.generateToken(user);
return { user, token };
}
}
exports.default = new AuthService();
//# sourceMappingURL=authService.js.map