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 ? ( + + ) : ( +
+
+

+ College Email Authentication +

+
+ + +
+ +
+
+ ); +} + +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 ? ( + + ) : ( +
+
+

+ OTP Verification +

+
+ + +
+ +
+
+ ); +} + +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 (
{ className="border rounded-md p-2 w-full focus:ring focus:ring-blue-300" />
+ {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; +}