From a9deca22f044067baafa0abcb2a9b4f708db44b5 Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Fri, 24 May 2024 19:01:24 +0300 Subject: [PATCH] verivy email before log in --- pages/api/auth/[...nextauth].ts | 42 +++++++++--- pages/api/email.ts | 68 ++++++++++++++++++- .../migrations/20240524150310_/migration.sql | 2 + prisma/schema.prisma | 1 + src/helpers/email.js | 11 +++ src/templates/emails/emailValidate.hbs | 16 +++++ 6 files changed, 128 insertions(+), 12 deletions(-) create mode 100644 prisma/migrations/20240524150310_/migration.sql create mode 100644 src/templates/emails/emailValidate.hbs diff --git a/pages/api/auth/[...nextauth].ts b/pages/api/auth/[...nextauth].ts index 6020c99..44dbc53 100644 --- a/pages/api/auth/[...nextauth].ts +++ b/pages/api/auth/[...nextauth].ts @@ -10,6 +10,7 @@ import CredentialsProvider from "next-auth/providers/credentials" import { PrismaAdapter } from "@auth/prisma-adapter" import bcrypt from "bcrypt" +const emailHelper = require('../../../src/helpers/email'); //microsoft import AzureADProvider from "next-auth/providers/azure-ad"; @@ -87,32 +88,51 @@ export const authOptions: NextAuthOptions = { const prisma = common.getPrismaClient(); const user = await prisma.user.findUnique({ where: { email: credentials.username } }); if (user) { - const match = await bcrypt.compare(credentials?.password, user.passwordHashLocalAccount); - if (match) { - console.log("User authenticated successfully."); - //create access token - user.accessToken = await getAccessToken(); - - return user; + if (!user.emailVerified) { + const mailVerifyToken = await bcrypt.hash(credentials.username, 10); + const date = new Date().getTime(); + const emailVerifyToken = date + "_" + mailVerifyToken; + await prisma.user.update({ + where: { email: credentials.username }, + data: { emailVerifyToken: emailVerifyToken } + }); + emailHelper.SendEmail_ValidateTemplate(credentials.username, emailVerifyToken); + throw new Error('Моля потвърди имейла си преди да влезеш в системата.'); } else { - console.log("Password mismatch."); - throw new Error('невалидна парола'); + const match = await bcrypt.compare(credentials?.password, user.passwordHashLocalAccount); + if (match) { + console.log("User authenticated successfully."); + //create access token + user.accessToken = await getAccessToken(); + + return user; + } + else { + console.log("Password mismatch."); + throw new Error('невалидна парола'); + } } } else { const pub = await prisma.publisher.findUnique({ where: { email: credentials.username } }); if (pub) { const passHash = await bcrypt.hash(credentials.password, 10); + const mailVerifyToken = await bcrypt.hash(pub.email, 10); + const date = new Date().getTime(); + const emailVerifyToken = date + "_" + mailVerifyToken; const newUser = await prisma.user.create({ data: { name: credentials.username, email: credentials.username, - passwordHashLocalAccount: passHash + passwordHashLocalAccount: passHash, + emailVerifyToken: emailVerifyToken } }); console.log("New local credential user created for publisher ", pub.firstName, " ", pub.lastName, " (", pub.email, ")"); - return newUser; + emailHelper.SendEmail_ValidateTemplate(pub.email, emailVerifyToken, pub.firstName, pub.lastName); + //return newUser; + throw new Error("Моля проверете вашия имейл '" + credentials?.username + "' за да потвърдите регистрацията си."); } else { diff --git a/pages/api/email.ts b/pages/api/email.ts index 6817488..bc36e11 100644 --- a/pages/api/email.ts +++ b/pages/api/email.ts @@ -13,11 +13,20 @@ const logger = require('../../src/logger'); import fs from 'fs'; import path from 'path'; +import { log } from "console"; const handlebars = require("handlebars"); const router = createRouter(); +// programatically sign in +import { getSession } from "next-auth/react"; +import { signIn } from "next-auth/react"; + +import { authOptions } from './auth/[...nextauth]'; +// import NextAuth from 'next-auth'; +// import { setCookie } from 'nookies'; + //action to accept coverme request from email @@ -33,10 +42,54 @@ export default async function handler(req, res) { const emailaction = req.query.emailaction; // Retrieve and validate the JWT token + let email = req.body.email || req.query.email; //response is a special action that does not require a token //PUBLIC if (action == "email_response" || action == "account") { switch (emailaction) { + case "validateEmail": + let token = req.query.token; + let user = await prisma.user.findUnique({ + where: { + email: email + } + }); + let pub = await prisma.publisher.findUnique({ + where: { + email: email + } + }); + if (!user && !pub) { + return res.status(400).json({ message: "Invalid user" }); + } + if (user) { + await prisma.user.update({ + where: { + email: email + }, + data: { + emailVerified: new Date() + } + }); + } + logger.info("User: " + email + " validated his email."); + console.log("User: " + email + " validated his email. Logging in..."); + return res.redirect("/dash"); + // log in the user using nextauth and redirect to the dashboard + //how to login the user with nextauth? maybe use the signIn callback + // const result = await signIn("credentials", { + // // redirect: false, + // email, + // account: user + // }); + + // if (result.error) { + // return res.status(401).json({ message: "Invalid credentials" }); + // } + + // return res.status(200).json({ message: "Signed in successfully" }); + + case "coverMeAccept": //validate shiftId and assignmentId let shiftId = req.query.shiftId; @@ -208,7 +261,6 @@ export default async function handler(req, res) { // Send password reset form to the user //parse the request body - let email = req.body.email || req.query.email; let actualUser = await prisma.publisher.findUnique({ where: { email: email @@ -285,6 +337,9 @@ export default async function handler(req, res) { // const emailResponse = await common.sendEmail(user.email, "Email Action Processed", // "Your email action was processed successfully"); } + // ######################## + // PRIVATE API + // ######################## else { const token = await getToken({ req: req }); @@ -301,6 +356,17 @@ export default async function handler(req, res) { //PRIVATE ACTIONS switch (action) { + //in nextauth.ts + // case "validateEmail": + // let publisher = await prisma.publisher.findUnique({ + // where: { + // email: token.email + // } + // }); + // if (!publisher) { + // return res.status(400).json({ message: "Invalid user" }); + // } + case "sendCoverMeRequestByEmail": // Send CoverMe request to the users //get from POST data: shiftId, assignmentId, date diff --git a/prisma/migrations/20240524150310_/migration.sql b/prisma/migrations/20240524150310_/migration.sql new file mode 100644 index 0000000..c7c2002 --- /dev/null +++ b/prisma/migrations/20240524150310_/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE `user` ADD COLUMN `emailVerifyToken` VARCHAR(191) NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 1d7d231..c5cc4d8 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -302,6 +302,7 @@ model User { id String @id @default(cuid()) name String? email String? @unique + emailVerifyToken String? emailVerified DateTime? image String? accounts Account[] diff --git a/src/helpers/email.js b/src/helpers/email.js index 402f82d..3d8f658 100644 --- a/src/helpers/email.js +++ b/src/helpers/email.js @@ -114,6 +114,17 @@ exports.SendEmail = async function (to, subject, text, html, attachments = []) { return result; }; + +exports.SendEmail_ValidateTemplate = async function (to, token, firstName, lastName) { + let validateUrl = process.env.NEXTAUTH_URL + "/api/email?action=email_response&emailaction=validateEmail&token=" + token + "&email=" + to; + return await this.SendEmailHandlebars(to, "emailValidate", { + user: to, + validateUrl: validateUrl, + sentDate: common.getDateFormated(new Date()) + }); +}; + + exports.SendEmailHandlebars = async function (to, templateName, model, attachments = []) { try { // Ensure the sender and mailtrapTestClient are correctly defined or imported diff --git a/src/templates/emails/emailValidate.hbs b/src/templates/emails/emailValidate.hbs new file mode 100644 index 0000000..147750e --- /dev/null +++ b/src/templates/emails/emailValidate.hbs @@ -0,0 +1,16 @@ +{{!--Subject: ССОМ: Потвърдете имейла си--}} + +
+

Здравей {{user}},

+

Получихме заявка за вход в сайта за Специално свидетелстване на обществени места в София.

+

За да потвърдиш твоя достъп моля използвай бутона по долу или кликни тук.

+

+ + Потвърждавам +

+
+ \ No newline at end of file