implement authentik auth
This commit is contained in:
2
src/app/api/auth/[...nextauth]/route.ts
Normal file
2
src/app/api/auth/[...nextauth]/route.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import { handlers } from "@/auth";
|
||||
export const { GET, POST } = handlers;
|
||||
37
src/app/components/sign-in.tsx
Normal file
37
src/app/components/sign-in.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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
56
src/auth.ts
Normal 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);
|
||||
Reference in New Issue
Block a user