implement authentik auth

This commit is contained in:
2025-08-16 19:09:57 -04:00
parent ad54758193
commit 2c6737ceb4
7 changed files with 139 additions and 14 deletions

View File

@@ -0,0 +1,2 @@
import { handlers } from "@/auth";
export const { GET, POST } = handlers;

View File

@@ -0,0 +1,37 @@
import { signIn, signOut } from "@/auth"
import { auth } from "@/auth"
import { Button } from "@/components/ui/button"
export default async function SignIn() {
const session = await auth()
if (session?.user) {
return (
<div className="flex items-center gap-4">
<form
action={async () => {
"use server"
await signOut()
}}
>
<Button type="submit" variant="secondary" >
Sign Out
</Button>
</form>
</div>
)
}
return (
<form
action={async () => {
"use server"
await signIn("authentik")
}}
>
<Button type="submit" variant="default" >
Sign In
</Button>
</form>
)
}

View File

@@ -3,6 +3,7 @@ import { Geist } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "next-themes";
import { ModeToggle } from "@/components/mode-toggle";
import SignIn from "./components/sign-in";
const geist = Geist({ subsets: ['latin', 'cyrillic'], variable: "--font-geist-sans" })
@@ -30,7 +31,10 @@ export default function RootLayout({
>
<header className="dark:text-white text-gray-900 px-4 py-3 font-bold shadow flex justify-between items-center-safe">
{metadata.title as string || "iCal PWA"}
<ModeToggle />
<div className="flex flex-row gap-2">
<SignIn />
<ModeToggle />
</div>
</header>
<main className="flex-1 p-4">{children}</main>
</ThemeProvider>

View File

@@ -13,8 +13,9 @@ import { addEvent, deleteEvent, getAllEvents, clearEvents, getDB } from '@/lib/d
import { parseICS, generateICS } from '@/lib/ical'
import type { CalendarEvent } from '@/lib/types'
import { Textarea } from '@/components/ui/textarea'
import { auth } from '@/auth'
export default function HomePage() {
export default async function HomePage() {
const [events, setEvents] = useState<CalendarEvent[]>([])
const [dialogOpen, setDialogOpen] = useState(false)
const [editingId, setEditingId] = useState<string | null>(null)
@@ -43,6 +44,8 @@ export default function HomePage() {
})()
}, [])
const session = await auth()
const resetForm = () => {
setTitle('')
setDescription('')
@@ -218,23 +221,30 @@ export default function HomePage() {
return (
<div onDragOver={handleDragOver} onDragLeave={handleDragLeave} onDrop={handleDrop}
className={`p-4 min-h-[80vh] rounded border-2 border-dashed transition ${isDragOver ? 'border-blue-500 bg-blue-50' : 'border-gray-300'
className={`p-4 min-h-[80vh] relative rounded border-2 border-dashed transition ${isDragOver ? 'border-blue-500 bg-blue-50' : 'border-gray-300'
}`}
>
<div className='absolute bottom-0 w-full pb-4 text-gray-400'>
<div className='max-w-fit m-auto'> Drag & Drop *.ics here</div>
</div>
{/* AI Toolbar */}
<div className="flex flex-row gap-4 mb-4 items-start">
<div className='w-full'>
<Textarea
className="wrap-anywhere min-h-12"
placeholder='Describe event for AI to create'
value={aiPrompt}
onChange={e => setAiPrompt(e.target.value)}
/>
</div>
{session?.user && (
<div className='w-full'>
<Textarea
className="wrap-anywhere min-h-12"
placeholder='Describe event for AI to create'
value={aiPrompt}
onChange={e => setAiPrompt(e.target.value)}
/>
</div>
)}
<div className='flex flex-row gap-2 pt-1.5'>
<Button onClick={handleAiCreate} disabled={aiLoading}>
{aiLoading ? 'Thinking...' : 'AI Create'}
</Button>
{session?.user && (
<Button onClick={handleAiCreate} disabled={aiLoading}>
{aiLoading ? 'Thinking...' : 'AI Create'}
</Button>
)}
<Button variant="secondary" onClick={handleAiSummarize} disabled={aiLoading}>
{aiLoading ? 'Summarizing...' : 'AI Summarize'}
</Button>

56
src/auth.ts Normal file
View File

@@ -0,0 +1,56 @@
import NextAuth, { NextAuthConfig } from "next-auth";
import Authentik from "next-auth/providers/authentik";
import type { Provider } from "next-auth/providers";
const providers: Provider[] = [
Authentik({
clientId: process.env.AUTH_AUTHENTIK_CLIENT_ID,
clientSecret: process.env.AUTH_AUTHENTIK_CLIENT_SECRET,
issuer: process.env.AUTH_AUTHENTIK_ISSUER,
authorization: {
params: {
scope: "openid email profile",
},
},
profile(profile) {
return {
id: profile.sub,
name: profile.name,
email: profile.email,
image: profile.picture,
};
},
}),
];
export const providerMap = providers.map((provider) => {
if (typeof provider === "function") {
const providerData = provider();
return { id: providerData.id, name: providerData.name };
} else {
return { id: provider.id, name: provider.name };
}
});
const config = {
providers,
pages: {
signIn: "/signin",
signOut: "/signout",
},
// callbacks: {
// authorized({ auth, request: { nextUrl } }) {
// const isLoggedIn = !!auth?.user;
// const isOnProtectedRoute = nextUrl.pathname.startsWith("/api/ai-event");
//
// if (isOnProtectedRoute) {
// if (isLoggedIn) return true;
// return false;
// } else if (isLoggedIn) {
// return Response.redirect(new URL("/api/ai-event", nextUrl));
// }
// return true;
// },
// },
} satisfies NextAuthConfig;
export const { handlers, signIn, signOut, auth } = NextAuth(config);