118 lines
3.6 KiB
JavaScript
118 lines
3.6 KiB
JavaScript
const config = require('../config');
|
|
const apiClient = require('../api-client');
|
|
|
|
async function authRoutes(fastify, options) {
|
|
|
|
// Root route - redirect based on signed session cookie validity
|
|
fastify.get('/', async (req, reply) => {
|
|
try {
|
|
const raw = req.cookies && req.cookies.kitchen_session;
|
|
if (raw) {
|
|
const { valid, value } = req.unsignCookie(raw || '');
|
|
if (valid) {
|
|
const token = config.get('authToken');
|
|
const expiry = config.get('tokenExpiry');
|
|
if (token && !apiClient.isTokenExpired(expiry) && value === token) {
|
|
return reply.redirect('/dashboard');
|
|
}
|
|
}
|
|
}
|
|
} catch (_) {}
|
|
return reply.redirect('/login');
|
|
});
|
|
|
|
// Login page
|
|
fastify.get('/login', async (req, reply) => {
|
|
try {
|
|
const raw = req.cookies && req.cookies.kitchen_session;
|
|
if (raw) {
|
|
const { valid, value } = req.unsignCookie(raw || '');
|
|
if (valid) {
|
|
const token = config.get('authToken');
|
|
const expiry = config.get('tokenExpiry');
|
|
if (token && !apiClient.isTokenExpired(expiry) && value === token) {
|
|
return reply.redirect('/dashboard');
|
|
}
|
|
}
|
|
}
|
|
} catch (_) {}
|
|
return reply.view('login', {
|
|
error: req.query.error || null,
|
|
recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY || ''
|
|
});
|
|
});
|
|
|
|
// Login handler
|
|
fastify.post('/auth/login', async (req, reply) => {
|
|
const { login, password, 'g-recaptcha-response': recaptchaToken } = req.body;
|
|
|
|
// Validate inputs
|
|
if (!login || !password) {
|
|
return reply.view('login', {
|
|
error: 'Please provide email and password',
|
|
recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY || ''
|
|
});
|
|
}
|
|
|
|
if (!recaptchaToken) {
|
|
return reply.view('login', {
|
|
error: 'reCAPTCHA verification required',
|
|
recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY || ''
|
|
});
|
|
}
|
|
|
|
try {
|
|
// Call API to authenticate
|
|
const result = await apiClient.login(login, password, recaptchaToken);
|
|
|
|
if (result.error) {
|
|
return reply.view('login', {
|
|
error: result.message || 'Login failed',
|
|
recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY || ''
|
|
});
|
|
}
|
|
|
|
// Store auth token and expiration
|
|
config.set('authToken', result.token);
|
|
config.set('tokenExpiry', result.expirationDate);
|
|
config.set('userEmail', login);
|
|
|
|
// Set signed cookie for session; secure only on HTTPS
|
|
const isHttps = (req.protocol === 'https') || ((req.headers['x-forwarded-proto'] || '').toString().toLowerCase() === 'https');
|
|
reply.setCookie('kitchen_session', result.token, {
|
|
signed: true,
|
|
httpOnly: true,
|
|
secure: isHttps,
|
|
sameSite: 'strict',
|
|
maxAge: 30 * 24 * 60 * 60,
|
|
path: '/'
|
|
});
|
|
|
|
return reply.redirect('/dashboard');
|
|
|
|
} catch (error) {
|
|
console.error('Login error:', error.message);
|
|
return reply.view('login', {
|
|
error: 'An error occurred during login. Please try again.',
|
|
recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY || ''
|
|
});
|
|
}
|
|
});
|
|
|
|
// Logout handler
|
|
fastify.post('/auth/logout', async (req, reply) => {
|
|
// Allow multiple sessions: only clear the browser cookie
|
|
reply.clearCookie('kitchen_session');
|
|
return reply.redirect('/login');
|
|
});
|
|
|
|
fastify.get('/auth/logout', async (req, reply) => {
|
|
// Allow multiple sessions: only clear the browser cookie
|
|
reply.clearCookie('kitchen_session');
|
|
return reply.redirect('/login');
|
|
});
|
|
}
|
|
|
|
module.exports = authRoutes;
|
|
|