79 lines
2 KiB
JavaScript
79 lines
2 KiB
JavaScript
// server/api/auth/login.post.js
|
|
import jwt from "jsonwebtoken";
|
|
import { randomUUID } from "crypto";
|
|
import { Resend } from "resend";
|
|
import Member from "../../models/member.js";
|
|
import { connectDB } from "../../utils/mongoose.js";
|
|
import { validateBody } from "../../utils/validateBody.js";
|
|
import { emailSchema } from "../../utils/schemas.js";
|
|
|
|
const resend = new Resend(process.env.RESEND_API_KEY);
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
await connectDB();
|
|
|
|
const baseUrl = process.env.BASE_URL;
|
|
if (!baseUrl) {
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: "BASE_URL environment variable is not set",
|
|
});
|
|
}
|
|
|
|
const { email } = await validateBody(event, emailSchema);
|
|
|
|
const GENERIC_MESSAGE = "If this email is registered, we've sent a login link.";
|
|
|
|
const member = await Member.findOne({ email });
|
|
|
|
if (!member) {
|
|
return {
|
|
success: true,
|
|
message: GENERIC_MESSAGE,
|
|
};
|
|
}
|
|
|
|
const config = useRuntimeConfig(event);
|
|
const jti = randomUUID();
|
|
|
|
const token = jwt.sign(
|
|
{ memberId: member._id, jti },
|
|
config.jwtSecret,
|
|
{ expiresIn: "15m" },
|
|
);
|
|
|
|
// Store jti so we can burn it on first use
|
|
await Member.findByIdAndUpdate(
|
|
member._id,
|
|
{ $set: { magicLinkJti: jti, magicLinkJtiUsed: false } },
|
|
{ runValidators: false }
|
|
);
|
|
|
|
// Token goes in the fragment — never sent to server, never logged
|
|
const magicLink = `${baseUrl}/verify#${token}`;
|
|
|
|
try {
|
|
await resend.emails.send({
|
|
from: "Ghost Guild <ghostguild@babyghosts.org>",
|
|
to: email,
|
|
subject: "Your Ghost Guild login link",
|
|
text: `Hi,
|
|
|
|
Sign in to Ghost Guild:
|
|
${magicLink}
|
|
|
|
This link expires in 15 minutes. If you didn't request it, ignore this email.`,
|
|
});
|
|
|
|
return {
|
|
success: true,
|
|
message: GENERIC_MESSAGE,
|
|
};
|
|
} catch (error) {
|
|
console.error("Failed to send email:", error);
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: "Failed to send login email. Please try again.",
|
|
});
|
|
}
|
|
});
|