From fc87a576a9bac7d5e7155d2fe24a2496a54c33f3 Mon Sep 17 00:00:00 2001 From: Dino Danic Date: Sat, 2 Mar 2024 22:35:14 +0100 Subject: [PATCH] add authorization --- .../bible/app/bible/components/bible-side.tsx | 6 +- apps/bible/app/bible/system/auth/layout.tsx | 9 +++ apps/bible/app/bible/system/auth/loading.tsx | 3 + apps/bible/app/bible/system/auth/page.tsx | 18 ++++++ apps/bible/app/layout.tsx | 2 +- apps/bible/lib/utils.ts | 1 - apps/bible/middleware.ts | 6 ++ apps/bible/module/auth/components/login.tsx | 33 ++++++++++ .../module/auth/components/user-card.tsx | 15 +++++ apps/bible/module/auth/lib.ts | 63 +++++++++++++++++++ apps/bible/module/auth/types.ts | 11 ++++ apps/bible/package.json | 1 + apps/bible/site/bible-contents.ts | 4 ++ apps/bible/site/routes.ts | 3 + 14 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 apps/bible/app/bible/system/auth/layout.tsx create mode 100644 apps/bible/app/bible/system/auth/loading.tsx create mode 100644 apps/bible/app/bible/system/auth/page.tsx create mode 100644 apps/bible/middleware.ts create mode 100644 apps/bible/module/auth/components/login.tsx create mode 100644 apps/bible/module/auth/components/user-card.tsx create mode 100644 apps/bible/module/auth/lib.ts create mode 100644 apps/bible/module/auth/types.ts diff --git a/apps/bible/app/bible/components/bible-side.tsx b/apps/bible/app/bible/components/bible-side.tsx index a495618..a9e38ea 100644 --- a/apps/bible/app/bible/components/bible-side.tsx +++ b/apps/bible/app/bible/components/bible-side.tsx @@ -1,12 +1,16 @@ import { Stack } from '@/components/primitives/stack' import { Text } from '@/components/typography/text' import { BibleContent, bibleContents } from '@/site/bible-contents' -import { FC } from 'react' +import { FC, Suspense } from 'react' import { BibleChild } from './bible-child' +import { UserCard } from '@/module/auth/components/user-card' export const BibleSide = () => { return ( + loading user card..}> + + {bibleContents.map((bibleContent) => ( ))} diff --git a/apps/bible/app/bible/system/auth/layout.tsx b/apps/bible/app/bible/system/auth/layout.tsx new file mode 100644 index 0000000..d824159 --- /dev/null +++ b/apps/bible/app/bible/system/auth/layout.tsx @@ -0,0 +1,9 @@ +import { LoginForm } from '@/module/auth/components/login' +import { getSession } from '@/module/auth/lib' +import { PropsWithChildren } from 'react' + +export default async function Layout({ children }: PropsWithChildren) { + const session = await getSession() + if (!session) return + return <>{children} +} diff --git a/apps/bible/app/bible/system/auth/loading.tsx b/apps/bible/app/bible/system/auth/loading.tsx new file mode 100644 index 0000000..a26fc8b --- /dev/null +++ b/apps/bible/app/bible/system/auth/loading.tsx @@ -0,0 +1,3 @@ +export default function Loading() { + return
Loading user..
+} diff --git a/apps/bible/app/bible/system/auth/page.tsx b/apps/bible/app/bible/system/auth/page.tsx new file mode 100644 index 0000000..a98ee20 --- /dev/null +++ b/apps/bible/app/bible/system/auth/page.tsx @@ -0,0 +1,18 @@ +import { getSession, logout } from '@/module/auth/lib' + +export default async function AuthPage() { + const session = await getSession() + return ( +
+ hello {session?.user?.name} +
{ + 'use server' + await logout() + }} + > + +
+
+ ) +} diff --git a/apps/bible/app/layout.tsx b/apps/bible/app/layout.tsx index 6141afb..6a5b14d 100644 --- a/apps/bible/app/layout.tsx +++ b/apps/bible/app/layout.tsx @@ -24,7 +24,7 @@ export default function RootLayout( - {props.children} +
{props.children}
diff --git a/apps/bible/lib/utils.ts b/apps/bible/lib/utils.ts index 00cade5..5d57228 100644 --- a/apps/bible/lib/utils.ts +++ b/apps/bible/lib/utils.ts @@ -5,5 +5,4 @@ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } - export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) diff --git a/apps/bible/middleware.ts b/apps/bible/middleware.ts new file mode 100644 index 0000000..7091dac --- /dev/null +++ b/apps/bible/middleware.ts @@ -0,0 +1,6 @@ +import { NextRequest } from 'next/server' +import { updateSession } from '@/module/auth/lib' + +export async function middleware(request: NextRequest) { + return await updateSession(request) +} diff --git a/apps/bible/module/auth/components/login.tsx b/apps/bible/module/auth/components/login.tsx new file mode 100644 index 0000000..b97ad6a --- /dev/null +++ b/apps/bible/module/auth/components/login.tsx @@ -0,0 +1,33 @@ +import { redirect } from 'next/navigation' +import { getSession, login, logout } from '../lib' +import { Stack } from '@/components/primitives/stack' + +export async function LoginForm() { + const session = await getSession() + return ( + +
{ + 'use server' + await login(formData) + // redirect('/bible/system/auth') + }} + > + +
+
+ +
+
{ + 'use server' + await logout() + redirect('/') + }} + > + +
+
{JSON.stringify(session, null, 2)}
+
+ ) +} diff --git a/apps/bible/module/auth/components/user-card.tsx b/apps/bible/module/auth/components/user-card.tsx new file mode 100644 index 0000000..ee7fb7e --- /dev/null +++ b/apps/bible/module/auth/components/user-card.tsx @@ -0,0 +1,15 @@ +import { Text } from '@/components/typography/text' +import { getSession } from '../lib' +import { cn } from '@/lib/utils' +import { Stack } from '@/components/primitives/stack' + +export const UserCard = async () => { + const session = await getSession() + if (!session) return null + return ( + + {session?.user.name} + {session?.user.email} + + ) +} diff --git a/apps/bible/module/auth/lib.ts b/apps/bible/module/auth/lib.ts new file mode 100644 index 0000000..ffdf7b7 --- /dev/null +++ b/apps/bible/module/auth/lib.ts @@ -0,0 +1,63 @@ +import { SignJWT, jwtVerify } from 'jose' +import { cookies } from 'next/headers' +import { NextRequest, NextResponse } from 'next/server' +import { Session } from './types' + +const secretKey = 'secret' +const key = new TextEncoder().encode(secretKey) +const expires = new Date(Date.now() + 86400 * 1000) // 86400 sec / 1 day + +export async function encrypt(payload: any) { + return await new SignJWT(payload) + .setProtectedHeader({ alg: 'HS256' }) + .setIssuedAt() + .setExpirationTime('1 day from now') + .sign(key) +} + +export async function decrypt(input: string): Promise { + const { payload } = await jwtVerify(input, key, { + algorithms: ['HS256'], + }) + return payload +} + +export async function login(formData: FormData) { + // Verify credentials && get the user + + const user = { email: formData.get('email'), name: 'John' } + + // Create the session + const session = await encrypt({ user, expires }) + + // Save the session in a cookie + cookies().set('session', session, { expires, httpOnly: true }) +} + +export async function logout() { + // Destroy the session + cookies().set('session', '', { expires: new Date(0) }) +} + +export async function getSession(): Promise { + const session = cookies().get('session')?.value + if (!session) return null + return await decrypt(session) +} + +export async function updateSession(request: NextRequest) { + const session = request.cookies.get('session')?.value + if (!session) return + + // Refresh the session so it doesn't expire + const parsed = await decrypt(session) + parsed.expires = expires + const res = NextResponse.next() + res.cookies.set({ + name: 'session', + value: await encrypt(parsed), + httpOnly: true, + expires: parsed.expires, + }) + return res +} diff --git a/apps/bible/module/auth/types.ts b/apps/bible/module/auth/types.ts new file mode 100644 index 0000000..0d95a7c --- /dev/null +++ b/apps/bible/module/auth/types.ts @@ -0,0 +1,11 @@ +export type User = { + email: string | null + name: string +} + +export type Session = { + expires: string + iat: number + exp: number + user: User +} diff --git a/apps/bible/package.json b/apps/bible/package.json index 9df11cc..a8f7306 100644 --- a/apps/bible/package.json +++ b/apps/bible/package.json @@ -14,6 +14,7 @@ "@radix-ui/react-slot": "^1.0.2", "class-variance-authority": "^0.6.1", "clsx": "^1.2.1", + "jose": "^5.2.2", "lucide-react": "^0.341.0", "next": "14.1.1", "next-themes": "^0.2.1", diff --git a/apps/bible/site/bible-contents.ts b/apps/bible/site/bible-contents.ts index f509641..eee9f60 100644 --- a/apps/bible/site/bible-contents.ts +++ b/apps/bible/site/bible-contents.ts @@ -53,6 +53,10 @@ export const bibleContents: BibleContent[] = [ title: 'Error Boundarys', href: routes.bible.system.errorBoundary.index, }, + { + title: 'Authorization', + href: routes.bible.system.auth.index, + }, ], }, ] diff --git a/apps/bible/site/routes.ts b/apps/bible/site/routes.ts index 60acd9b..933fa1e 100644 --- a/apps/bible/site/routes.ts +++ b/apps/bible/site/routes.ts @@ -50,6 +50,9 @@ export const routes = { errorBoundary: { index: '/bible/system/error-boundary', }, + auth: { + index: '/bible/system/auth', + } }, }, } as const