diff --git a/.all-contributorsrc b/.all-contributorsrc
index 5381eb142..1df78fb21 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -761,6 +761,15 @@
"contributions": [
"doc"
]
+ },
+ {
+ "login": "Ajen07",
+ "name": "Arman Kumar Jena",
+ "avatar_url": "https://avatars.githubusercontent.com/u/112967686?v=4",
+ "profile": "https://github.com/Ajen07",
+ "contributions": [
+ "code"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/README.md b/README.md
index 44f8fbce3..770a16c47 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
**Digitomize**, part of **Microsoft for Startups Founders Hub**, is an open-source platform that combines two main sections: Contests and User Profiles. It allows users to explore upcoming coding contests and dynamically create developer portfolios.
-[![All Contributors](https://img.shields.io/badge/all_contributors-83-orange.svg?style=flat-square)](#contributors-)
+[![All Contributors](https://img.shields.io/badge/all_contributors-84-orange.svg?style=flat-square)](#contributors-)
Website: [![Better Stack Badge](https://uptime.betterstack.com/status-badges/v1/monitor/zb9g.svg)](https://uptime.betterstack.com/?utm_source=status_badge)
@@ -257,6 +257,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Keshav Priyam 💻 |
Ramakrushna Biswal 💻 |
Rohith Singh 📖 |
+ Arman Kumar Jena 💻 |
diff --git a/backend/users/middlewares/authMiddleware.js b/backend/users/middlewares/authMiddleware.js
index 7fd2c68a6..108d779fb 100644
--- a/backend/users/middlewares/authMiddleware.js
+++ b/backend/users/middlewares/authMiddleware.js
@@ -23,10 +23,24 @@ const addUID = async (request, response, next) => {
}
try {
+ const decodedToken = await getAuth().verifyIdToken(authToken);
+ const {
+ uid,
+ firebase: { sign_in_provider },
+ } = decodedToken;
+
+ if (sign_in_provider === "github.com") {
+ await getAuth().updateUser(uid, {
+ emailVerified: true,
+ });
+ }
getAuth()
.verifyIdToken(authToken)
.then((decTok) => {
request.decodedToken = decTok;
+ if (decTok.firebase.sign_in_provider === "github.com") {
+ decTok.email_verified = true;
+ }
// console.log("calling next");
next();
})
diff --git a/client/src/App.jsx b/client/src/App.jsx
index 76d3ae67e..d43e29c16 100644
--- a/client/src/App.jsx
+++ b/client/src/App.jsx
@@ -34,6 +34,7 @@ import {
About,
Footer,
MetaData,
+ resendLoader
} from "./components/CustomComponents";
// import UserDashBoardAccount from "./user/dashboard/Account";
import UserDashboard from "./user/dashboard/UserDashboard";
@@ -117,6 +118,8 @@ import { userDashboardDetails } from "../api";
import Preferences from "./user/dashboard/Preferences/Preferences";
import Ratings from "./user/dashboard/Ratings/Ratings";
import Settings from "./user/dashboard/Settings/Settings";
+import ResendEmailVerification from "./pages/verification/ResendEmailVerification";
+import VerifyEmailPage from "./pages/verification/VerifyEmailPage";
function Logout() {
const navigate = useNavigate();
@@ -177,6 +180,12 @@ const router = createBrowserRouter(
} />
} loader={signupLoader} />
} loader={forgotPasswordLoader} />
+ }
+ loader={resendLoader}
+ />
+ } />
}>
} />
} />
diff --git a/client/src/ProtectedRoute.jsx b/client/src/ProtectedRoute.jsx
index f54093e9e..70b2d40e3 100644
--- a/client/src/ProtectedRoute.jsx
+++ b/client/src/ProtectedRoute.jsx
@@ -1,14 +1,20 @@
import React from "react";
import { Outlet, Navigate } from "react-router-dom";
import { useUserAuth } from "@context/UserAuthContext";
+import { auth } from "../firebase";
function ProtectedRoute() {
const { user } = useUserAuth();
- return user ? (
-
- ) : (
-
- );
+
+ if (!user) {
+ return ;
+ }
+
+ if (auth?.currentUser?.emailVerified) {
+ return ;
+ }
+
+ return ;
}
export default ProtectedRoute;
diff --git a/client/src/components/AuthButtons/GithubAuthButton.jsx b/client/src/components/AuthButtons/GithubAuthButton.jsx
index b566a7917..aa2c05e06 100644
--- a/client/src/components/AuthButtons/GithubAuthButton.jsx
+++ b/client/src/components/AuthButtons/GithubAuthButton.jsx
@@ -34,7 +34,8 @@ export default function GithubAuthButton() {
})
// .then((res) => console.log(res))
.catch((err) => console.error(err));
- navigate("/u/dashboard/account");
+ await auth.currentUser.reload()
+ navigate("/u/dashboard");
})
.catch((error) => {
toast.error(error.code, {
diff --git a/client/src/components/CustomComponents.jsx b/client/src/components/CustomComponents.jsx
index ed9ff5df7..852d02255 100644
--- a/client/src/components/CustomComponents.jsx
+++ b/client/src/components/CustomComponents.jsx
@@ -18,6 +18,7 @@ import Footer from "./globals/Footer";
import { loader as loginLoader } from "./Login";
import { loader as signupLoader } from "./globals/Signup";
import { loader as forgotPasswordLoader } from "./ForgotPassword";
+import {loader as resendLoader} from "../pages/verification/ResendEmailVerification"
import NewNavbar from "./globals/Navbar/NewNavbar";
import ScrollToTop from "./globals/ScrollToTop";
import Signup from "./globals/Signup";
@@ -51,4 +52,5 @@ export {
signupLoader,
ForgotPassword,
forgotPasswordLoader,
+ resendLoader,
};
diff --git a/client/src/components/Login.jsx b/client/src/components/Login.jsx
index 8c000b4b4..840574a5e 100644
--- a/client/src/components/Login.jsx
+++ b/client/src/components/Login.jsx
@@ -21,8 +21,9 @@ import { Eye, EyeOff } from "lucide-react";
export async function loader({ request }) {
const message = new URL(request.url).searchParams.get("message");
const loggedIn = await isLoggedIn();
+
if (loggedIn) {
- return redirect("/u/dashboard");
+ return redirect("/u/dashboard");
}
return message;
diff --git a/client/src/components/globals/Signup.jsx b/client/src/components/globals/Signup.jsx
index c0e955fb6..eb4285091 100644
--- a/client/src/components/globals/Signup.jsx
+++ b/client/src/components/globals/Signup.jsx
@@ -26,6 +26,9 @@ export async function loader() {
if (loggedIn) {
return redirect("/login");
}
+ if (loggedIn && auth.currentUser && auth.currentUser.emailVerified) {
+ return redirect("/u/dashboard");
+ }
return null;
}
@@ -105,7 +108,17 @@ export default function Signup() {
.catch((err) => setError(err.code));
}
- navigate("/login");
+ toast.success("Verification link sent to email", {
+ position: "top-right",
+ autoClose: 5000,
+ hideProgressBar: false,
+ closeOnClick: true,
+ pauseOnHover: true,
+ draggable: true,
+ progress: undefined,
+ theme: "colored",
+ });
+ navigate("/login", { replace: true });
} catch (err) {
toast.error(err.code, {
position: "top-right",
diff --git a/client/src/context/UserAuthContext.jsx b/client/src/context/UserAuthContext.jsx
index afe1c308a..77b6635f0 100644
--- a/client/src/context/UserAuthContext.jsx
+++ b/client/src/context/UserAuthContext.jsx
@@ -9,6 +9,7 @@ import {
signInWithPopup,
GithubAuthProvider,
updateProfile,
+ sendEmailVerification,
} from "firebase/auth";
import { auth } from "../../firebase";
@@ -37,6 +38,11 @@ export function UserAuthContextProvider({ children }) {
console.error("Error updating profile:", error);
});
const token = await user.getIdToken();
+ sendEmailVerification(user).then(() => {
+ /* console.log("Email verification sent."); */
+ }).catch((error) => {
+ console.error("Error sending email verification:", error);
+ });
return { result, token };
})
.catch((error) => {
diff --git a/client/src/pages/verification/ResendEmailVerification.jsx b/client/src/pages/verification/ResendEmailVerification.jsx
new file mode 100644
index 000000000..3cd06385b
--- /dev/null
+++ b/client/src/pages/verification/ResendEmailVerification.jsx
@@ -0,0 +1,92 @@
+import React, { useState } from "react";
+import { toast, ToastContainer } from "react-toastify";
+import { auth } from "../../../firebase";
+import { sendEmailVerification } from "firebase/auth";
+import { isLoggedIn } from "../../../api";
+import { redirect } from "react-router-dom";
+
+
+export async function loader() {
+ const loggedIn = await isLoggedIn();
+ if (loggedIn && auth.currentUser.emailVerified) {
+ return redirect("/u/dashboard");
+ }else if (!loggedIn ) {
+ return redirect("/login");
+ }
+ return null;
+}
+
+const ResendEmailVerification = () => {
+ const [btnState, setbtnState] = useState(false);
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ try {
+ setbtnState(true);
+ await sendEmailVerification(auth.currentUser);
+ toast.success("Verification E-Mail send", {
+ position: "top-right",
+ autoClose: 5000,
+ hideProgressBar: false,
+ closeOnClick: true,
+ pauseOnHover: true,
+ draggable: true,
+ progress: undefined,
+ theme: "colored",
+ });
+ setbtnState(false);
+ } catch (err) {
+ setbtnState(false);
+ toast.error(err.code, {
+ position: "top-right",
+ autoClose: 5000,
+ hideProgressBar: false,
+ closeOnClick: true,
+ pauseOnHover: true,
+ draggable: true,
+ progress: undefined,
+ theme: "colored",
+ });
+ }
+ };
+ return (
+ <>
+
+
+
+
+
+ Please verify your email to continue
+
+
+ Didn't receive the verification email? Click the button below to
+ resend it.
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default ResendEmailVerification;
\ No newline at end of file
diff --git a/client/src/pages/verification/VerifyEmailPage.jsx b/client/src/pages/verification/VerifyEmailPage.jsx
new file mode 100644
index 000000000..c7ce91b7a
--- /dev/null
+++ b/client/src/pages/verification/VerifyEmailPage.jsx
@@ -0,0 +1,131 @@
+import React, { useEffect, useState } from "react";
+import { auth } from "../../../firebase";
+import { applyActionCode } from "firebase/auth";
+import { ToastContainer, toast } from "react-toastify";
+import { Link } from "react-router-dom";
+
+const VerifyEmailPage = () => {
+ const [isLoading, setIsLoading] = useState(false);
+ const [isError, setIsError] = useState(false);
+ const [isSuccess, setIsSuccess] = useState(false);
+ let isMounted = true;
+ useEffect(() => {
+ const { oobCode } = Object.fromEntries(
+ new URLSearchParams(window.location.search),
+ );
+ async function verifyEmail() {
+ try {
+ setIsLoading((prevState)=>!prevState);
+
+ if (isMounted) {
+ await applyActionCode(auth, oobCode);
+ toast.success("Verification Successfull", {
+ position: "top-right",
+ autoClose: 5000,
+ hideProgressBar: false,
+ closeOnClick: true,
+ pauseOnHover: true,
+ draggable: true,
+ progress: undefined,
+ theme: "colored",
+ });
+ await auth.currentUser.reload();
+ setIsSuccess(true);
+ }
+ } catch (error) {
+
+ setIsError(true);
+ toast.error(error.code, {
+ position: "top-right",
+ autoClose: 5000,
+ hideProgressBar: false,
+ closeOnClick: true,
+ pauseOnHover: true,
+ draggable: true,
+ progress: undefined,
+ theme: "colored",
+ });
+
+ } finally {
+
+ setIsLoading(false);
+
+ }
+ }
+ verifyEmail();
+ return () => {
+ isMounted = false;
+ };
+ }, [isMounted]);
+ if (isLoading) {
+ return (
+
+ );
+ }
+ if (isSuccess) {
+ return (
+ <>
+
+
+
+
+ Verification Successful , Please Login
+
+
+
+
+
+
+ Login
+
+
+
+
+ >
+ );
+ }
+ if (isError) {
+ return (
+ <>
+
+
+ Oops!! something went wrong , Please try again later
+
+
+
+
+
+
+ Home
+
+
+
+ >
+ );
+ }
+};
+
+export default VerifyEmailPage;
\ No newline at end of file
diff --git a/client/tailwind.config.js b/client/tailwind.config.js
index cf35080c2..70dd30be4 100644
--- a/client/tailwind.config.js
+++ b/client/tailwind.config.js
@@ -1,4 +1,7 @@
/** @type {import('tailwindcss').Config} */
+const {
+ default: flattenColorPalette,
+} = require("tailwindcss/lib/util/flattenColorPalette");
export default {
content: [
"node_modules/daisyui/dist/**/*.js",