auth stuff (prototype)

This commit is contained in:
Lea 2024-01-16 21:38:41 +01:00
parent 13426f34eb
commit 2bf719d1e3
Signed by: Lea
GPG key ID: 1BAFFE8347019C42
8 changed files with 192 additions and 7 deletions

1
.env Normal file
View file

@ -0,0 +1 @@
NEXTAUTH_SECRET=changeme

View file

@ -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"
},

View file

@ -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==}

View file

@ -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 };

View file

@ -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 (
<html lang="en">
<body className={`${inter.className} p-4`}>
<ThemeWrapper>
{children}
</ThemeWrapper>
<AuthWrapper session={await getServerSession()}>
<ThemeWrapper>
{children}
</ThemeWrapper>
</AuthWrapper>
</body>
</html>
);

View file

@ -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 (
<>
<Heading size="9">Welcome back.</Heading>
{session?.user && (
<Card className='w-fit'>
<Flex gap="3" align="center">
<Avatar
size="3"
src={session.user.image ?? "/favicon.ico"}
radius='full'
fallback={session.user.name?.slice(0, 1) || "@"}
/>
<Box>
<Text as="div" size="2" weight="bold">
{session.user.email}
</Text>
</Box>
</Flex>
</Card>
)}
<Link href={session?.user ? "/api/auth/signout" : "/api/auth/signin"}>
<Button variant='soft'>
Sign {session?.user ? "out" : "in"}
</Button>
</Link>
</>
);
}

View file

@ -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 (
<SessionProvider session={session}>
{children}
</SessionProvider>
);
}

7
src/lib/util.ts Normal file
View file

@ -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');
}