Skip to content

Commit

Permalink
add authorization
Browse files Browse the repository at this point in the history
  • Loading branch information
dinoDanic committed Mar 2, 2024
1 parent 269a121 commit fc87a57
Show file tree
Hide file tree
Showing 14 changed files with 172 additions and 3 deletions.
6 changes: 5 additions & 1 deletion apps/bible/app/bible/components/bible-side.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Stack gap="2xl" className="p-4">
<Suspense fallback={<div>loading user card..</div>}>
<UserCard />
</Suspense>
{bibleContents.map((bibleContent) => (
<BibleContent key={bibleContent.title} {...bibleContent} />
))}
Expand Down
9 changes: 9 additions & 0 deletions apps/bible/app/bible/system/auth/layout.tsx
Original file line number Diff line number Diff line change
@@ -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 <LoginForm />
return <>{children}</>
}
3 changes: 3 additions & 0 deletions apps/bible/app/bible/system/auth/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Loading() {
return <div>Loading user..</div>
}
18 changes: 18 additions & 0 deletions apps/bible/app/bible/system/auth/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { getSession, logout } from '@/module/auth/lib'

export default async function AuthPage() {
const session = await getSession()
return (
<div>
hello {session?.user?.name}
<form
action={async () => {
'use server'
await logout()
}}
>
<button type="submit">Logout</button>
</form>
</div>
)
}
2 changes: 1 addition & 1 deletion apps/bible/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default function RootLayout(
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem disableTransitionOnChange>
<Sidebar>
<BibleSide />
{props.children}
<div className='py-xs'>{props.children}</div>
</Sidebar>
</ThemeProvider>
</body>
Expand Down
1 change: 0 additions & 1 deletion apps/bible/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}


export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
6 changes: 6 additions & 0 deletions apps/bible/middleware.ts
Original file line number Diff line number Diff line change
@@ -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)
}
33 changes: 33 additions & 0 deletions apps/bible/module/auth/components/login.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Stack className='text-sm'>
<form
action={async (formData) => {
'use server'
await login(formData)
// redirect('/bible/system/auth')
}}
>
<input className='p-xs' type="email" name="email" placeholder="Email" />
<br />
<br />
<button type="submit">Login</button>
</form>
<form
action={async () => {
'use server'
await logout()
redirect('/')
}}
>
<button type="submit">Logout</button>
</form>
<pre>{JSON.stringify(session, null, 2)}</pre>
</Stack>
)
}
15 changes: 15 additions & 0 deletions apps/bible/module/auth/components/user-card.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Stack gap="xs" className={cn('p-sm border rounded-md animate-in slide-in-from-top overflow-hidden')}>
<Text>{session?.user.name}</Text>
<Text tone="muted">{session?.user.email}</Text>
</Stack>
)
}
63 changes: 63 additions & 0 deletions apps/bible/module/auth/lib.ts
Original file line number Diff line number Diff line change
@@ -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<any> {
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<Session | null> {
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
}
11 changes: 11 additions & 0 deletions apps/bible/module/auth/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type User = {
email: string | null
name: string
}

export type Session = {
expires: string
iat: number
exp: number
user: User
}
1 change: 1 addition & 0 deletions apps/bible/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions apps/bible/site/bible-contents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
],
},
]
3 changes: 3 additions & 0 deletions apps/bible/site/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ export const routes = {
errorBoundary: {
index: '/bible/system/error-boundary',
},
auth: {
index: '/bible/system/auth',
}
},
},
} as const

0 comments on commit fc87a57

Please sign in to comment.