This commit implements CSRF token support for all session-based API requests to fix the "CSRF token missing" and "CSRF token mismatch" errors introduced after CSRF protection was added in commit 62c4cc84. Changes: - Created csrfService.ts utility for fetching and caching CSRF tokens - Added getPostHeadersWithCsrf() helper to authUtils for async token injection - Updated all service files (*Service.ts) to include CSRF tokens in POST/PUT/PATCH/DELETE requests - Updated components with inline fetch calls to use getCsrfToken() - Fixed CSRF middleware to use single lusca instance instead of creating new instances per request - Improved generateToken() to use req.csrfToken() when available - Added CalDAV path exemption to CSRF protection Technical details: - CSRF tokens are fetched from /api/csrf-token endpoint - Tokens are cached and reused across requests to avoid unnecessary fetches - Tokens are included in x-csrf-token header for state-changing requests - Public endpoints (login, register) remain exempt from CSRF protection - Bearer token authentication remains exempt from CSRF protection Files modified: - Backend: app.js, middleware/csrf.js - Frontend: 13 service files, 8 component files - New file: frontend/utils/csrfService.ts This ensures all session-based requests properly include CSRF tokens while maintaining support for API token authentication.
109 lines
3.4 KiB
JavaScript
109 lines
3.4 KiB
JavaScript
'use strict';
|
|
|
|
const authService = require('./service');
|
|
const { logError } = require('../../services/logService');
|
|
const { generateToken } = require('../../middleware/csrf');
|
|
|
|
const authController = {
|
|
getVersion(req, res) {
|
|
res.json(authService.getVersion());
|
|
},
|
|
|
|
async getRegistrationStatus(req, res, next) {
|
|
try {
|
|
const result = await authService.getRegistrationStatus();
|
|
res.json(result);
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
},
|
|
|
|
async register(req, res, next) {
|
|
try {
|
|
const { email, password } = req.body;
|
|
const result = await authService.register(email, password);
|
|
res.status(201).json(result);
|
|
} catch (error) {
|
|
// Handle specific error messages for compatibility
|
|
if (error.statusCode === 404) {
|
|
return res.status(404).json({ error: error.message });
|
|
}
|
|
if (error.statusCode === 400) {
|
|
return res.status(400).json({ error: error.message });
|
|
}
|
|
if (
|
|
error.message ===
|
|
'Failed to send verification email. Please try again later.'
|
|
) {
|
|
return res.status(500).json({ error: error.message });
|
|
}
|
|
logError('Registration error:', error);
|
|
res.status(500).json({
|
|
error: 'Registration failed. Please try again.',
|
|
});
|
|
}
|
|
},
|
|
|
|
async verifyEmail(req, res, next) {
|
|
try {
|
|
const { token } = req.query;
|
|
const result = await authService.verifyEmail(token);
|
|
res.redirect(result.redirect);
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
},
|
|
|
|
async getCurrentUser(req, res, next) {
|
|
try {
|
|
const result = await authService.getCurrentUser(req.session);
|
|
res.json(result);
|
|
} catch (error) {
|
|
logError('Error fetching current user:', error);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
},
|
|
|
|
async login(req, res, next) {
|
|
try {
|
|
const { email, password } = req.body;
|
|
const result = await authService.login(
|
|
email,
|
|
password,
|
|
req.session
|
|
);
|
|
res.json(result);
|
|
} catch (error) {
|
|
if (error.statusCode === 400) {
|
|
return res.status(400).json({ error: error.message });
|
|
}
|
|
if (error.statusCode === 401) {
|
|
return res.status(401).json({ errors: [error.message] });
|
|
}
|
|
if (error.statusCode === 403) {
|
|
return res.status(403).json({
|
|
error: error.message,
|
|
email_not_verified: error.email_not_verified || false,
|
|
});
|
|
}
|
|
logError('Login error:', error);
|
|
res.status(500).json({ error: 'Internal server error' });
|
|
}
|
|
},
|
|
|
|
async logout(req, res, next) {
|
|
try {
|
|
const result = await authService.logout(req.session);
|
|
res.json(result);
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
},
|
|
|
|
getCsrfToken(req, res) {
|
|
const token = generateToken(req, res);
|
|
res.json({ csrfToken: token });
|
|
},
|
|
};
|
|
|
|
module.exports = authController;
|