From 3d04c429ea2f241ed95a52791d3404eb2cccb0cf Mon Sep 17 00:00:00 2001
From: Adi-204
Date: Wed, 25 Dec 2024 00:41:57 +0530
Subject: [PATCH 1/6] Separate Authentication Module
---
app/api/authenticate/generate/route.js | 73 +++++++++++++++
app/api/authenticate/validate-jwt/route.js | 62 +++++++++++++
app/api/authenticate/verify/route.js | 91 +++++++++++++++++++
.../generate-otp/GenerateOtpForm.jsx | 84 +++++++++++++++++
app/authenticate/generate-otp/page.jsx | 18 ++++
app/authenticate/page.jsx | 4 +-
app/authenticate/verify-otp/VerifyOtpForm.jsx | 84 +++++++++++++++++
app/authenticate/verify-otp/page.jsx | 33 +++++++
app/utils/auth.js | 13 +++
app/utils/institute.js | 7 +-
package-lock.json | 27 ++++++
package.json | 3 +
12 files changed, 497 insertions(+), 2 deletions(-)
create mode 100644 app/api/authenticate/generate/route.js
create mode 100644 app/api/authenticate/validate-jwt/route.js
create mode 100644 app/api/authenticate/verify/route.js
create mode 100644 app/authenticate/generate-otp/GenerateOtpForm.jsx
create mode 100644 app/authenticate/generate-otp/page.jsx
create mode 100644 app/authenticate/verify-otp/VerifyOtpForm.jsx
create mode 100644 app/authenticate/verify-otp/page.jsx
diff --git a/app/api/authenticate/generate/route.js b/app/api/authenticate/generate/route.js
new file mode 100644
index 0000000..648a64e
--- /dev/null
+++ b/app/api/authenticate/generate/route.js
@@ -0,0 +1,73 @@
+import { NextResponse } from "next/server";
+import otpGenerator from "otp-generator";
+import { HmacSHA256 } from "crypto-js";
+import nodemailer from "nodemailer";
+import { cookies } from "next/headers";
+
+export async function POST(req) {
+ try {
+ req = await req.json();
+
+ // Form fields:
+ // i) email – (Institute Email) – Text
+
+ const { email, instituteCode } = req;
+
+ const otp = otpGenerator.generate(6, { lowerCaseAlphabets: false, upperCaseAlphabets: false, specialChars: false });
+
+ const expiryTime = Date.now() + 5 * 60 * 1000; // 5 mins
+
+ const data = `${instituteCode}.${email}.${otp}`;
+
+ const hashData = HmacSHA256(JSON.stringify(data), process.env.HASH_SECRET).toString() + "." + expiryTime;
+
+ const transporter = nodemailer.createTransport({
+ service: 'gmail',
+ auth: {
+ user: process.env.SYSTEM_MAIL,
+ pass: process.env.SYSTEM_MAIL_PASS
+ }
+ });
+
+ const mailOptions = {
+ from: process.env.SYSTEM_MAIL,
+ to: email,
+ subject: 'OTP Code for Authentication',
+ text: `Your OTP code is: ${otp}. This will expire at ${new Date(expiryTime).toLocaleString()}`
+ };
+
+ await transporter.sendMail(mailOptions);
+
+ const cookieStore = cookies();
+
+ cookieStore.set({
+ name: "otpData",
+ value: JSON.stringify({ email, hashData, instituteCode }),
+ httpOnly: true,
+ path: "/",
+ maxAge: 5 * 60, // 5 mins
+ });
+
+ return NextResponse.json(
+ {
+ message: "OTP send to mail successfully!",
+ },
+ {
+ status: 200,
+ }
+ );
+
+ } catch (error) {
+ console.log(error.message);
+ return NextResponse.json(
+ {
+ message:
+ error.message ||
+ "Something went wrong - please try again later!",
+ },
+ {
+ status: 500,
+ }
+ );
+ }
+}
\ No newline at end of file
diff --git a/app/api/authenticate/validate-jwt/route.js b/app/api/authenticate/validate-jwt/route.js
new file mode 100644
index 0000000..58047b8
--- /dev/null
+++ b/app/api/authenticate/validate-jwt/route.js
@@ -0,0 +1,62 @@
+import { NextResponse } from "next/server";
+import { cookies } from "next/headers";
+import jwt from "jsonwebtoken";
+
+export async function GET() {
+ try {
+
+ const cookieStore = cookies();
+
+ const authCookie = req.headers.get("Cookie");
+
+ if (!authCookie) {
+ throw new Error("No cookies found in the request.");
+ }
+
+ const [authCookiePair] = authCookie.split("; ");
+
+ if (!authCookiePair) {
+ throw new Error("Invalid cookie format.");
+ }
+
+ const [authCookieName, token] = authCookiePair.split("=");
+
+ if (!authCookieName || !token) {
+ throw new Error("Authentication cookie not found.");
+ }
+
+ const decoded = jwt.verify(token, process.env.JWT_SECRET);
+
+ const expiryTime = decoded.exp;
+ const currentTime = Date.now();
+
+ if (currentTime >= expiryTime * 1000) {
+ cookieStore.delete("authCookieName");
+ throw new Error("Authentication token has expired.");
+ }
+
+ return NextResponse.json(
+ {
+ message: "Authentication successful.",
+ email: decoded.email,
+ },
+ {
+ status: 200,
+ }
+ );
+
+ } catch (error) {
+ console.log(error.message);
+ return NextResponse.json(
+ {
+ message:
+ error.message ||
+ "Something went wrong - please try again later!",
+ },
+ {
+ status: 500,
+ }
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/app/api/authenticate/verify/route.js b/app/api/authenticate/verify/route.js
new file mode 100644
index 0000000..d37e5dc
--- /dev/null
+++ b/app/api/authenticate/verify/route.js
@@ -0,0 +1,91 @@
+
+import { NextResponse } from "next/server";
+import { HmacSHA256 } from "crypto-js";
+import validator from "validator";
+import { cookies } from "next/headers";
+import { instituteDetails } from "@/app/utils/institute";
+
+export async function POST(req) {
+ try {
+ req = await req.json();
+
+ // Form fields:
+ // i) email – (Institute Email) – Text
+ // ii) hashData – (Hashed Data for OTP verification) – Text
+ // iii) instituteCode - (Institute Code) – Text
+ // iv) otp - (Submitted OTP) - Text
+
+ const cookieStore = cookies();
+
+ const { email, hashData, instituteCode, otp } = req;
+
+ if (
+ validator.isEmpty(email) ||
+ validator.isEmpty(hashData) ||
+ validator.isEmpty(otp) ||
+ validator.isEmpty(instituteCode)
+ ) {
+ throw new Error("Please fill all the fields!");
+ }
+
+ if (otp.length > 0 && otp.length != 6) {
+ throw new Error("Please enter a valid 6-digit OTP!");
+ }
+
+ const hash = hashData.split('.');
+
+ if (hash.length !== 2) {
+ throw new Error("Invalid hash data format!");
+ }
+
+ const data = `${instituteCode}.${email}.${otp}`;
+
+ const userHash = HmacSHA256(data, process.env.HASH_SECRET).toString();
+
+ if (userHash !== hash[0]) {
+ throw new Error("Verification failed try again.");
+ }
+
+ const expiryTime = parseInt(hash[1]);
+
+ const currentTime = Date.now();
+
+ if (currentTime > expiryTime) {
+ cookieStore.delete("otpData");
+ throw new Error("OTP has expired. Please request a new one.");
+ }
+
+ const institute = await instituteDetails({ instituteCode });
+
+ cookieStore.set({
+ name: institute.authCookie,
+ value: JSON.stringify({ email }),
+ httpOnly: true,
+ path: "/",
+ maxAge: 5 * 60, // Temporary maxAge
+ });
+
+ cookieStore.delete("otpData");
+
+ return NextResponse.json(
+ {
+ message: "OTP is valid, Verification successful!",
+ },
+ {
+ status: 200,
+ }
+ );
+
+ } catch (error) {
+ return NextResponse.json(
+ {
+ message:
+ error.message ||
+ "Something went wrong - please try again later!",
+ },
+ {
+ status: 500,
+ }
+ );
+ }
+}
\ No newline at end of file
diff --git a/app/authenticate/generate-otp/GenerateOtpForm.jsx b/app/authenticate/generate-otp/GenerateOtpForm.jsx
new file mode 100644
index 0000000..043439e
--- /dev/null
+++ b/app/authenticate/generate-otp/GenerateOtpForm.jsx
@@ -0,0 +1,84 @@
+"use client";
+
+import { verifyUserMail } from "@/app/utils/auth";
+import Loading from "@/app/utils/Loading";
+import { useState } from "react";
+import { useRouter } from "next/navigation";
+
+const GenerateOtpForm = ({ instituteCode }) => {
+ const [loading, setLoading] = useState(false);
+ const [email, setEmail] = useState("");
+ const router = useRouter();
+
+ const handleChange = (e) => {
+ setEmail(e.target.value);
+ };
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+
+ const verifiedEmail = await verifyUserMail({ instituteCode, email });
+
+ const data = {
+ email: verifiedEmail,
+ instituteCode: instituteCode
+ }
+
+ setLoading(true);
+
+ const res = await fetch("/api/authenticate/generate", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(data),
+ });
+
+ setLoading(false);
+
+ if (res.ok) {
+ const json = await res.json();
+ router.push("/authenticate/verify-otp");
+ } else {
+ const json = await res.json();
+ alert(json.message);
+ }
+ };
+
+ return loading ? (
+
+ ) : (
+
+ );
+}
+
+export default GenerateOtpForm;
diff --git a/app/authenticate/generate-otp/page.jsx b/app/authenticate/generate-otp/page.jsx
new file mode 100644
index 0000000..63e29c5
--- /dev/null
+++ b/app/authenticate/generate-otp/page.jsx
@@ -0,0 +1,18 @@
+import GenerateOtpForm from "./GenerateOtpForm";
+
+export const metadata = {
+ title: "Request OTP for Email verification",
+};
+
+const Page = async ({ searchParams }) => {
+
+ let { instituteCode } = searchParams;
+
+ if (instituteCode) {
+ instituteCode = instituteCode.split("?")[0];
+ }
+
+ return ;
+};
+
+export default Page;
diff --git a/app/authenticate/page.jsx b/app/authenticate/page.jsx
index e0d6ded..3f6a1e3 100644
--- a/app/authenticate/page.jsx
+++ b/app/authenticate/page.jsx
@@ -11,8 +11,10 @@ const Page = async () => {
// Return only names of Institues
const institutes = await Institute.find({}, { _id: 0, name: 1, code: 1 });
+
+ const institutesData = JSON.parse(JSON.stringify(institutes));
- return ;
+ return ;
};
export default Page;
diff --git a/app/authenticate/verify-otp/VerifyOtpForm.jsx b/app/authenticate/verify-otp/VerifyOtpForm.jsx
new file mode 100644
index 0000000..9db21d7
--- /dev/null
+++ b/app/authenticate/verify-otp/VerifyOtpForm.jsx
@@ -0,0 +1,84 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { useRouter } from "next/navigation";
+import Loading from "@/app/utils/Loading";
+
+const VerifyOtpForm = ({ email, hashData, instituteCode }) => {
+
+ const [loading, setLoading] = useState(false);
+ const [otp, setOtp] = useState("");
+ const router = useRouter();
+
+ const handleChange = (e) => {
+ setOtp(e.target.value);
+ };
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+
+ setLoading(true);
+
+ const data = {
+ email: email,
+ hashData: hashData,
+ instituteCode: instituteCode,
+ otp: otp
+ }
+
+ const res = await fetch("/api/authenticate/verify", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(data),
+ });
+
+ setLoading(false);
+
+ if (res.ok) {
+ const json = await res.json();
+ router.push("/");
+ } else {
+ const json = await res.json();
+ alert(json.message);
+ }
+ };
+
+
+ return loading ? (
+
+ ) : (
+
+ );
+}
+
+export default VerifyOtpForm;
\ No newline at end of file
diff --git a/app/authenticate/verify-otp/page.jsx b/app/authenticate/verify-otp/page.jsx
new file mode 100644
index 0000000..a94dfd7
--- /dev/null
+++ b/app/authenticate/verify-otp/page.jsx
@@ -0,0 +1,33 @@
+import { redirect } from "next/navigation";
+import VerifyOtpForm from "./VerifyOtpForm";
+import { cookies } from "next/headers";
+
+export const metadata = {
+ title: "Enter OTP for Email verification",
+};
+
+const Page = async () => {
+
+ const cookieStore = cookies();
+ const cookie = cookieStore.get("otpData");
+
+ if (!cookie) {
+ redirect("/authenticate/generate-otp");
+ }
+
+ const token = cookie.value;
+
+ if (!token) {
+ redirect("/authenticate/generate-otp");
+ }
+
+ const { email, hashData, instituteCode } = JSON.parse(token);
+
+ if(!email || !hashData || !instituteCode){
+ redirect("/authenticate/generate-otp");
+ }
+
+ return ;
+};
+
+export default Page;
diff --git a/app/utils/auth.js b/app/utils/auth.js
index 158d915..8046c12 100644
--- a/app/utils/auth.js
+++ b/app/utils/auth.js
@@ -4,6 +4,7 @@ import { connectToDatabase } from "@/app/lib/mongodb";
import User from "@/app/models/User";
import jwt from "jsonwebtoken";
import { headers } from "next/headers";
+import { instituteDetails } from "./institute";
export async function checkUser({ email }) {
if (!email || email === "") {
@@ -49,3 +50,15 @@ export async function checkAuth() {
return email;
}
+
+export async function verifyUserMail({ instituteCode, email }) {
+ const institute = await instituteDetails({ instituteCode });
+
+ const emailDomain = email.split("@")[1];
+
+ if (emailDomain != institute.domain) {
+ throw new Error("Error during email domain validation.");
+ }
+
+ return email;
+}
diff --git a/app/utils/institute.js b/app/utils/institute.js
index fc2863a..795dfe2 100644
--- a/app/utils/institute.js
+++ b/app/utils/institute.js
@@ -11,9 +11,13 @@ export async function instituteDetails({ instituteCode }) {
const institute = await Institute.findOne({
code: instituteCode
});
+ // console.log(institute);
if (!institute) {
throw new Error("Institute not found.");
}
+ if (!institute.domain) {
+ throw new Error("Institute authentication Email domain not found.");
+ }
if(!institute.authLink || institute.authLink.trim() == ""){
throw new Error("Institute authentication URL not found.");
}
@@ -23,7 +27,8 @@ export async function instituteDetails({ instituteCode }) {
if(!institute.authCookie){
throw new Error("Institute authentication cookie not found.");
}
- return institute;
+ const data = JSON.parse(JSON.stringify(institute));
+ return data;
}
diff --git a/package-lock.json b/package-lock.json
index bc7334e..0df003d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,10 +11,13 @@
"@next/third-parties": "^15.0.3",
"axios": "^1.7.7",
"bootstrap": "^5.3.3",
+ "crypto-js": "^4.2.0",
"jsonwebtoken": "^9.0.2",
"mongo-sanitize": "^1.1.0",
"mongoose": "^8.7.0",
"next": "14.2.13",
+ "nodemailer": "^6.9.16",
+ "otp-generator": "^4.0.1",
"react": "^18",
"react-bootstrap": "^2.10.5",
"react-dom": "^18",
@@ -705,6 +708,12 @@
"node": ">= 8"
}
},
+ "node_modules/crypto-js": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+ "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
+ "license": "MIT"
+ },
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -1466,6 +1475,15 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/nodemailer": {
+ "version": "6.9.16",
+ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.16.tgz",
+ "integrity": "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==",
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -1492,6 +1510,15 @@
"node": ">= 6"
}
},
+ "node_modules/otp-generator": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/otp-generator/-/otp-generator-4.0.1.tgz",
+ "integrity": "sha512-2TJ52vUftA0+J3eque4wwVtpaL4/NdIXDL0gFWFJFVUAZwAN7+9tltMhL7GCNYaHJtuONoier8Hayyj4HLbSag==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.10.0"
+ }
+ },
"node_modules/package-json-from-dist": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
diff --git a/package.json b/package.json
index 90bbf92..8427ef1 100644
--- a/package.json
+++ b/package.json
@@ -12,10 +12,13 @@
"@next/third-parties": "^15.0.3",
"axios": "^1.7.7",
"bootstrap": "^5.3.3",
+ "crypto-js": "^4.2.0",
"jsonwebtoken": "^9.0.2",
"mongo-sanitize": "^1.1.0",
"mongoose": "^8.7.0",
"next": "14.2.13",
+ "nodemailer": "^6.9.16",
+ "otp-generator": "^4.0.1",
"react": "^18",
"react-bootstrap": "^2.10.5",
"react-dom": "^18",
From a18be535c062db780f2fdbdc8854b2bfe8bcf564 Mon Sep 17 00:00:00 2001
From: Adi-204
Date: Thu, 26 Dec 2024 14:57:47 +0530
Subject: [PATCH 2/6] Correction of hashData
---
app/api/authenticate/generate/route.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/api/authenticate/generate/route.js b/app/api/authenticate/generate/route.js
index 648a64e..122b7fd 100644
--- a/app/api/authenticate/generate/route.js
+++ b/app/api/authenticate/generate/route.js
@@ -19,7 +19,7 @@ export async function POST(req) {
const data = `${instituteCode}.${email}.${otp}`;
- const hashData = HmacSHA256(JSON.stringify(data), process.env.HASH_SECRET).toString() + "." + expiryTime;
+ const hashData = HmacSHA256(data, process.env.HASH_SECRET).toString() + "." + expiryTime;
const transporter = nodemailer.createTransport({
service: 'gmail',
From b56cbf50d4059f058109f69f5c354612e52d65e2 Mon Sep 17 00:00:00 2001
From: Adi-204
Date: Fri, 27 Dec 2024 00:33:36 +0530
Subject: [PATCH 3/6] Solved jwt issue
---
app/api/authenticate/validate-jwt/route.js | 14 ++++----------
app/api/authenticate/verify/route.js | 21 ++++++++++++++++-----
app/api/register/route.js | 2 --
3 files changed, 20 insertions(+), 17 deletions(-)
diff --git a/app/api/authenticate/validate-jwt/route.js b/app/api/authenticate/validate-jwt/route.js
index 58047b8..6a30111 100644
--- a/app/api/authenticate/validate-jwt/route.js
+++ b/app/api/authenticate/validate-jwt/route.js
@@ -2,24 +2,18 @@ import { NextResponse } from "next/server";
import { cookies } from "next/headers";
import jwt from "jsonwebtoken";
-export async function GET() {
+export async function GET(req) {
try {
-
+
const cookieStore = cookies();
-
+
const authCookie = req.headers.get("Cookie");
if (!authCookie) {
throw new Error("No cookies found in the request.");
}
- const [authCookiePair] = authCookie.split("; ");
-
- if (!authCookiePair) {
- throw new Error("Invalid cookie format.");
- }
-
- const [authCookieName, token] = authCookiePair.split("=");
+ const [authCookieName, token] = authCookie.split("=");
if (!authCookieName || !token) {
throw new Error("Authentication cookie not found.");
diff --git a/app/api/authenticate/verify/route.js b/app/api/authenticate/verify/route.js
index d37e5dc..118e4dc 100644
--- a/app/api/authenticate/verify/route.js
+++ b/app/api/authenticate/verify/route.js
@@ -4,6 +4,13 @@ import { HmacSHA256 } from "crypto-js";
import validator from "validator";
import { cookies } from "next/headers";
import { instituteDetails } from "@/app/utils/institute";
+import jwt from "jsonwebtoken";
+
+const createJwt = (payload) => {
+ const secret = process.env.JWT_SECRET;
+ const token = jwt.sign(payload, secret, { expiresIn: '1h' }); // Set expiration time
+ return token;
+};
export async function POST(req) {
try {
@@ -16,7 +23,7 @@ export async function POST(req) {
// iv) otp - (Submitted OTP) - Text
const cookieStore = cookies();
-
+
const { email, hashData, instituteCode, otp } = req;
if (
@@ -49,20 +56,24 @@ export async function POST(req) {
const expiryTime = parseInt(hash[1]);
const currentTime = Date.now();
-
+
if (currentTime > expiryTime) {
cookieStore.delete("otpData");
throw new Error("OTP has expired. Please request a new one.");
}
-
+
const institute = await instituteDetails({ instituteCode });
+ const payload = { email };
+
+ const token = createJwt(payload);
+
cookieStore.set({
name: institute.authCookie,
- value: JSON.stringify({ email }),
+ value: token,
httpOnly: true,
path: "/",
- maxAge: 5 * 60, // Temporary maxAge
+ maxAge: 60 * 60, // Temporary maxAge
});
cookieStore.delete("otpData");
diff --git a/app/api/register/route.js b/app/api/register/route.js
index 014a247..efb315a 100644
--- a/app/api/register/route.js
+++ b/app/api/register/route.js
@@ -20,7 +20,6 @@ export async function POST(req) {
// v) instituteCode - (Institute Code) – Text
let { name, roll, number, instituteCode } = req;
-
const institute = await instituteDetails({ instituteCode });
let { authCookie, verifyAuthLink } = institute;
@@ -50,7 +49,6 @@ export async function POST(req) {
}
);
}
-
const response = await Axios.get(verifyAuthLink, {
headers: {
Cookie: Object.entries({
From f2b9084b779096239de287a9d25ebf641103d858 Mon Sep 17 00:00:00 2001
From: Adi-204
Date: Sun, 29 Dec 2024 11:42:54 +0530
Subject: [PATCH 4/6] Made requested changes
---
.env.example | 5 +++
README.md | 13 +++++---
app/api/authenticate/generate/route.js | 9 ++++++
app/api/authenticate/validate-jwt/route.js | 31 +++++++++++++------
app/api/register/route.js | 24 +++++++++++++-
.../generate-otp/GenerateOtpForm.jsx | 5 +--
app/authenticate/verify-otp/VerifyOtpForm.jsx | 2 +-
app/utils/auth.js | 15 +--------
8 files changed, 69 insertions(+), 35 deletions(-)
create mode 100644 .env.example
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..727217f
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,5 @@
+MONGODB_URI=your_mongodb_connection_string
+JWT_SECRET=your_jwt_secret_key
+HASH_SECRET=your_HmacSHA256_secret_key
+SYSTEM_MAIL=your_email_address
+SYSTEM_MAIL_PASS=your_email_address_password
\ No newline at end of file
diff --git a/README.md b/README.md
index 504e797..c2f042b 100644
--- a/README.md
+++ b/README.md
@@ -82,17 +82,20 @@ To set up Travel Buddy locally, follow these steps:
3. Configure environment variables:
- Create a `.env` file in the root directory.
- - Add your MongoDB Atlas connection string:
+ - Copy the content from `.env.example` to `.env`:
- ```env
- MONGODB_URI=your_mongodb_connection_string
+ ```bash
+ cp .env.example .env
```
- - Add your JWT secret key:
-
```env
+ MONGODB_URI=your_mongodb_connection_string
JWT_SECRET=your_jwt_secret_key
+ HASH_SECRET=your_HmacSHA256_secret_key
+ SYSTEM_MAIL=your_email_address
+ SYSTEM_MAIL_PASS=your_email_address_password
```
+ - Replace placeholder values in the .env file with actual values.
4. Run the application:
```sh
diff --git a/app/api/authenticate/generate/route.js b/app/api/authenticate/generate/route.js
index 122b7fd..d514ab7 100644
--- a/app/api/authenticate/generate/route.js
+++ b/app/api/authenticate/generate/route.js
@@ -3,6 +3,7 @@ import otpGenerator from "otp-generator";
import { HmacSHA256 } from "crypto-js";
import nodemailer from "nodemailer";
import { cookies } from "next/headers";
+import { instituteDetails } from "@/app/utils/institute";
export async function POST(req) {
try {
@@ -13,6 +14,14 @@ export async function POST(req) {
const { email, instituteCode } = req;
+ const institute = await instituteDetails({ instituteCode });
+
+ const emailDomain = email.split("@")[1];
+
+ if (emailDomain != institute.domain) {
+ throw new Error("Error during email domain validation.");
+ }
+
const otp = otpGenerator.generate(6, { lowerCaseAlphabets: false, upperCaseAlphabets: false, specialChars: false });
const expiryTime = Date.now() + 5 * 60 * 1000; // 5 mins
diff --git a/app/api/authenticate/validate-jwt/route.js b/app/api/authenticate/validate-jwt/route.js
index 6a30111..575c3b6 100644
--- a/app/api/authenticate/validate-jwt/route.js
+++ b/app/api/authenticate/validate-jwt/route.js
@@ -1,22 +1,33 @@
import { NextResponse } from "next/server";
import { cookies } from "next/headers";
import jwt from "jsonwebtoken";
+import { instituteDetails } from "@/app/utils/institute";
export async function GET(req) {
try {
- const cookieStore = cookies();
+ const { searchParams } = new URL(req.url);
+
+ const instituteCode = searchParams.get("instituteCode");
+
+ const institute = await instituteDetails({ instituteCode });
+
+ const { authCookie } = institute;
- const authCookie = req.headers.get("Cookie");
+ const cookieStore = cookies();
+
+ const cookie = cookieStore.get(authCookie);
- if (!authCookie) {
- throw new Error("No cookies found in the request.");
+ if (!cookie) {
+ throw new Error("No JWT cookie found in the request.");
}
- const [authCookieName, token] = authCookie.split("=");
+ const token = cookie.value;
+
+ console.log(token);
- if (!authCookieName || !token) {
- throw new Error("Authentication cookie not found.");
+ if (!token) {
+ throw new Error("No JWT session token found.");
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
@@ -25,14 +36,14 @@ export async function GET(req) {
const currentTime = Date.now();
if (currentTime >= expiryTime * 1000) {
- cookieStore.delete("authCookieName");
- throw new Error("Authentication token has expired.");
+ cookieStore.delete(cookie);
+ throw new Error("JWT cookie token has expired.");
}
return NextResponse.json(
{
message: "Authentication successful.",
- email: decoded.email,
+ email: decoded.email,
},
{
status: 200,
diff --git a/app/api/register/route.js b/app/api/register/route.js
index efb315a..ada6c24 100644
--- a/app/api/register/route.js
+++ b/app/api/register/route.js
@@ -49,7 +49,24 @@ export async function POST(req) {
}
);
}
- const response = await Axios.get(verifyAuthLink, {
+
+ const isExternal = verifyAuthLink.startsWith("http://") || verifyAuthLink.startsWith("https://");
+
+ let verifyrouteURL;
+
+ if(isExternal){
+ verifyrouteURL = verifyAuthLink;
+ }
+
+ else{
+ verifyrouteURL = "https://travel.metakgp.org/" + verifyAuthLink;
+ }
+
+ if(!verifyrouteURL || verifyrouteURL === ""){
+ throw new Error("Invalid authentication verfiy URL.")
+ }
+
+ const response = await Axios.get(verifyrouteURL, {
headers: {
Cookie: Object.entries({
[authCookie]: token,
@@ -58,6 +75,11 @@ export async function POST(req) {
.join("; "),
},
});
+
+ if (!response || !response.data || !response.data.email) {
+ throw new Error("No JWT session token found.");
+ }
+
const email = response.data.email;
if (!email) {
diff --git a/app/authenticate/generate-otp/GenerateOtpForm.jsx b/app/authenticate/generate-otp/GenerateOtpForm.jsx
index 043439e..73b89a4 100644
--- a/app/authenticate/generate-otp/GenerateOtpForm.jsx
+++ b/app/authenticate/generate-otp/GenerateOtpForm.jsx
@@ -1,6 +1,5 @@
"use client";
-import { verifyUserMail } from "@/app/utils/auth";
import Loading from "@/app/utils/Loading";
import { useState } from "react";
import { useRouter } from "next/navigation";
@@ -17,10 +16,8 @@ const GenerateOtpForm = ({ instituteCode }) => {
const handleSubmit = async (e) => {
e.preventDefault();
- const verifiedEmail = await verifyUserMail({ instituteCode, email });
-
const data = {
- email: verifiedEmail,
+ email: email,
instituteCode: instituteCode
}
diff --git a/app/authenticate/verify-otp/VerifyOtpForm.jsx b/app/authenticate/verify-otp/VerifyOtpForm.jsx
index 9db21d7..6b848c0 100644
--- a/app/authenticate/verify-otp/VerifyOtpForm.jsx
+++ b/app/authenticate/verify-otp/VerifyOtpForm.jsx
@@ -38,7 +38,7 @@ const VerifyOtpForm = ({ email, hashData, instituteCode }) => {
if (res.ok) {
const json = await res.json();
- router.push("/");
+ router.push("/register?instituteCode=" + instituteCode);
} else {
const json = await res.json();
alert(json.message);
diff --git a/app/utils/auth.js b/app/utils/auth.js
index 8046c12..c12b1b2 100644
--- a/app/utils/auth.js
+++ b/app/utils/auth.js
@@ -4,7 +4,6 @@ import { connectToDatabase } from "@/app/lib/mongodb";
import User from "@/app/models/User";
import jwt from "jsonwebtoken";
import { headers } from "next/headers";
-import { instituteDetails } from "./institute";
export async function checkUser({ email }) {
if (!email || email === "") {
@@ -49,16 +48,4 @@ export async function checkAuth() {
}
return email;
-}
-
-export async function verifyUserMail({ instituteCode, email }) {
- const institute = await instituteDetails({ instituteCode });
-
- const emailDomain = email.split("@")[1];
-
- if (emailDomain != institute.domain) {
- throw new Error("Error during email domain validation.");
- }
-
- return email;
-}
+}
\ No newline at end of file
From ac354feb4cbc8b3e0a812d8bc2ec993eb826e94e Mon Sep 17 00:00:00 2001
From: Adi-204
Date: Wed, 1 Jan 2025 12:25:54 +0530
Subject: [PATCH 5/6] Code Refinement
---
app/api/authenticate/generate/route.js | 2 +-
app/api/authenticate/validate-jwt/route.js | 2 -
app/api/authenticate/verify/route.js | 1 +
app/api/register/route.js | 16 +++---
.../generate-otp/GenerateOtpForm.jsx | 49 ++++++++++++++++---
app/authenticate/verify-otp/VerifyOtpForm.jsx | 27 +++++++---
app/register/page.jsx | 15 ++++--
app/utils/institute.js | 1 -
8 files changed, 82 insertions(+), 31 deletions(-)
diff --git a/app/api/authenticate/generate/route.js b/app/api/authenticate/generate/route.js
index d514ab7..e461e3e 100644
--- a/app/api/authenticate/generate/route.js
+++ b/app/api/authenticate/generate/route.js
@@ -59,7 +59,7 @@ export async function POST(req) {
return NextResponse.json(
{
- message: "OTP send to mail successfully!",
+ message: "OTP sent successfully! Please check your inbox.",
},
{
status: 200,
diff --git a/app/api/authenticate/validate-jwt/route.js b/app/api/authenticate/validate-jwt/route.js
index 575c3b6..4033d15 100644
--- a/app/api/authenticate/validate-jwt/route.js
+++ b/app/api/authenticate/validate-jwt/route.js
@@ -24,8 +24,6 @@ export async function GET(req) {
const token = cookie.value;
- console.log(token);
-
if (!token) {
throw new Error("No JWT session token found.");
}
diff --git a/app/api/authenticate/verify/route.js b/app/api/authenticate/verify/route.js
index 118e4dc..4f070fb 100644
--- a/app/api/authenticate/verify/route.js
+++ b/app/api/authenticate/verify/route.js
@@ -88,6 +88,7 @@ export async function POST(req) {
);
} catch (error) {
+ console.log(error.message);
return NextResponse.json(
{
message:
diff --git a/app/api/register/route.js b/app/api/register/route.js
index ada6c24..fc21dcf 100644
--- a/app/api/register/route.js
+++ b/app/api/register/route.js
@@ -3,7 +3,7 @@ import sanitize from "mongo-sanitize";
import validator from "validator";
import { connectToDatabase } from "@/app/lib/mongodb";
import User from "@/app/models/User";
-import { cookies } from "next/headers";
+import { cookies, headers } from "next/headers";
import Axios from "axios";
import { checkUser } from "@/app/utils/auth";
import { instituteDetails } from "@/app/utils/institute";
@@ -11,7 +11,6 @@ import { instituteDetails } from "@/app/utils/institute";
export async function POST(req) {
try {
req = await req.json();
-
// Form fields:
// i) name – (Full Name) - Text
// ii) roll – (Roll Number) – Text
@@ -54,16 +53,19 @@ export async function POST(req) {
let verifyrouteURL;
- if(isExternal){
+ if (isExternal) {
verifyrouteURL = verifyAuthLink;
}
- else{
- verifyrouteURL = "https://travel.metakgp.org/" + verifyAuthLink;
+ else {
+ const requestHeaders = headers();
+ const host = requestHeaders.get('host');
+ const protocol = requestHeaders.get('x-forwarded-proto') || 'http';
+ verifyrouteURL = protocol + "://" + host + verifyAuthLink;
}
- if(!verifyrouteURL || verifyrouteURL === ""){
- throw new Error("Invalid authentication verfiy URL.")
+ if (!verifyrouteURL || verifyrouteURL === "") {
+ throw new Error("Invalid authentication verfiy URL.");
}
const response = await Axios.get(verifyrouteURL, {
diff --git a/app/authenticate/generate-otp/GenerateOtpForm.jsx b/app/authenticate/generate-otp/GenerateOtpForm.jsx
index 73b89a4..cdff51f 100644
--- a/app/authenticate/generate-otp/GenerateOtpForm.jsx
+++ b/app/authenticate/generate-otp/GenerateOtpForm.jsx
@@ -1,14 +1,32 @@
"use client";
-import Loading from "@/app/utils/Loading";
-import { useState } from "react";
+import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
+import { instituteDetails } from "@/app/utils/institute";
const GenerateOtpForm = ({ instituteCode }) => {
const [loading, setLoading] = useState(false);
const [email, setEmail] = useState("");
+ const [institute, setInstitute] = useState("");
const router = useRouter();
+ const checkInstituteCode = async () => {
+ if (!instituteCode) {
+ alert('Institute not found.');
+ router.push("/authenticate");
+ }
+ const selectInstitute = await instituteDetails({ instituteCode });
+ if (!selectInstitute) {
+ alert('Institute not found.');
+ router.push("/authenticate");
+ }
+ setInstitute(selectInstitute.name);
+ }
+
+ useEffect(() => {
+ checkInstituteCode();
+ }, [])
+
const handleChange = (e) => {
setEmail(e.target.value);
};
@@ -35,16 +53,15 @@ const GenerateOtpForm = ({ instituteCode }) => {
if (res.ok) {
const json = await res.json();
- router.push("/authenticate/verify-otp");
+ alert(json.message);
+ router.push("/authenticate/verify-otp");;
} else {
const json = await res.json();
alert(json.message);
}
};
- return loading ? (
-
- ) : (
+ return (
+ {institute && (
+
+
+
+
+ )}
diff --git a/app/authenticate/verify-otp/VerifyOtpForm.jsx b/app/authenticate/verify-otp/VerifyOtpForm.jsx
index 6b848c0..40f2cf7 100644
--- a/app/authenticate/verify-otp/VerifyOtpForm.jsx
+++ b/app/authenticate/verify-otp/VerifyOtpForm.jsx
@@ -1,8 +1,7 @@
"use client";
-import { useState, useEffect } from "react";
+import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
-import Loading from "@/app/utils/Loading";
const VerifyOtpForm = ({ email, hashData, instituteCode }) => {
@@ -14,6 +13,17 @@ const VerifyOtpForm = ({ email, hashData, instituteCode }) => {
setOtp(e.target.value);
};
+ const check = () =>{
+ if (!hashData || !instituteCode || !email) {
+ alert("Invalid access. Redirecting to authentication page.");
+ router.push("/authenticate");
+ }
+ }
+
+ useEffect(()=>{
+ check();
+ },[])
+
const handleSubmit = async (e) => {
e.preventDefault();
@@ -38,6 +48,7 @@ const VerifyOtpForm = ({ email, hashData, instituteCode }) => {
if (res.ok) {
const json = await res.json();
+ alert(json.message);
router.push("/register?instituteCode=" + instituteCode);
} else {
const json = await res.json();
@@ -46,9 +57,7 @@ const VerifyOtpForm = ({ email, hashData, instituteCode }) => {
};
- return loading ? (
-
- ) : (
+ return (
diff --git a/app/register/page.jsx b/app/register/page.jsx
index d399401..e7c21b9 100644
--- a/app/register/page.jsx
+++ b/app/register/page.jsx
@@ -1,6 +1,6 @@
import { redirect } from "next/navigation";
import RegForm from "./RegForm";
-import { cookies } from "next/headers";
+import { cookies, headers } from "next/headers";
import { instituteDetails } from "@/app/utils/institute";
export const metadata = {
@@ -13,23 +13,28 @@ const Page = async ({ searchParams }) => {
const institute = await instituteDetails({ instituteCode });
const { authCookie, authLink } = institute;
-
+
const cookieStore = cookies();
const cookie = cookieStore.get(authCookie);
+ const requestHeaders = headers();
+ const host = requestHeaders.get('host');
+ const protocol = requestHeaders.get('x-forwarded-proto') || 'http';
+ const redirect_url = protocol + "://" + host;
+
if (!cookie) {
- redirect(authLink + "?redirect_url=https://travel.metakgp.org/");
+ redirect(authLink + "?redirect_url=" + redirect_url);
}
const token = cookie.value;
if (!token) {
- redirect(authLink + "?redirect_url=https://travel.metakgp.org/");
+ redirect(authLink + "?redirect_url=" + redirect_url);
}
const email = JSON.parse(atob(token.split(".")[1])).email; // get the user email from jwt
if (!email) {
- redirect(authLink + "?redirect_url=https://travel.metakgp.org/");
+ redirect(authLink + "?redirect_url=" + redirect_url);
}
return ;
diff --git a/app/utils/institute.js b/app/utils/institute.js
index 795dfe2..18c2eb1 100644
--- a/app/utils/institute.js
+++ b/app/utils/institute.js
@@ -11,7 +11,6 @@ export async function instituteDetails({ instituteCode }) {
const institute = await Institute.findOne({
code: instituteCode
});
- // console.log(institute);
if (!institute) {
throw new Error("Institute not found.");
}
From a37251d1771535ca006863eedf424dc12d89d71f Mon Sep 17 00:00:00 2001
From: Adi-204
Date: Wed, 8 Jan 2025 23:17:08 +0530
Subject: [PATCH 6/6] Made more changes to improve
---
app/api/authenticate/generate/route.js | 2 +-
app/api/authenticate/verify/route.js | 2 +-
app/authenticate/AuthForm.jsx | 4 +--
.../generate-otp/GenerateOtpForm.jsx | 18 ++++++-------
app/authenticate/generate-otp/page.jsx | 4 +--
app/authenticate/page.jsx | 15 ++++++++---
app/authenticate/verify-otp/VerifyOtpForm.jsx | 16 ++++++------
app/authenticate/verify-otp/page.jsx | 14 ++++++++---
app/data.json | 9 -------
app/models/Institute.js | 8 +-----
app/register/RegForm.jsx | 4 +--
app/register/page.jsx | 19 ++++++--------
app/trains/create/TrainForm.jsx | 7 +++---
app/trains/my-trains/MyTrains.jsx | 6 ++---
app/trains/train/[trainID]/TrainDetails.jsx | 6 ++---
app/trips/create/TripForm.jsx | 25 ++++++++++++-------
app/trips/my-trips/MyTrips.jsx | 6 ++---
app/trips/trip/[tripID]/TripDetails.jsx | 24 ++++++++++++------
app/trips/trip/[tripID]/page.jsx | 17 ++++++++++++-
app/utils/institute.js | 23 ++++++++++++-----
20 files changed, 133 insertions(+), 96 deletions(-)
diff --git a/app/api/authenticate/generate/route.js b/app/api/authenticate/generate/route.js
index e461e3e..e4a9324 100644
--- a/app/api/authenticate/generate/route.js
+++ b/app/api/authenticate/generate/route.js
@@ -19,7 +19,7 @@ export async function POST(req) {
const emailDomain = email.split("@")[1];
if (emailDomain != institute.domain) {
- throw new Error("Error during email domain validation.");
+ throw new Error("Invalid email. Choose the correct institute.");
}
const otp = otpGenerator.generate(6, { lowerCaseAlphabets: false, upperCaseAlphabets: false, specialChars: false });
diff --git a/app/api/authenticate/verify/route.js b/app/api/authenticate/verify/route.js
index 4f070fb..aad594f 100644
--- a/app/api/authenticate/verify/route.js
+++ b/app/api/authenticate/verify/route.js
@@ -80,7 +80,7 @@ export async function POST(req) {
return NextResponse.json(
{
- message: "OTP is valid, Verification successful!",
+ message: "OTP verified successfully!",
},
{
status: 200,
diff --git a/app/authenticate/AuthForm.jsx b/app/authenticate/AuthForm.jsx
index 3b04977..7b69c1d 100644
--- a/app/authenticate/AuthForm.jsx
+++ b/app/authenticate/AuthForm.jsx
@@ -2,7 +2,7 @@
import { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
-export default function AuthForm({ institutes }) {
+export default function AuthForm({ institutes, redirect_url }) {
const router = useRouter();
const check = () => {
@@ -28,7 +28,7 @@ export default function AuthForm({ institutes }) {
alert("Please select an institute");
return;
}
- router.push("/register?instituteCode=" + institute);
+ router.push("/register?instituteCode=" + institute + "&redirect_url=" + redirect_url);
return;
};
diff --git a/app/authenticate/generate-otp/GenerateOtpForm.jsx b/app/authenticate/generate-otp/GenerateOtpForm.jsx
index cdff51f..150c124 100644
--- a/app/authenticate/generate-otp/GenerateOtpForm.jsx
+++ b/app/authenticate/generate-otp/GenerateOtpForm.jsx
@@ -4,7 +4,7 @@ import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { instituteDetails } from "@/app/utils/institute";
-const GenerateOtpForm = ({ instituteCode }) => {
+const GenerateOtpForm = ({ instituteCode, redirect_url }) => {
const [loading, setLoading] = useState(false);
const [email, setEmail] = useState("");
const [institute, setInstitute] = useState("");
@@ -13,14 +13,14 @@ const GenerateOtpForm = ({ instituteCode }) => {
const checkInstituteCode = async () => {
if (!instituteCode) {
alert('Institute not found.');
- router.push("/authenticate");
+ router.push("/");
}
- const selectInstitute = await instituteDetails({ instituteCode });
- if (!selectInstitute) {
- alert('Institute not found.');
- router.push("/authenticate");
+ try {
+ const selectInstitute = await instituteDetails({ instituteCode });
+ setInstitute(selectInstitute.name);
+ } catch (error) {
+ router.push("/");
}
- setInstitute(selectInstitute.name);
}
useEffect(() => {
@@ -54,7 +54,7 @@ const GenerateOtpForm = ({ instituteCode }) => {
if (res.ok) {
const json = await res.json();
alert(json.message);
- router.push("/authenticate/verify-otp");;
+ router.push("/authenticate/verify-otp?redirect_url=" + redirect_url);
} else {
const json = await res.json();
alert(json.message);
@@ -104,7 +104,7 @@ const GenerateOtpForm = ({ instituteCode }) => {
: "bg-blue-500 text-white hover:bg-blue-600"
}`}
>
- {loading ? "Submitting..." : "Send OTP"}
+ {loading ? "Sending OTP..." : "Send OTP"}
diff --git a/app/authenticate/generate-otp/page.jsx b/app/authenticate/generate-otp/page.jsx
index 63e29c5..54eee69 100644
--- a/app/authenticate/generate-otp/page.jsx
+++ b/app/authenticate/generate-otp/page.jsx
@@ -6,13 +6,13 @@ export const metadata = {
const Page = async ({ searchParams }) => {
- let { instituteCode } = searchParams;
+ let { instituteCode, redirect_url } = searchParams;
if (instituteCode) {
instituteCode = instituteCode.split("?")[0];
}
- return ;
+ return ;
};
export default Page;
diff --git a/app/authenticate/page.jsx b/app/authenticate/page.jsx
index 3f6a1e3..99f926b 100644
--- a/app/authenticate/page.jsx
+++ b/app/authenticate/page.jsx
@@ -1,20 +1,29 @@
import { connectToDatabase } from "@/app/lib/mongodb";
import Institute from "@/app/models/Institute";
import AuthForm from "./AuthForm";
+import { headers } from "next/headers";
export const metadata = {
title: "Select Institute",
};
-const Page = async () => {
+const Page = async ({ searchParams }) => {
+
+ const { redirect_path } = searchParams;
+
+ const requestHeaders = headers();
+ const host = requestHeaders.get('host');
+ const protocol = requestHeaders.get('x-forwarded-proto') || 'http';
+ const redirect_url = protocol + "://" + host + redirect_path;
+
await connectToDatabase();
// Return only names of Institues
const institutes = await Institute.find({}, { _id: 0, name: 1, code: 1 });
-
+
const institutesData = JSON.parse(JSON.stringify(institutes));
- return ;
+ return ;
};
export default Page;
diff --git a/app/authenticate/verify-otp/VerifyOtpForm.jsx b/app/authenticate/verify-otp/VerifyOtpForm.jsx
index 40f2cf7..849bd0b 100644
--- a/app/authenticate/verify-otp/VerifyOtpForm.jsx
+++ b/app/authenticate/verify-otp/VerifyOtpForm.jsx
@@ -3,7 +3,7 @@
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
-const VerifyOtpForm = ({ email, hashData, instituteCode }) => {
+const VerifyOtpForm = ({ email, hashData, instituteCode, redirect_url }) => {
const [loading, setLoading] = useState(false);
const [otp, setOtp] = useState("");
@@ -13,16 +13,16 @@ const VerifyOtpForm = ({ email, hashData, instituteCode }) => {
setOtp(e.target.value);
};
- const check = () =>{
+ const check = () => {
if (!hashData || !instituteCode || !email) {
alert("Invalid access. Redirecting to authentication page.");
router.push("/authenticate");
}
}
- useEffect(()=>{
+ useEffect(() => {
check();
- },[])
+ }, [])
const handleSubmit = async (e) => {
e.preventDefault();
@@ -49,7 +49,7 @@ const VerifyOtpForm = ({ email, hashData, instituteCode }) => {
if (res.ok) {
const json = await res.json();
alert(json.message);
- router.push("/register?instituteCode=" + instituteCode);
+ router.push("/register?instituteCode=" + instituteCode + "&redirect_url=" + redirect_url);
} else {
const json = await res.json();
alert(json.message);
@@ -83,11 +83,11 @@ const VerifyOtpForm = ({ email, hashData, instituteCode }) => {
type="submit"
disabled={loading}
className={`w-full p-2 rounded-md transition ${loading
- ? "bg-gray-400 cursor-not-allowed"
- : "bg-blue-500 text-white hover:bg-blue-600"
+ ? "bg-gray-400 cursor-not-allowed"
+ : "bg-blue-500 text-white hover:bg-blue-600"
}`}
>
- {loading ? "Submitting..." : "Submit"}
+ {loading ? "Verifying..." : "Verify"}
diff --git a/app/authenticate/verify-otp/page.jsx b/app/authenticate/verify-otp/page.jsx
index a94dfd7..2591be6 100644
--- a/app/authenticate/verify-otp/page.jsx
+++ b/app/authenticate/verify-otp/page.jsx
@@ -6,8 +6,9 @@ export const metadata = {
title: "Enter OTP for Email verification",
};
-const Page = async () => {
-
+const Page = async ({ searchParams }) => {
+ const { redirect_url } = searchParams;
+ console.log(redirect_url);
const cookieStore = cookies();
const cookie = cookieStore.get("otpData");
@@ -23,11 +24,16 @@ const Page = async () => {
const { email, hashData, instituteCode } = JSON.parse(token);
- if(!email || !hashData || !instituteCode){
+ if (!email || !hashData || !instituteCode) {
redirect("/authenticate/generate-otp");
}
- return ;
+ return ;
};
export default Page;
diff --git a/app/data.json b/app/data.json
index 8e0103f..0c9947f 100644
--- a/app/data.json
+++ b/app/data.json
@@ -1,13 +1,4 @@
{
- "locations": {
- "IIT": "IIT Kharagpur Campus",
- "CCU": "Kolkata Airport",
- "KGP": "Kharagpur Railway Station",
- "HWH": "Howrah Railway Station",
- "SDAH": "Sealdah Railway Station",
- "HIJ": "Hijli Railway Station",
- "CAT": "Kolkata CAT Centre(s)"
- },
"slots": {
"0": "12 AM - 1 AM",
"1": "1 AM - 2 AM",
diff --git a/app/models/Institute.js b/app/models/Institute.js
index 9c659f9..bd4b911 100644
--- a/app/models/Institute.js
+++ b/app/models/Institute.js
@@ -23,14 +23,8 @@ const instituteSchema = new Schema({
required: [true, "Please provide a domain for the email"],
},
locations: {
- type: [String],
+ type: Object,
required: [true, "Please provide at least one location"],
- validate: {
- validator: function (locations) {
- return locations.length > 0;
- },
- message: "Institute must have at least one location",
- },
},
authLink: {
type: String,
diff --git a/app/register/RegForm.jsx b/app/register/RegForm.jsx
index bb5f170..f4e1dcb 100644
--- a/app/register/RegForm.jsx
+++ b/app/register/RegForm.jsx
@@ -5,7 +5,7 @@ import Loading from "@/app/utils/Loading";
import { useRouter } from "next/navigation";
import { checkUser } from "@/app/utils/auth";
-export default function RegForm({ email, instituteCode }) {
+export default function RegForm({ email, instituteCode, redirect_url }) {
const router = useRouter();
const [loading, setLoading] = useState(true);
const [formData, setFormData] = useState({
@@ -76,7 +76,7 @@ export default function RegForm({ email, instituteCode }) {
const json = await res.json();
alert(json.message);
localStorage.setItem("travelbuddy", json.user);
- router.push("/");
+ router.push(redirect_url);
} else {
const json = await res.json();
alert(json.message);
diff --git a/app/register/page.jsx b/app/register/page.jsx
index e7c21b9..67c6bcb 100644
--- a/app/register/page.jsx
+++ b/app/register/page.jsx
@@ -1,6 +1,6 @@
import { redirect } from "next/navigation";
import RegForm from "./RegForm";
-import { cookies, headers } from "next/headers";
+import { cookies } from "next/headers";
import { instituteDetails } from "@/app/utils/institute";
export const metadata = {
@@ -8,36 +8,31 @@ export const metadata = {
};
const Page = async ({ searchParams }) => {
- const { instituteCode } = searchParams;
+ const { instituteCode, redirect_url } = searchParams;
const institute = await instituteDetails({ instituteCode });
const { authCookie, authLink } = institute;
-
+
const cookieStore = cookies();
const cookie = cookieStore.get(authCookie);
- const requestHeaders = headers();
- const host = requestHeaders.get('host');
- const protocol = requestHeaders.get('x-forwarded-proto') || 'http';
- const redirect_url = protocol + "://" + host;
-
if (!cookie) {
- redirect(authLink + "?redirect_url=" + redirect_url);
+ redirect(authLink + "&redirect_url=" + redirect_url);
}
const token = cookie.value;
if (!token) {
- redirect(authLink + "?redirect_url=" + redirect_url);
+ redirect(authLink + "&redirect_url=" + redirect_url);
}
const email = JSON.parse(atob(token.split(".")[1])).email; // get the user email from jwt
if (!email) {
- redirect(authLink + "?redirect_url=" + redirect_url);
+ redirect(authLink + "&redirect_url=" + redirect_url);
}
- return ;
+ return ;
};
export default Page;
diff --git a/app/trains/create/TrainForm.jsx b/app/trains/create/TrainForm.jsx
index de5d4e4..e94e976 100644
--- a/app/trains/create/TrainForm.jsx
+++ b/app/trains/create/TrainForm.jsx
@@ -1,6 +1,6 @@
"use client";
-import { useRouter } from "next/navigation";
+import { useRouter, usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import Loading from "@/app/utils/Loading";
import Link from "next/link";
@@ -9,20 +9,21 @@ import { verifyUser } from "@/app/utils/auth";
const TrainForm = () => {
const router = useRouter();
+ const pathname = usePathname();
const [loading, setLoading] = useState(true);
const [email, setEmail] = useState("");
const check = async () => {
if (!localStorage.getItem("travelbuddy")) {
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
const token = localStorage.getItem("travelbuddy");
const email = await verifyUser({ token });
if (!email) {
localStorage.removeItem("travelbuddy");
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
setEmail(email);
diff --git a/app/trains/my-trains/MyTrains.jsx b/app/trains/my-trains/MyTrains.jsx
index 1376cf1..72b5856 100644
--- a/app/trains/my-trains/MyTrains.jsx
+++ b/app/trains/my-trains/MyTrains.jsx
@@ -1,18 +1,18 @@
"use client";
import Link from "next/link";
-import { useRouter } from "next/navigation";
+import { useRouter, usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import Loading from "@/app/utils/Loading";
const MyTrains = () => {
const router = useRouter();
-
+ const pathname = usePathname();
const [myTrains, setMyTrains] = useState(null);
const getDetails = async () => {
if (!localStorage.getItem("travelbuddy")) {
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
const res = await fetch("/api/trains/find", {
diff --git a/app/trains/train/[trainID]/TrainDetails.jsx b/app/trains/train/[trainID]/TrainDetails.jsx
index 300d8c2..2872088 100644
--- a/app/trains/train/[trainID]/TrainDetails.jsx
+++ b/app/trains/train/[trainID]/TrainDetails.jsx
@@ -1,18 +1,18 @@
"use client";
-import { useRouter } from "next/navigation";
+import { useRouter, usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import Link from "next/link";
import Loading from "@/app/utils/Loading";
const TrainDetails = ({ trainID }) => {
const router = useRouter();
-
+ const pathname = usePathname();
const [data, setData] = useState(null);
const getDetails = async () => {
if (!localStorage.getItem("travelbuddy")) {
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
const res = await fetch("/api/trains/find", {
diff --git a/app/trips/create/TripForm.jsx b/app/trips/create/TripForm.jsx
index 561227b..b7e207b 100644
--- a/app/trips/create/TripForm.jsx
+++ b/app/trips/create/TripForm.jsx
@@ -1,33 +1,40 @@
"use client";
-import { useRouter } from "next/navigation";
+import { useRouter, usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import Loading from "@/app/utils/Loading";
import Link from "next/link";
import { today } from "@/app/utils/date";
import { verifyUser } from "@/app/utils/auth";
import data from "@/app/data.json";
+import { userInstituteDetails } from "@/app/utils/institute";
const TripForm = () => {
const router = useRouter();
+ const pathname = usePathname();
const [loading, setLoading] = useState(true);
-
+ const [locations, setLocations] = useState({});
const [email, setEmail] = useState("");
const check = async () => {
if (!localStorage.getItem("travelbuddy")) {
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
const token = localStorage.getItem("travelbuddy");
const email = await verifyUser({ token });
if (!email) {
localStorage.removeItem("travelbuddy");
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
setEmail(email);
-
+ try {
+ const userInstitue = await userInstituteDetails({email});
+ setLocations(userInstitue.locations);
+ } catch (error) {
+ router.push("/");
+ }
setLoading(false);
};
@@ -155,9 +162,9 @@ const TripForm = () => {
className="border rounded-md p-2 w-full"
>
- {Object.keys(data.locations).map((location) => (
+ {Object.keys(locations).map((location) => (
))}
@@ -173,9 +180,9 @@ const TripForm = () => {
className="border rounded-md p-2 w-full"
>
- {Object.keys(data.locations).map((location) => (
+ {Object.keys(locations).map((location) => (
))}
diff --git a/app/trips/my-trips/MyTrips.jsx b/app/trips/my-trips/MyTrips.jsx
index 52fb724..af9ffb2 100644
--- a/app/trips/my-trips/MyTrips.jsx
+++ b/app/trips/my-trips/MyTrips.jsx
@@ -1,19 +1,19 @@
"use client";
import Link from "next/link";
-import { useRouter } from "next/navigation";
+import { useRouter, usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import Loading from "@/app/utils/Loading";
import mapping from "@/app/data.json";
const MyTrips = () => {
const router = useRouter();
-
+ const pathname = usePathname();
const [myTrips, setMyTrips] = useState(null);
const getDetails = async () => {
if (!localStorage.getItem("travelbuddy")) {
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
const res = await fetch("/api/trips/find", {
diff --git a/app/trips/trip/[tripID]/TripDetails.jsx b/app/trips/trip/[tripID]/TripDetails.jsx
index 3d9df84..f433fc7 100644
--- a/app/trips/trip/[tripID]/TripDetails.jsx
+++ b/app/trips/trip/[tripID]/TripDetails.jsx
@@ -1,20 +1,28 @@
"use client";
-import { useRouter } from "next/navigation";
+import { useRouter, usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import Loading from "@/app/utils/Loading";
import mapping from "@/app/data.json";
+import { userInstituteDetails } from "@/app/utils/institute";
-const TripDetails = ({ tripID }) => {
+const TripDetails = ({ tripID, email }) => {
const router = useRouter();
-
+ const pathname = usePathname();
const [data, setData] = useState(null);
+ const [locations, setLocations] = useState({});
const getDetails = async () => {
if (!localStorage.getItem("travelbuddy")) {
- router.push("/authenticate");
+ router.push("/authenticate?redirect_path=" + pathname);
return;
}
+ try {
+ const userInstitue = await userInstituteDetails({ email });
+ setLocations(userInstitue.locations);
+ } catch (error) {
+ router.push("/");
+ }
const res = await fetch("/api/trips/find", {
method: "POST",
headers: {
@@ -107,11 +115,11 @@ const TripDetails = ({ tripID }) => {
Email: {data.trip.email}
- Source: {mapping.locations[data.trip.source]}
+ Source: {locations[data.trip.source]}
Destination:{" "}
- {mapping.locations[data.trip.destination]}
+ {locations[data.trip.destination]}
Date:{" "}
@@ -158,11 +166,11 @@ const TripDetails = ({ tripID }) => {
Source:{" "}
- {mapping.locations[trip.source]}
+ {locations[trip.source]}
Destination:{" "}
- {mapping.locations[trip.destination]}
+ {locations[trip.destination]}
Date:{" "}
diff --git a/app/trips/trip/[tripID]/page.jsx b/app/trips/trip/[tripID]/page.jsx
index 026ce4f..dcd1df0 100644
--- a/app/trips/trip/[tripID]/page.jsx
+++ b/app/trips/trip/[tripID]/page.jsx
@@ -1,4 +1,7 @@
+import { connectToDatabase } from "@/app/lib/mongodb";
import TripDetails from "./TripDetails";
+import Trip from "@/app/models/Trip";
+import { redirect } from "next/navigation";
export const metadata = {
title: "Trip Details",
@@ -7,7 +10,19 @@ export const metadata = {
const Page = async ({ params }) => {
const tripID = params.tripID;
- return ;
+ await connectToDatabase();
+
+ const trip = await Trip.findOne({
+ tripID: tripID
+ });
+
+ if(!trip){
+ redirect('/');
+ }
+
+ const tripData = JSON.parse(JSON.stringify(trip));
+
+ return ;
};
export default Page;
diff --git a/app/utils/institute.js b/app/utils/institute.js
index 18c2eb1..b300461 100644
--- a/app/utils/institute.js
+++ b/app/utils/institute.js
@@ -2,6 +2,7 @@
import { connectToDatabase } from "@/app/lib/mongodb";
import Institute from "@/app/models/Institute";
+import User from "@/app/models/User";
export async function instituteDetails({ instituteCode }) {
if (!instituteCode || instituteCode == "") {
@@ -15,20 +16,30 @@ export async function instituteDetails({ instituteCode }) {
throw new Error("Institute not found.");
}
if (!institute.domain) {
- throw new Error("Institute authentication Email domain not found.");
- }
- if(!institute.authLink || institute.authLink.trim() == ""){
+ throw new Error("Institute authentication Email domain not found.");
+ }
+ if (!institute.authLink || institute.authLink.trim() == "") {
throw new Error("Institute authentication URL not found.");
}
- if(!institute.verifyAuthLink || institute.verifyAuthLink.trim() == ""){
+ if (!institute.verifyAuthLink || institute.verifyAuthLink.trim() == "") {
throw new Error("Institute authentication verification URL not found.");
}
- if(!institute.authCookie){
+ if (!institute.authCookie) {
throw new Error("Institute authentication cookie not found.");
}
const data = JSON.parse(JSON.stringify(institute));
return data;
}
-
+export async function userInstituteDetails({ email }) {
+ await connectToDatabase();
+ const user = await User.findOne({
+ email: email
+ });
+ if (!user) {
+ throw new Error("User not found.");
+ }
+ const institute = await instituteDetails({ instituteCode: user.instituteCode });
+ return institute;
+}