Backup: 2025-06-07 19:48 - Script test
[Restore from backup: vip-coordinator-backup-2025-06-07-19-48-script-test]
This commit is contained in:
197
backend/src/services/authService.ts
Normal file
197
backend/src/services/authService.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { OAuth2Client } from 'google-auth-library';
|
||||
import dataService from './unifiedDataService';
|
||||
|
||||
// Simplified authentication service - removes excessive logging and complexity
|
||||
class AuthService {
|
||||
private jwtSecret: string;
|
||||
private jwtExpiry: string = '24h';
|
||||
private googleClient: OAuth2Client;
|
||||
|
||||
constructor() {
|
||||
// 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 OAuth2Client(process.env.GOOGLE_CLIENT_ID);
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
generateToken(user: any): string {
|
||||
const payload = { id: user.id, email: user.email, role: user.role };
|
||||
return jwt.sign(payload, this.jwtSecret, { expiresIn: this.jwtExpiry }) as string;
|
||||
}
|
||||
|
||||
// Verify Google ID token from frontend
|
||||
async verifyGoogleToken(credential: string): Promise<{ user: any; token: string }> {
|
||||
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 dataService.getUserByEmail(payload.email);
|
||||
|
||||
if (!user) {
|
||||
// Auto-create user with coordinator role
|
||||
user = await dataService.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: string): any {
|
||||
try {
|
||||
return jwt.verify(token, this.jwtSecret);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware to check authentication
|
||||
requireAuth = async (req: Request & { user?: any }, res: Response, next: NextFunction) => {
|
||||
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 dataService.getUserById(decoded.id);
|
||||
if (!user) {
|
||||
return res.status(401).json({ error: 'User not found' });
|
||||
}
|
||||
|
||||
req.user = user;
|
||||
next();
|
||||
};
|
||||
|
||||
// Middleware to check role
|
||||
requireRole = (roles: string[]) => {
|
||||
return (req: Request & { user?: any }, res: Response, next: NextFunction) => {
|
||||
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();
|
||||
};
|
||||
};
|
||||
|
||||
// Google OAuth helpers
|
||||
getGoogleAuthUrl(): string {
|
||||
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: string): Promise<any> {
|
||||
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: string): Promise<any> {
|
||||
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: string): Promise<{ user: any; token: string }> {
|
||||
// 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 dataService.getUserByEmail(googleUser.email);
|
||||
|
||||
if (!user) {
|
||||
// Auto-create user with coordinator role
|
||||
user = await dataService.createUser({
|
||||
email: googleUser.email,
|
||||
name: googleUser.name,
|
||||
role: 'coordinator',
|
||||
googleId: googleUser.id
|
||||
});
|
||||
}
|
||||
|
||||
// Generate JWT
|
||||
const token = this.generateToken(user);
|
||||
|
||||
return { user, token };
|
||||
}
|
||||
}
|
||||
|
||||
export default new AuthService();
|
||||
Reference in New Issue
Block a user