-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from AmrMustafa282/feat/add-o-auth
add Authentication useing auth hook
- Loading branch information
Showing
19 changed files
with
7,347 additions
and
8,025 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
BACKEND_URL="process.env.BACKEND_URL" | ||
|
||
GOOGLE_CLIENT_ID="" | ||
GOOGLE_CLIENT_SECRET="" | ||
NEXTAUTH_SECRET="" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,20 @@ | ||
"use client"; | ||
import { useEffect, useState } from "react"; | ||
import useUserStore from "@/zustand/userStore"; | ||
import { useEffect } from "react"; | ||
import { useSession } from "next-auth/react"; | ||
import { useRouter } from "next/navigation"; | ||
import useCheckUserExpiration from "@/hooks/useCheckUserExpiration"; | ||
|
||
const ProtectedLayout = ({ children }: { children: React.ReactNode }) => { | ||
const router = useRouter(); | ||
const { user } = useUserStore(); | ||
const [isMounted, setIsMounted] = useState(false); | ||
console.log(user); | ||
useCheckUserExpiration(); | ||
// Ensure we only access persisted state after mount | ||
useEffect(() => { | ||
setIsMounted(true); | ||
}, []); | ||
export default function MePage() { | ||
const { data: session, status } = useSession(); | ||
const router = useRouter(); | ||
|
||
useEffect(() => { | ||
if (isMounted && !user) { | ||
router.push("/login"); | ||
} | ||
}, [isMounted, user, router]); | ||
useEffect(() => { | ||
if (status === "unauthenticated") { | ||
router.push("/login"); | ||
} | ||
}, [status, router]); | ||
|
||
if (!isMounted) return null; // Prevent hydration mismatch | ||
if (status === "loading") return <p>Loading...</p>; | ||
if (!session) return null; | ||
|
||
return <>{children}</>; | ||
}; | ||
|
||
export default ProtectedLayout; | ||
return <h1>Welcome, {session.user?.name}!</h1>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import NextAuth from "next-auth"; | ||
import axios from "axios"; | ||
import type { NextAuthOptions } from "next-auth"; | ||
import Credentials from "next-auth/providers/credentials"; | ||
import Google from "next-auth/providers/google"; | ||
import GitHub from "next-auth/providers/github"; // Add GitHub provider | ||
|
||
export const authConfig = { | ||
providers: [ | ||
Credentials({ | ||
name: "Credentials", | ||
credentials: { | ||
username: { label: "Email", type: "text" }, | ||
password: { label: "Password", type: "password" }, | ||
}, | ||
async authorize(credentials) { | ||
const res = await axios.post(`${process.env.BACKEND_URL}/login`, { | ||
username: credentials?.username, | ||
password: credentials?.password, | ||
}); | ||
|
||
const user = res.data.data; | ||
|
||
if (res.status && user) { | ||
return user; // Return user object if authentication is successful | ||
} else { | ||
return null; // Return null if authentication fails | ||
} | ||
}, | ||
}), | ||
|
||
// Google Authentication | ||
Google({ | ||
clientId: process.env.GOOGLE_CLIENT_ID!, | ||
clientSecret: process.env.GOOGLE_CLIENT_SECRET!, | ||
}), | ||
|
||
// GitHub Authentication | ||
GitHub({ | ||
clientId: process.env.GITHUB_CLIENT_ID!, | ||
clientSecret: process.env.GITHUB_CLIENT_SECRET!, | ||
}), | ||
], | ||
|
||
// Customize session and JWT behavior | ||
session: { | ||
strategy: "jwt", | ||
}, | ||
|
||
callbacks: { | ||
// Handle JWT token creation | ||
async jwt({ token, user }) { | ||
if (user) { | ||
token.id = user.id; | ||
token.email = user.email; | ||
} | ||
return token; | ||
}, | ||
|
||
// Handle session creation | ||
async session({ session, token }: { session: any; token: any }) { | ||
session.user.id = token.id; | ||
session.user.email = token.email; | ||
return session; | ||
}, | ||
|
||
// Handle sign-in logic | ||
async signIn({ user, account, profile }) { | ||
if (account?.provider === "google") { | ||
// Call your backend API to find or register the user | ||
try { | ||
const res = await axios.post(`${process.env.BACKEND_URL}/api/v1/oauth`, { | ||
email: profile?.email, | ||
name: profile?.name, | ||
googleId: profile?.sub, | ||
}); | ||
|
||
if (res.data.user) { | ||
user.id = res.data.user.id; | ||
user.email = res.data.user.email; | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} catch (error) { | ||
console.error("Error during Google sign-in:", error); | ||
return false; | ||
} | ||
} | ||
|
||
if (account?.provider === "github") { | ||
// Call your backend API to find or register the user | ||
try { | ||
const res = await axios.post(`${process.env.BACKEND_URL}/api/v1/oauth`, { | ||
email: profile?.email, | ||
name: profile?.name, | ||
githubId: profile?.sub, | ||
}); | ||
|
||
if (res.data.user) { | ||
user.id = res.data.user.id; | ||
user.email = res.data.user.email; | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} catch (error) { | ||
console.error("Error during GitHub sign-in:", error); | ||
return false; | ||
} | ||
} | ||
|
||
// For credentials provider, proceed as usual | ||
return true; | ||
}, | ||
}, | ||
|
||
// Custom pages (optional) | ||
pages: { | ||
signIn: "/auth/signin", // Custom sign-in page | ||
}, | ||
} satisfies NextAuthOptions; | ||
|
||
const handler = NextAuth(authConfig); | ||
export { handler as GET, handler as POST }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
export async function authenticateGoogle(token: string) { | ||
const res = await fetch(`${process.env.BACKEND_URL}/api/auth/google`, { | ||
method: "POST", | ||
headers: { "Content-Type": "application/json" }, | ||
body: JSON.stringify({ token }), | ||
}); | ||
|
||
if (!res.ok) { | ||
throw new Error("Google authentication failed"); | ||
} | ||
|
||
return res.json(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,3 +17,5 @@ export default function LoginPage() { | |
</div> | ||
); | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,47 @@ | ||
"use client"; | ||
import { useEffect, useState } from "react"; | ||
import { useEffect } from "react"; | ||
import useUserStore from "../zustand/userStore"; | ||
import { Button } from "./ui/button"; | ||
import { useRouter } from "next/navigation"; | ||
import useAuthenticate from "../hooks/useAuthenticate"; | ||
|
||
export default function Header() { | ||
const [isMounted, setIsMounted] = useState(false); | ||
const { user, clearUser } = useUserStore(); | ||
const router = useRouter(); | ||
useEffect(() => { | ||
setIsMounted(true); | ||
}, [user]); | ||
const { session, status, isSessionExpired, signOut } = useAuthenticate(); | ||
const { user, clearUser } = useUserStore(); | ||
const router = useRouter(); | ||
|
||
if (!isMounted) { | ||
return null; | ||
} | ||
return ( | ||
<header className="bg-white py-4 "> | ||
<nav className="flex justify-between items-center container mx-auto "> | ||
<div>Welcome, {user ? user.name : "Guest"}</div> | ||
useEffect(() => { | ||
if (isSessionExpired) { | ||
clearUser(); | ||
} | ||
}, [isSessionExpired, clearUser]); | ||
|
||
{user ? ( | ||
<Button onClick={clearUser}>Logout</Button> | ||
) : ( | ||
<div className="flex gap-1"> | ||
<Button variant={"ghost"} size={"lg"} onClick={() => router.push("/signup")}> | ||
SignUp | ||
</Button> | ||
<Button size={"lg"} onClick={() => router.push("/login")}>Login</Button> | ||
</div> | ||
)} | ||
</nav> | ||
</header> | ||
); | ||
if (status === "loading") { | ||
return null; | ||
} | ||
|
||
return ( | ||
<header className="bg-white py-4"> | ||
<nav className="flex justify-between items-center container mx-auto"> | ||
<div>Welcome, {session?.user?.name || "Guest"}</div> | ||
|
||
{session?.user ? ( | ||
<Button onClick={() => signOut()}>Logout</Button> | ||
) : ( | ||
<div className="flex gap-1"> | ||
<Button | ||
variant="ghost" | ||
size="lg" | ||
onClick={() => router.push("/signup")} | ||
> | ||
SignUp | ||
</Button> | ||
<Button size="lg" onClick={() => router.push("/login")}> | ||
Login | ||
</Button> | ||
</div> | ||
)} | ||
</nav> | ||
</header> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
"use client"; | ||
|
||
import { SessionProvider } from "next-auth/react"; | ||
|
||
export default function AuthProvider({ | ||
children, | ||
}: { | ||
children: React.ReactNode; | ||
}) { | ||
return <SessionProvider>{children}</SessionProvider>; | ||
} |
Oops, something went wrong.