196 lines
4.8 KiB
JavaScript
196 lines
4.8 KiB
JavaScript
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;
|
|
|