Skip to content

Commit d031ed3

Browse files
committed
Implement auth provider
1 parent 144a3b8 commit d031ed3

File tree

15 files changed

+908
-9
lines changed

15 files changed

+908
-9
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
POSTGRES_URL=postgres://postgres:postgres@localhost:54322/riven

bun.lockb

23.1 KB
Binary file not shown.

middleware.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { NextResponse } from 'next/server'
2+
import type { NextRequest } from 'next/server'
3+
4+
import { signToken, verifyToken } from '@/lib/auth/session'
5+
6+
const protectedRoutes = '/dashboard'
7+
8+
export async function middleware(request: NextRequest) {
9+
const { pathname } = request.nextUrl
10+
const sessionCookie = request.cookies.get('session')
11+
const isProtectedRoute = pathname.startsWith(protectedRoutes)
12+
13+
if (isProtectedRoute && !sessionCookie) {
14+
return NextResponse.redirect(new URL('/sign-in', request.url))
15+
}
16+
17+
const res = NextResponse.next()
18+
19+
if (sessionCookie) {
20+
try {
21+
const parsed = await verifyToken(sessionCookie.value)
22+
const expiresInOneDay = new Date(Date.now() + 24 * 60 * 60 * 1000)
23+
24+
res.cookies.set({
25+
name: 'session',
26+
value: await signToken({
27+
...parsed,
28+
expires: expiresInOneDay.toISOString()
29+
}),
30+
httpOnly: true,
31+
secure: true,
32+
sameSite: 'lax',
33+
expires: expiresInOneDay
34+
})
35+
} catch (error) {
36+
console.error('Error updating session:', error)
37+
res.cookies.delete('session')
38+
if (isProtectedRoute) {
39+
return NextResponse.redirect(new URL('/sign-in', request.url))
40+
}
41+
}
42+
}
43+
44+
return res
45+
}
46+
47+
export const config = {
48+
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)']
49+
}

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,31 @@
1616
"@radix-ui/react-select": "^2.1.5",
1717
"@radix-ui/react-toast": "^1.2.5",
1818
"@tailwindcss/postcss": "^4.0.3",
19+
"bcryptjs": "^2.4.3",
1920
"class-variance-authority": "^0.7.1",
21+
"drizzle-orm": "^0.39.3",
22+
"jose": "^5.9.6",
2023
"lucide-react": "^0.474.0",
2124
"next": "15.1.6",
2225
"next-themes": "^0.4.4",
26+
"postgres": "^3.4.5",
2327
"react": "19.0.0",
2428
"react-dom": "19.0.0",
2529
"tailwind-merge": "^3.0.1",
2630
"tailwindcss": "^4.0.3",
27-
"vaul": "^1.1.2"
31+
"vaul": "^1.1.2",
32+
"zod": "^3.24.2"
2833
},
2934
"devDependencies": {
3035
"@eslint/js": "^9.19.0",
3136
"@next/bundle-analyzer": "^15.1.6",
3237
"@next/eslint-plugin-next": "^15.1.6",
3338
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
39+
"@types/bcryptjs": "^2.4.6",
3440
"@types/node": "^22.13.0",
3541
"@types/react": "19.0.8",
3642
"@types/react-dom": "19.0.3",
43+
"drizzle-kit": "^0.30.4",
3744
"eslint": "^9.19.0",
3845
"eslint-config-next": "15.1.6",
3946
"eslint-config-prettier": "^10.0.1",

src/app/(dashboard)/dashboard/layout.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
import type { ReactNode } from 'react'
1+
'use client'
22

3-
export default function DashboardLayout({ children }: { children: ReactNode }) {
3+
import { type ReactNode } from 'react'
4+
5+
interface SidebarProps {
6+
children: ReactNode
7+
}
8+
9+
function Sidebar({ children }: SidebarProps) {
410
return (
511
<div className='container-wrapper'>
612
<div className='container flex-1 items-start md:grid md:grid-cols-[220px_minmax(0,1fr)] md:gap-6 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-10'>
@@ -12,3 +18,7 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
1218
</div>
1319
)
1420
}
21+
22+
export default function DashboardLayout({ ...props }: SidebarProps) {
23+
return <Sidebar {...props} />
24+
}

src/app/(dashboard)/layout.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use client'
2+
3+
import { type ReactNode, use } from 'react'
4+
5+
import { useRouter } from 'next/navigation'
6+
7+
import { signOut } from '@/app/(login)/actions'
8+
import { Header } from '@/components/header'
9+
import { Toaster } from '@/components/ui/toaster'
10+
import { useUser } from '@/lib/auth'
11+
12+
export default function Layout({ children }: { children: ReactNode }) {
13+
const { userPromise } = useUser()
14+
const user = use(userPromise)
15+
const router = useRouter()
16+
17+
async function handleSignOut() {
18+
await signOut()
19+
router.refresh()
20+
router.push('/')
21+
}
22+
23+
return (
24+
<div data-vaul-drawer-wrapper>
25+
<Header isAuthenticated={user} onSignOut={handleSignOut} />
26+
<main>{children}</main>
27+
<Toaster />
28+
</div>
29+
)
30+
}

0 commit comments

Comments
 (0)