From 9a9110e58c6456c94b3efba5f0429fd4ceee73cc Mon Sep 17 00:00:00 2001 From: michivonah Date: Sun, 27 Apr 2025 18:31:17 +0200 Subject: [PATCH] add next auth & login with github --- package-lock.json | 117 ++++++++++++++++++++++ package.json | 2 + src/app/api/auth/callback/github/route.ts | 2 + src/app/login/page.tsx | 12 +++ src/app/page.tsx | 7 +- src/auth.ts | 9 ++ src/components/app-sidebar.tsx | 28 ++---- src/components/login/login-form.tsx | 41 ++++++++ src/components/login/signout-button.tsx | 18 ++++ src/middleware.ts | 12 +++ 10 files changed, 226 insertions(+), 22 deletions(-) create mode 100644 src/app/api/auth/callback/github/route.ts create mode 100644 src/app/login/page.tsx create mode 100644 src/auth.ts create mode 100644 src/components/login/login-form.tsx create mode 100644 src/components/login/signout-button.tsx create mode 100644 src/middleware.ts diff --git a/package-lock.json b/package-lock.json index 9238a7f..060cb30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,9 +16,11 @@ "clsx": "^2.1.1", "lucide-react": "^0.503.0", "next": "15.3.1", + "next-auth": "^5.0.0-beta.27", "next-themes": "^0.4.6", "react": "^19.0.0", "react-dom": "^19.0.0", + "simple-icons": "^14.12.3", "tailwind-merge": "^3.2.0" }, "devDependencies": { @@ -47,6 +49,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@auth/core": { + "version": "0.39.0", + "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.39.0.tgz", + "integrity": "sha512-jusviw/sUSfAh6S/wjY5tRmJOq0Itd3ImF+c/b4HB9DfmfChtcfVJTNJeqCeExeCG8oh4PBKRsMQJsn2W6NhFQ==", + "license": "ISC", + "dependencies": { + "@panva/hkdf": "^1.2.1", + "jose": "^6.0.6", + "oauth4webapi": "^3.3.0", + "preact": "10.24.3", + "preact-render-to-string": "6.5.11" + }, + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "nodemailer": "^6.8.0" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, "node_modules/@emnapi/core": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", @@ -904,6 +935,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/@radix-ui/primitive": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", @@ -4498,6 +4538,15 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.0.10.tgz", + "integrity": "sha512-skIAxZqcMkOrSwjJvplIPYrlXGpxTPnro2/QWTDCxAdWQrSTV5/KqspMWmi5WAx5+ULswASJiZ0a+1B/Lxt9cw==", + "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", @@ -5055,6 +5104,33 @@ } } }, + "node_modules/next-auth": { + "version": "5.0.0-beta.27", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.27.tgz", + "integrity": "sha512-/QtP9C0C99YpEuBEJqMaDXH3ISWMgObQalwVZEoC7sskaIPhv5fQl6fXS/rXJQKqLY6MNJ42rqjqmRdoXZH2EQ==", + "license": "ISC", + "dependencies": { + "@auth/core": "0.39.0" + }, + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "next": "^14.0.0-0 || ^15.0.0-0", + "nodemailer": "^6.6.5", + "react": "^18.2.0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, "node_modules/next-themes": { "version": "0.4.6", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", @@ -5093,6 +5169,15 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/oauth4webapi": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.5.0.tgz", + "integrity": "sha512-DF3mLWNuxPkxJkHmWxbSFz4aE5CjWOsm465VBfBdWzmzX4Mg3vF8icxK+iKqfdWrIumBJ2TaoNQWx+SQc2bsPQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5382,6 +5467,25 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/preact": { + "version": "10.24.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", + "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "6.5.11", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.5.11.tgz", + "integrity": "sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==", + "license": "MIT", + "peerDependencies": { + "preact": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5915,6 +6019,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/simple-icons": { + "version": "14.12.3", + "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-14.12.3.tgz", + "integrity": "sha512-P85Pqak4picfTzdujmGL7+pFfsd32K/tDjHPQ8vvJcr2Xk380A9kBVIW5QH86qWqa+YJgFa5huLcUuwmW+YBPQ==", + "license": "CC0-1.0", + "engines": { + "node": ">=0.12.18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/simple-icons" + } + }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", diff --git a/package.json b/package.json index 30b4bbb..9a22924 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,11 @@ "clsx": "^2.1.1", "lucide-react": "^0.503.0", "next": "15.3.1", + "next-auth": "^5.0.0-beta.27", "next-themes": "^0.4.6", "react": "^19.0.0", "react-dom": "^19.0.0", + "simple-icons": "^14.12.3", "tailwind-merge": "^3.2.0" }, "devDependencies": { diff --git a/src/app/api/auth/callback/github/route.ts b/src/app/api/auth/callback/github/route.ts new file mode 100644 index 0000000..0cf1408 --- /dev/null +++ b/src/app/api/auth/callback/github/route.ts @@ -0,0 +1,2 @@ +import { handlers } from "@/auth" // Referring to the auth.ts we just created +export const { GET, POST } = handlers \ No newline at end of file diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx new file mode 100644 index 0000000..6d70784 --- /dev/null +++ b/src/app/login/page.tsx @@ -0,0 +1,12 @@ +import LoginForm from "@/components/login/login-form"; +import { Suspense } from "react"; + +export default function LoginPage(){ + return ( +
+ + + +
+ ); +} \ No newline at end of file diff --git a/src/app/page.tsx b/src/app/page.tsx index 6efb563..28629a9 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,5 +1,6 @@ import { Button } from "@/components/ui/button"; import { Rss } from "lucide-react"; +import Link from "next/link"; export default function Page() { return ( @@ -12,7 +13,11 @@ export default function Page() {
- + + +
); diff --git a/src/auth.ts b/src/auth.ts new file mode 100644 index 0000000..7699b19 --- /dev/null +++ b/src/auth.ts @@ -0,0 +1,9 @@ +import NextAuth from "next-auth"; +import GitHub from "next-auth/providers/github"; + +export const { handlers, signIn, signOut, auth } = NextAuth({ + providers: [GitHub], + pages: { + signIn: "/login", + } +}) \ No newline at end of file diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx index db4376a..52f5037 100644 --- a/src/components/app-sidebar.tsx +++ b/src/components/app-sidebar.tsx @@ -1,5 +1,6 @@ -import { Home, Rss, LogOut } from "lucide-react"; +import { Home, Rss } from "lucide-react"; import Link from 'next/link'; +import { SignOut } from "./login/signout-button"; import { Sidebar, @@ -28,13 +29,6 @@ const items = { icon: Rss, }, ], - footer: [ - { - title: "Log out", - url: "#", - icon: LogOut, - }, - ] }; export function AppSidebar() { @@ -74,19 +68,11 @@ export function AppSidebar() { - {items.footer.map((item) => ( - - - - - {item.title} - - - - ))} + + + + + diff --git a/src/components/login/login-form.tsx b/src/components/login/login-form.tsx new file mode 100644 index 0000000..4766aa8 --- /dev/null +++ b/src/components/login/login-form.tsx @@ -0,0 +1,41 @@ +import { signIn } from "@/auth"; +import { Button } from '@/components/ui/button'; +import { + Card, + CardHeader, + CardTitle, + CardDescription, + CardContent, + CardFooter +} from '@/components/ui/card'; + +export default function LoginForm(){ + return ( +
+ + + Nice to meet you! + Sign in below with your GitHub account. + + +
{ + "use server"; + await signIn("github", { redirectTo: "/home" }); + }}> + +
+
+
+ +

+ By clicking continue, you agree to our Terms of Service and Privacy Policy. +

+
+ ); +} \ No newline at end of file diff --git a/src/components/login/signout-button.tsx b/src/components/login/signout-button.tsx new file mode 100644 index 0000000..1635b50 --- /dev/null +++ b/src/components/login/signout-button.tsx @@ -0,0 +1,18 @@ +import { signOut } from "@/auth"; +import { LogOut } from "lucide-react"; + +export function SignOut() { + return ( +
{ + "use server"; + await signOut(); + }} + > + +
+ ) +} \ No newline at end of file diff --git a/src/middleware.ts b/src/middleware.ts new file mode 100644 index 0000000..33e02b6 --- /dev/null +++ b/src/middleware.ts @@ -0,0 +1,12 @@ +import { auth } from "@/auth" + +export default auth((req) => { + if (!req.auth && req.nextUrl.pathname !== "/login") { + const newUrl = new URL("/login", req.nextUrl.origin) + return Response.redirect(newUrl) + } +}) + +export const config = { + matcher: ["/((?!api|_next/static|_next/image|favicon.ico|$).*)"], +} \ No newline at end of file