From 2bf719d1e3e1f81b7764b1c637d027f67146a69b Mon Sep 17 00:00:00 2001 From: Lea Date: Tue, 16 Jan 2024 21:38:41 +0100 Subject: [PATCH] auth stuff (prototype) --- .env | 1 + package.json | 1 + pnpm-lock.yaml | 88 ++++++++++++++++++++++++- src/app/api/auth/[...nextauth]/route.ts | 42 ++++++++++++ src/app/layout.tsx | 12 ++-- src/app/page.tsx | 30 ++++++++- src/lib/components/AuthWrapper.tsx | 18 +++++ src/lib/util.ts | 7 ++ 8 files changed, 192 insertions(+), 7 deletions(-) create mode 100644 .env create mode 100644 src/app/api/auth/[...nextauth]/route.ts create mode 100644 src/lib/components/AuthWrapper.tsx create mode 100644 src/lib/util.ts diff --git a/.env b/.env new file mode 100644 index 0000000..96a7835 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +NEXTAUTH_SECRET=changeme \ No newline at end of file diff --git a/package.json b/package.json index 3c783e7..bd6fc6b 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@radix-ui/themes": "^2.0.3", "next": "14.0.4", + "next-auth": "^4.24.5", "react": "^18", "react-dom": "^18" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b79ad8d..9080164 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ dependencies: next: specifier: 14.0.4 version: 14.0.4(react-dom@18.2.0)(react@18.2.0) + next-auth: + specifier: ^4.24.5 + version: 4.24.5(next@14.0.4)(react-dom@18.2.0)(react@18.2.0) react: specifier: ^18 version: 18.2.0 @@ -304,6 +307,10 @@ packages: fastq: 1.16.0 dev: true + /@panva/hkdf@1.1.1: + resolution: {integrity: sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==} + dev: false + /@pkgjs/parseargs@0.11.0: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -1821,6 +1828,11 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true + /cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: false + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -2835,6 +2847,10 @@ packages: hasBin: true dev: true + /jose@4.15.4: + resolution: {integrity: sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==} + dev: false + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2940,7 +2956,6 @@ packages: engines: {node: '>=10'} dependencies: yallist: 4.0.0 - dev: true /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} @@ -3002,6 +3017,31 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true + /next-auth@4.24.5(next@14.0.4)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-3RafV3XbfIKk6rF6GlLE4/KxjTcuMCifqrmD+98ejFq73SRoj2rmzoca8u764977lH/Q7jo6Xu6yM+Re1Mz/Og==} + peerDependencies: + next: ^12.2.5 || ^13 || ^14 + nodemailer: ^6.6.5 + react: ^17.0.2 || ^18 + react-dom: ^17.0.2 || ^18 + peerDependenciesMeta: + nodemailer: + optional: true + dependencies: + '@babel/runtime': 7.23.8 + '@panva/hkdf': 1.1.1 + cookie: 0.5.0 + jose: 4.15.4 + next: 14.0.4(react-dom@18.2.0)(react@18.2.0) + oauth: 0.9.15 + openid-client: 5.6.4 + preact: 10.19.3 + preact-render-to-string: 5.2.6(preact@10.19.3) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + uuid: 8.3.2 + dev: false + /next@14.0.4(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==} engines: {node: '>=18.17.0'} @@ -3056,11 +3096,20 @@ packages: engines: {node: '>=0.10.0'} dev: true + /oauth@0.9.15: + resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==} + dev: false + /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} dev: true + /object-hash@2.2.0: + resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} + engines: {node: '>= 6'} + dev: false + /object-hash@3.0.0: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} @@ -3128,12 +3177,26 @@ packages: es-abstract: 1.22.3 dev: true + /oidc-token-hash@5.0.3: + resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==} + engines: {node: ^10.13.0 || >=12.0.0} + dev: false + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 dev: true + /openid-client@5.6.4: + resolution: {integrity: sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA==} + dependencies: + jose: 4.15.4 + lru-cache: 6.0.0 + object-hash: 2.2.0 + oidc-token-hash: 5.0.3 + dev: false + /optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -3296,11 +3359,28 @@ packages: source-map-js: 1.0.2 dev: true + /preact-render-to-string@5.2.6(preact@10.19.3): + resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==} + peerDependencies: + preact: '>=10' + dependencies: + preact: 10.19.3 + pretty-format: 3.8.0 + dev: false + + /preact@10.19.3: + resolution: {integrity: sha512-nHHTeFVBTHRGxJXKkKu5hT8C/YWBkPso4/Gad6xuj5dbptt9iF9NZr9pHbPhBrnT2klheu7mHTxTZ/LjwJiEiQ==} + dev: false + /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} dev: true + /pretty-format@3.8.0: + resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + dev: false + /prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} dependencies: @@ -3901,6 +3981,11 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true + /uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + dev: false + /watchpack@2.4.0: resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} engines: {node: '>=10.13.0'} @@ -3989,7 +4074,6 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true /yaml@2.3.4: resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==} diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..430fc97 --- /dev/null +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,42 @@ +import NextAuth from "next-auth"; +import CredentialProvider from "next-auth/providers/credentials"; +import { sha256sum } from "@/lib/util"; + +export const authOptions = { + providers: [ + CredentialProvider({ + name: 'Mail account', + credentials: { + email: { label: "E-Mail", type: "email", placeholder: "webmistress@amogus.cloud" }, + password: { label: "Password", type: "password" }, + }, + async authorize(credentials, req) { + console.log(`Authentication attempt for ${credentials?.email}`); + + // todo + if (credentials?.email == "balls@fortnite.org" && credentials.password == "ballsack obliteration") { + console.log(`[${credentials.email}] Authentication succeeded`); + const emailHash = sha256sum(credentials.email.trim().toLowerCase()); + + // todo fetch name from gravatar (why not) + //const res = await fetch(`https://gravatar.com/${emailHash}`).catch(() => null); + //const profile = await res?.json().catch(() => null); + + + return { + id: credentials.email, + email: credentials.email, + image: `https://gravatar.com/avatar/${emailHash}?d=identicon`, + }; + } + + console.log(`[${credentials?.email}] Authentication failed`); + return null; + }, + }), + ], +}; + +const handler = NextAuth(authOptions); + +export { handler as GET, handler as POST }; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 4118291..ab283c8 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -2,6 +2,8 @@ import type { Metadata } from 'next'; import { Inter } from 'next/font/google'; import ThemeWrapper from '@/lib/components/ThemeWrapper'; import './globals.css'; +import AuthWrapper from '@/lib/components/AuthWrapper'; +import { getServerSession } from 'next-auth'; const inter = Inter({ subsets: ['latin'] }); @@ -10,7 +12,7 @@ export const metadata: Metadata = { description: 'Generated by create next app', }; -export default function RootLayout({ +export default async function RootLayout({ children, }: { children: React.ReactNode @@ -18,9 +20,11 @@ export default function RootLayout({ return ( - - {children} - + + + {children} + + ); diff --git a/src/app/page.tsx b/src/app/page.tsx index 8f5dd1c..23024ff 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,11 +1,39 @@ "use client"; -import { Heading } from '@radix-ui/themes'; +import { Avatar, Card, Flex, Heading, Text, Box, Button } from '@radix-ui/themes'; +import { useSession } from 'next-auth/react'; +import Link from 'next/link'; export default function Home() { + const session = useSession().data; + return ( <> Welcome back. + + {session?.user && ( + + + + + + {session.user.email} + + + + + )} + + + + ); } diff --git a/src/lib/components/AuthWrapper.tsx b/src/lib/components/AuthWrapper.tsx new file mode 100644 index 0000000..279459e --- /dev/null +++ b/src/lib/components/AuthWrapper.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { Session } from "next-auth"; +import { SessionProvider } from "next-auth/react"; + +export default function AuthWrapper({ + children, + session, +}: { + children: React.ReactNode, + session: Session | null, +}) { + return ( + + {children} + + ); +} \ No newline at end of file diff --git a/src/lib/util.ts b/src/lib/util.ts new file mode 100644 index 0000000..a8898a7 --- /dev/null +++ b/src/lib/util.ts @@ -0,0 +1,7 @@ +import crypto from 'node:crypto'; + +export function sha256sum(input: any) { + const hash = crypto.createHash('sha256'); + hash.update(input); + return hash.digest('hex'); +} \ No newline at end of file