import { Router } from 'express'; import { authService } from '../services/auth.js'; import { authenticate } from '../middleware/auth.js'; import { validateBody, signupSchema, loginSchema, nostrLoginSchema } from '../utils/validation.js'; import { prisma } from '../config/database.js'; import { randomBytes } from 'crypto'; const router = Router(); // Signup router.post('/signup', validateBody(signupSchema), async (req, res, next) => { try { const result = await authService.signup(req.body); // Set refresh token as httpOnly cookie res.cookie('refresh_token', result.refreshToken, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days }); res.status(201).json({ user: result.user, accessToken: result.accessToken, expiresIn: result.expiresIn, }); } catch (error) { next(error); } }); // Login router.post('/login', validateBody(loginSchema), async (req, res, next) => { try { const result = await authService.login(req.body); res.cookie('refresh_token', result.refreshToken, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', maxAge: 7 * 24 * 60 * 60 * 1000, }); res.json({ user: result.user, accessToken: result.accessToken, expiresIn: result.expiresIn, }); } catch (error) { next(error); } }); // Refresh token router.post('/refresh', async (req, res, next) => { try { const refreshToken = req.cookies?.refresh_token || req.body.refreshToken; if (!refreshToken) { return res.status(401).json({ error: 'Refresh token required' }); } const result = await authService.refreshTokens(refreshToken); res.json({ user: result.user, accessToken: result.accessToken, expiresIn: result.expiresIn, }); } catch (error) { next(error); } }); // Logout router.post('/logout', async (req, res, next) => { try { const refreshToken = req.cookies?.refresh_token; await authService.logout(refreshToken); res.clearCookie('refresh_token'); res.json({ success: true }); } catch (error) { next(error); } }); // Get current user router.get('/me', authenticate, async (req, res) => { res.json({ user: req.user }); }); // Update profile router.patch('/me', authenticate, async (req, res, next) => { try { const { displayName, lightningAddress, avatarUrl } = req.body; const updated = await prisma.user.update({ where: { id: req.user.id }, data: { displayName, lightningAddress, avatarUrl, }, select: { id: true, email: true, displayName: true, avatarUrl: true, role: true, lightningAddress: true, createdAt: true, }, }); res.json({ user: updated }); } catch (error) { next(error); } }); // Nostr challenge router.post('/nostr/challenge', async (req, res) => { const challenge = randomBytes(32).toString('hex'); // Store challenge temporarily (in production, use Redis with TTL) // For now, we'll include it in the response and verify the signature res.json({ challenge, message: `Sign this message to login to LNPaywall: ${challenge}`, }); }); // Nostr verify router.post('/nostr/verify', validateBody(nostrLoginSchema), async (req, res, next) => { try { const result = await authService.nostrLogin(req.body); res.cookie('refresh_token', result.refreshToken, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', maxAge: 7 * 24 * 60 * 60 * 1000, }); res.json({ user: result.user, accessToken: result.accessToken, expiresIn: result.expiresIn, }); } catch (error) { next(error); } }); // OAuth start (redirect to provider) router.get('/oauth/:provider/start', (req, res) => { const { provider } = req.params; const { redirect } = req.query; // Store redirect URL in session/cookie for after OAuth callback if (redirect) { res.cookie('oauth_redirect', redirect, { httpOnly: true, maxAge: 10 * 60 * 1000, // 10 minutes }); } // In production, implement full OAuth flow // For now, return instructions res.json({ message: `OAuth ${provider} not fully implemented. Use email/password or Nostr login.`, supported: ['google', 'github'], }); }); // OAuth callback router.get('/oauth/:provider/callback', async (req, res, next) => { try { const { provider } = req.params; const { code } = req.query; // In production, exchange code for tokens and get user info res.json({ message: `OAuth ${provider} callback received`, code: code ? 'received' : 'missing', }); } catch (error) { next(error); } }); export default router;