From d4b3a96678b2f9b02968765587cbc5129b11c54c Mon Sep 17 00:00:00 2001 From: arihantjain-17 Date: Sat, 11 Jan 2025 20:42:41 +0530 Subject: [PATCH] adminauth --- app/dashboard/page.tsx | 9 ++ app/db/index.ts | 3 +- app/firebase.config.ts | 1 + app/globals.css | 21 ---- app/layout.tsx | 30 ++--- app/login/page.tsx | 29 +++++ app/page.tsx | 101 ---------------- lib/withAdmin.tsx | 57 ++++++++++ package-lock.json | 196 +++++++++++++++++++++++++++++++- package.json | 5 +- pages/api/auth/[...nextauth].ts | 12 ++ 11 files changed, 316 insertions(+), 148 deletions(-) create mode 100644 app/dashboard/page.tsx create mode 100644 app/login/page.tsx delete mode 100644 app/page.tsx create mode 100644 lib/withAdmin.tsx create mode 100644 pages/api/auth/[...nextauth].ts diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx new file mode 100644 index 0000000..6dd1253 --- /dev/null +++ b/app/dashboard/page.tsx @@ -0,0 +1,9 @@ +"use client"; + +import withAdmin from "@/lib/withAdmin"; + +function DashboardPage() { + return

Welcome to the admin dashboard!

; +} + +export default withAdmin(DashboardPage); diff --git a/app/db/index.ts b/app/db/index.ts index 23c29c7..b469c1e 100644 --- a/app/db/index.ts +++ b/app/db/index.ts @@ -1,7 +1,8 @@ import { getFirestore } from "firebase/firestore"; +import { getAuth } from "firebase/auth"; import fireapp from "@/app/firebase.config"; // Get Firestore instance const db = getFirestore(fireapp); - +export const auth = getAuth(fireapp); export { db }; diff --git a/app/firebase.config.ts b/app/firebase.config.ts index 08ebff2..f0153ea 100644 --- a/app/firebase.config.ts +++ b/app/firebase.config.ts @@ -13,3 +13,4 @@ const firebaseConfig = { const fireapp = initializeApp(firebaseConfig); export default fireapp; + \ No newline at end of file diff --git a/app/globals.css b/app/globals.css index 6b717ad..e69de29 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,21 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -:root { - --background: #ffffff; - --foreground: #171717; -} - -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - -body { - color: var(--foreground); - background: var(--background); - font-family: Arial, Helvetica, sans-serif; -} diff --git a/app/layout.tsx b/app/layout.tsx index f7fa87e..0f6ec39 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,33 +1,17 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; +"use client"; +import React from 'react' +import { SessionProvider } from "next-auth/react"; import "./globals.css"; -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); - -export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", -}; - export default function RootLayout({ children, -}: Readonly<{ +}: { children: React.ReactNode; -}>) { +}) { return ( - - {children} + + {children} ); diff --git a/app/login/page.tsx b/app/login/page.tsx new file mode 100644 index 0000000..2d1d3c0 --- /dev/null +++ b/app/login/page.tsx @@ -0,0 +1,29 @@ +"use client"; + +import { signIn, signOut, useSession } from "next-auth/react"; +import { useRouter } from "next/navigation"; + +export default function LoginPage() { + const { data: session, status } = useSession(); + console.log(session); + console.log(status); + const router = useRouter(); + + if (status === "authenticated") { + router.push("/dashboard"); + return null; + } + + return ( +
+ {status === "loading" ? ( +

Loading...=

+ ) : ( + <> +

Please log in:

+ + + )} +
+ ); +} diff --git a/app/page.tsx b/app/page.tsx deleted file mode 100644 index 9007252..0000000 --- a/app/page.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import Image from "next/image"; - -export default function Home() { - return ( -
-
- Next.js logo -
    -
  1. - Get started by editing{" "} - - app/page.tsx - - . -
  2. -
  3. Save and see your changes instantly.
  4. -
- -
- - Vercel logomark - Deploy now - - - Read our docs - -
-
- -
- ); -} diff --git a/lib/withAdmin.tsx b/lib/withAdmin.tsx new file mode 100644 index 0000000..d6f786c --- /dev/null +++ b/lib/withAdmin.tsx @@ -0,0 +1,57 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { db } from "@/app/db"; +import { collection, query, where, doc, getDoc } from "firebase/firestore"; +import { useSession } from "next-auth/react"; +import { useRouter } from "next/navigation"; + +export default function withAdmin(Component: React.FC) { + + return function AdminProtectedPage(props: any) { + const { data: session, status } = useSession(); + const [isAdmin, setIsAdmin] = useState(false); + const [loading, setLoading] = useState(true); + const router = useRouter(); + + useEffect(() => { + const checkAdmin = async () => { + if (status === "authenticated" && session!==null) { + const userEmail = session.user?.email; + + if (userEmail) { + try { + const adminQuery =doc(db,"admin",userEmail); + + const querySnapshot = await getDoc(adminQuery); + if (querySnapshot.exists()) { + setIsAdmin(true); + } else { + console.log(querySnapshot); + alert("You do not have admin access."); + router.push("/login"); + } + } catch (error) { + console.error("Error checking admin access:", error); + router.push("/login"); + } + } else { + router.push("/login"); + } + } else if (status === "unauthenticated") { + router.push("/login"); + } + + setLoading(false); + }; + + checkAdmin(); + }, [session, status, router]); + + if (loading) { + return

Loading...

; + } + + return isAdmin ? : null; + }; +} diff --git a/package-lock.json b/package-lock.json index 4464452..ac9aa14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,14 @@ "name": "admin-panel", "version": "0.1.0", "dependencies": { + "@types/next-auth": "^3.13.0", "axios": "^1.7.9", "firebase": "^11.1.0", "next": "15.1.3", + "next-auth": "^4.24.11", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "react-firebase-hooks": "^5.1.1" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -39,6 +42,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@emnapi/runtime": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", @@ -1503,6 +1518,15 @@ "node": ">=12.4.0" } }, + "node_modules/@panva/hkdf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1628,6 +1652,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/next-auth": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@types/next-auth/-/next-auth-3.15.0.tgz", + "integrity": "sha512-ZVfejlu81YiIRX1m0iKAfvZ3nK7K9EyZWhNARNKsFop8kNAgEvMnlKpTpwN59xkK2OhyWLagPuiDAVBYSO9jSA==", + "deprecated": "This is a stub types definition. next-auth provides its own type definitions, so you do not need this installed.", + "license": "MIT", + "dependencies": { + "next-auth": "*" + } + }, "node_modules/@types/node": { "version": "20.17.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.11.tgz", @@ -2573,6 +2607,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -4610,6 +4653,15 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4985,6 +5037,38 @@ } } }, + "node_modules/next-auth": { + "version": "4.24.11", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.11.tgz", + "integrity": "sha512-pCFXzIDQX7xmHFs4KVH4luCjaCbuPRtZ9oBUjUhOk84mZ9WVPf94n87TxYI4rSRf9HmfHEF8Yep3JrYDVOo3Cw==", + "license": "ISC", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@panva/hkdf": "^1.0.2", + "cookie": "^0.7.0", + "jose": "^4.15.5", + "oauth": "^0.9.15", + "openid-client": "^5.4.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "@auth/core": "0.34.2", + "next": "^12.2.5 || ^13 || ^14 || ^15", + "nodemailer": "^6.6.5", + "react": "^17.0.2 || ^18 || ^19", + "react-dom": "^17.0.2 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@auth/core": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -5023,6 +5107,12 @@ "node": ">=0.10.0" } }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==", + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5155,6 +5245,51 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/oidc-token-hash": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz", + "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || >=12.0.0" + } + }, + "node_modules/openid-client": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", + "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", + "license": "MIT", + "dependencies": { + "jose": "^4.15.9", + "lru-cache": "^6.0.0", + "object-hash": "^2.2.0", + "oidc-token-hash": "^5.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/openid-client/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/openid-client/node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -5486,6 +5621,28 @@ "dev": true, "license": "MIT" }, + "node_modules/preact": { + "version": "10.25.4", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.25.4.tgz", + "integrity": "sha512-jLdZDb+Q+odkHJ+MpW/9U5cODzqnB+fy2EiHSZES7ldV5LK7yjlVzTp7R8Xy6W6y75kfK8iWYtFVH7lvjwrCMA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "license": "MIT", + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5496,6 +5653,12 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==", + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5590,6 +5753,16 @@ "react": "^19.0.0" } }, + "node_modules/react-firebase-hooks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/react-firebase-hooks/-/react-firebase-hooks-5.1.1.tgz", + "integrity": "sha512-y2UpWs82xs+39q5Rc/wq316ca52QsC0n8m801V+yM4IC4hbfOL4yQPVSh7w+ydstdvjN9F+lvs1WrO2VYxpmdA==", + "license": "Apache-2.0", + "peerDependencies": { + "firebase": ">= 9.0.0", + "react": ">= 16.8.0" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -5643,6 +5816,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -6690,6 +6869,15 @@ "dev": true, "license": "MIT" }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", @@ -6931,6 +7119,12 @@ "node": ">=10" } }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/yaml": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", diff --git a/package.json b/package.json index c915cad..ccb4ede 100644 --- a/package.json +++ b/package.json @@ -9,11 +9,14 @@ "lint": "next lint" }, "dependencies": { + "@types/next-auth": "^3.13.0", "axios": "^1.7.9", "firebase": "^11.1.0", "next": "15.1.3", + "next-auth": "^4.24.11", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "react-firebase-hooks": "^5.1.1" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/pages/api/auth/[...nextauth].ts b/pages/api/auth/[...nextauth].ts new file mode 100644 index 0000000..1567b43 --- /dev/null +++ b/pages/api/auth/[...nextauth].ts @@ -0,0 +1,12 @@ +import NextAuth from "next-auth"; +import GoogleProvider from "next-auth/providers/google"; + +export default NextAuth({ + providers: [ + GoogleProvider({ + clientId: process.env.GOOGLE_CLIENT_ID!, + clientSecret: process.env.GOOGLE_CLIENT_SECRET!, + }), + ], + secret: process.env.NEXTAUTH_SECRET, +});