From d77adc37109371eb99502b97821517b4a2804eb6 Mon Sep 17 00:00:00 2001 From: chriseo Date: Thu, 23 Apr 2026 22:36:59 +0200 Subject: [PATCH] Add ZITADEL auth and hello world page --- app/api/auth/[...nextauth]/route.ts | 2 + app/layout.tsx | 4 +- app/page.tsx | 97 +++++++++++--------------- auth.ts | 31 +++++++++ package-lock.json | 103 ++++++++++++++++++++++++++++ package.json | 1 + 6 files changed, 178 insertions(+), 60 deletions(-) create mode 100644 app/api/auth/[...nextauth]/route.ts create mode 100644 auth.ts diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..42e2953 --- /dev/null +++ b/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,2 @@ +import { handlers } from "@/auth" +export const { GET, POST } = handlers diff --git a/app/layout.tsx b/app/layout.tsx index 976eb90..7f1e1ce 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -13,8 +13,8 @@ const geistMono = Geist_Mono({ }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "Demo", + description: "NextAuth.js + ZITADEL demo", }; export default function RootLayout({ diff --git a/app/page.tsx b/app/page.tsx index 3f36f7c..e811742 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,65 +1,46 @@ -import Image from "next/image"; +import { auth, signIn, signOut } from "@/auth" + +export default async function Home() { + const session = await auth() -export default function Home() { return ( -
-
- Next.js logo -
-

- To get started, edit the page.tsx file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - +

+ {session ? ( + <> +

+ Hello, {session.user?.name}! +

+
{ + "use server" + await signOut({ redirectTo: "/" }) + }} > - Templates - {" "} - or the{" "} - + Logout + + + + ) : ( +
{ + "use server" + await signIn("zitadel") + }} + > +
-
- - Vercel logomark - Deploy Now - - - Documentation - -
+ Login + + + )}
- ); + ) } diff --git a/auth.ts b/auth.ts new file mode 100644 index 0000000..f897fca --- /dev/null +++ b/auth.ts @@ -0,0 +1,31 @@ +import NextAuth from "next-auth" +import ZITADEL from "next-auth/providers/zitadel" + +export const { handlers, auth, signIn, signOut } = NextAuth({ + providers: [ + ZITADEL({ + clientId: process.env.ZITADEL_CLIENT_ID!, + clientSecret: process.env.ZITADEL_CLIENT_SECRET!, + issuer: process.env.ZITADEL_ISSUER, + }), + ], + callbacks: { + async jwt({ token, account }) { + // account is only present on the initial sign-in + if (account?.access_token) { + const res = await fetch( + `${process.env.ZITADEL_ISSUER}/oidc/v1/userinfo`, + { headers: { Authorization: `Bearer ${account.access_token}` } } + ) + const userinfo = await res.json() + console.log("[auth] ZITADEL userinfo:", JSON.stringify(userinfo, null, 2)) + token.name = userinfo.name ?? userinfo.preferred_username ?? token.name + } + return token + }, + session({ session, token }) { + if (token.name) session.user.name = token.name as string + return session + }, + }, +}) diff --git a/package-lock.json b/package-lock.json index 30f5edf..7db933f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "next": "16.2.4", + "next-auth": "^5.0.0-beta.31", "react": "19.2.4", "react-dom": "19.2.4" }, @@ -36,6 +37,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@auth/core": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.41.2.tgz", + "integrity": "sha512-Hx5MNBxN2fJTbJKGUKAA0wca43D0Akl3TvufY54Gn8lop7F+34vU1zA1pn0vQfIoVuLIrpfc2nkyjwIaPJMW7w==", + "license": "ISC", + "dependencies": { + "@panva/hkdf": "^1.2.1", + "jose": "^6.0.6", + "oauth4webapi": "^3.3.0", + "preact": "10.24.3", + "preact-render-to-string": "6.5.11" + }, + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "nodemailer": "^7.0.7" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -1286,6 +1316,15 @@ "node": ">=12.4.0" } }, + "node_modules/@panva/hkdf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -4506,6 +4545,15 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5120,6 +5168,33 @@ } } }, + "node_modules/next-auth": { + "version": "5.0.0-beta.31", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.31.tgz", + "integrity": "sha512-1OBgCKPzo+S7UWWMp3xgvGvIJ0OpV7B3vR4ZDRqD9a4Ch+OT6dakLXG9ivhtmIWVa71nTSXattOHyCg8sNi8/Q==", + "license": "ISC", + "dependencies": { + "@auth/core": "0.41.2" + }, + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "next": "^14.0.0-0 || ^15.0.0 || ^16.0.0", + "nodemailer": "^7.0.7", + "react": "^18.2.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -5174,6 +5249,15 @@ "dev": true, "license": "MIT" }, + "node_modules/oauth4webapi": { + "version": "3.8.5", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.5.tgz", + "integrity": "sha512-A8jmyUckVhRJj5lspguklcl90Ydqk61H3dcU0oLhH3Yv13KpAliKTt5hknpGGPZSSfOwGyraNEFmofDYH+1kSg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5463,6 +5547,25 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/preact": { + "version": "10.24.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", + "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "6.5.11", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.5.11.tgz", + "integrity": "sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==", + "license": "MIT", + "peerDependencies": { + "preact": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", diff --git a/package.json b/package.json index 1b8a2e2..37084d5 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "next": "16.2.4", + "next-auth": "^5.0.0-beta.31", "react": "19.2.4", "react-dom": "19.2.4" },