diff --git a/.env.template b/.env.template
index 3c05bdc..c9db31d 100644
--- a/.env.template
+++ b/.env.template
@@ -14,4 +14,6 @@ SENDGRID_SENDER_ID=""
NEXTAUTH_URL="http://localhost:3000"
NEXTAUTH_SECRET="change-me"
-NEXTAUTH_ROOT_DOMAIN="http://localhost"
\ No newline at end of file
+NEXTAUTH_ROOT_DOMAIN="http://localhost"
+
+SECRET_LINK="http://localhost:3000/verify-email/"
diff --git a/package-lock.json b/package-lock.json
index b75a06c..b2bcdb2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,8 +10,10 @@
"dependencies": {
"@prisma/client": "^5.9.1",
"@sendgrid/mail": "^8.1.0",
+ "@types/jsonwebtoken": "^9.0.6",
"bcrypt": "^5.1.1",
- "eclipse-components": "^0.0.141",
+ "eclipse-components": "^0.0.149",
+ "jsonwebtoken": "^9.0.2",
"next": "14.1.0",
"next-auth": "^4.24.5",
"prisma": "^5.9.1",
@@ -586,11 +588,18 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
+ "node_modules/@types/jsonwebtoken": {
+ "version": "9.0.6",
+ "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz",
+ "integrity": "sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/node": {
"version": "20.11.16",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz",
"integrity": "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==",
- "dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
@@ -1223,6 +1232,11 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
+ },
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@@ -1588,10 +1602,18 @@
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"dev": true
},
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/eclipse-components": {
- "version": "0.0.141",
- "resolved": "https://registry.npmjs.org/eclipse-components/-/eclipse-components-0.0.141.tgz",
- "integrity": "sha512-1uBiEY/gYK8SBp3lY6bv3Ab+cuo5qe0YvvMkOwsbnqCVuTQu19UoFrhiXHwPbBgQEGrGQxLR2wrHmy418SR4qw==",
+ "version": "0.0.149",
+ "resolved": "https://registry.npmjs.org/eclipse-components/-/eclipse-components-0.0.149.tgz",
+ "integrity": "sha512-0d3gxDuoKl3X99ETLJ9JFgpe8jy52MInqGounJoF3hPRcwwpRrlwMLohtDl7msztnkjjfvuFUqRJ+ib27kY2Ew==",
"peerDependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
@@ -3291,6 +3313,27 @@
"json5": "lib/cli.js"
}
},
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
+ "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
"node_modules/jsx-ast-utils": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
@@ -3306,6 +3349,25 @@
"node": ">=4.0"
}
},
+ "node_modules/jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "dependencies": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "dependencies": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -3376,12 +3438,47 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
+ },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -5239,8 +5336,7 @@
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
- "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
- "dev": true
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"node_modules/update-browserslist-db": {
"version": "1.0.13",
diff --git a/package.json b/package.json
index 3adace8..002ace3 100644
--- a/package.json
+++ b/package.json
@@ -11,8 +11,10 @@
"dependencies": {
"@prisma/client": "^5.9.1",
"@sendgrid/mail": "^8.1.0",
+ "@types/jsonwebtoken": "^9.0.6",
"bcrypt": "^5.1.1",
- "eclipse-components": "^0.0.141",
+ "eclipse-components": "^0.0.149",
+ "jsonwebtoken": "^9.0.2",
"next": "14.1.0",
"next-auth": "^4.24.5",
"prisma": "^5.9.1",
@@ -31,4 +33,4 @@
"tailwindcss": "^3.3.0",
"typescript": "^5"
}
-}
\ No newline at end of file
+}
diff --git a/prisma/migrations/20240312222610_add_email_verification_auth_table/migration.sql b/prisma/migrations/20240312222610_add_email_verification_auth_table/migration.sql
new file mode 100644
index 0000000..aea1564
--- /dev/null
+++ b/prisma/migrations/20240312222610_add_email_verification_auth_table/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "Auth" ADD COLUMN "emailVerified" BOOLEAN NOT NULL DEFAULT false;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 8e8415e..736bf9d 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -31,6 +31,8 @@ model Auth {
createdAt DateTime @default(now()) @db.Timestamp(0)
updatedAt DateTime @default(now()) @updatedAt @db.Timestamp(0)
+
+ emailVerified Boolean @default(false)
}
model MailingList {
diff --git a/src/app/api/auth/signup/route.ts b/src/app/api/auth/signup/route.ts
index edb7b3b..c24bd74 100644
--- a/src/app/api/auth/signup/route.ts
+++ b/src/app/api/auth/signup/route.ts
@@ -1,6 +1,8 @@
import { prisma } from "@/lib/prisma";
import bcrypt from "bcrypt";
import { NextResponse } from "next/server";
+import {tokenGeneration} from "@/app/api/auth/signup/verifyEmail";
+import internal from "stream";
/**
*
@@ -60,6 +62,7 @@ export async function POST(request: Request) {
console.log(email, " Account Created");
+ await tokenGeneration(newUser.id, email);
return NextResponse.json({ user: newUser }, { status: 201 });
}
@@ -80,5 +83,6 @@ export async function POST(request: Request) {
console.log(email, " Account Created");
+ await tokenGeneration(newUser.id, email);
return NextResponse.json({ user: newUser }, { status: 201 });
}
diff --git a/src/app/api/auth/signup/verifyEmail.ts b/src/app/api/auth/signup/verifyEmail.ts
new file mode 100644
index 0000000..f644461
--- /dev/null
+++ b/src/app/api/auth/signup/verifyEmail.ts
@@ -0,0 +1,15 @@
+import jwt from "jsonwebtoken";
+import { sendEmail } from "@/lib/email";
+import { send } from "process";
+
+export async function tokenGeneration(userId:string, email:string) {
+ const secret = process.env.NEXTAUTH_SECRET as string;
+
+ const token = jwt.sign({userId, email}, secret, {expiresIn: "7d"});
+ const link = process.env.SECRET_LINK + token;
+
+ const subject = "Verify your email";
+ const body = `Click this link to verify your email: ${link}`;
+
+ await sendEmail(email, subject, body);
+}
diff --git a/src/app/login/LoginForm.tsx b/src/app/login/LoginForm.tsx
index 6392cea..5fea9f0 100644
--- a/src/app/login/LoginForm.tsx
+++ b/src/app/login/LoginForm.tsx
@@ -3,7 +3,7 @@
import {
TextField,
Button,
- EclipseLogoLong,
+ EclipseLogoTextOrbGlow,
Notification,
} from "eclipse-components";
import { signIn } from "next-auth/react";
@@ -22,7 +22,7 @@ export default function LoginForm() {
return (
<>
-