diff --git a/packages/nextjs/.env.development b/packages/nextjs/.env.development
index 8bec845..b981aa2 100644
--- a/packages/nextjs/.env.development
+++ b/packages/nextjs/.env.development
@@ -1,2 +1,3 @@
-
POSTGRES_URL="postgresql://postgres:mysecretpassword@localhost:5432/postgres"
+NEXTAUTH_URL=http://localhost:3000
+NEXTAUTH_SECRET=somereallysecretsecret
\ No newline at end of file
diff --git a/packages/nextjs/app/admin/page.tsx b/packages/nextjs/app/admin/page.tsx
new file mode 100644
index 0000000..9ac066c
--- /dev/null
+++ b/packages/nextjs/app/admin/page.tsx
@@ -0,0 +1,39 @@
+import type { NextPage } from "next";
+import { getServerSession } from "next-auth";
+import { Address } from "~~/components/scaffold-eth";
+import { getAllSubmissions } from "~~/services/database/repositories/submissions";
+import { authOptions } from "~~/utils/auth";
+
+const Admin: NextPage = async () => {
+ const session = await getServerSession(authOptions);
+
+ if (session?.user?.role !== "admin") {
+ return
Access denied
;
+ }
+
+ const submissions = await getAllSubmissions();
+ return (
+ <>
+
+ {submissions.map(submission => {
+ return (
+
+ );
+ })}
+
+ >
+ );
+};
+
+export default Admin;
diff --git a/packages/nextjs/app/api/auth/[...nextauth]/route.ts b/packages/nextjs/app/api/auth/[...nextauth]/route.ts
new file mode 100644
index 0000000..6238bcf
--- /dev/null
+++ b/packages/nextjs/app/api/auth/[...nextauth]/route.ts
@@ -0,0 +1,18 @@
+import { NextRequest, NextResponse } from "next/server";
+import NextAuth from "next-auth";
+import { authOptions, providers } from "~~/utils/auth";
+
+// For more information on each option (and a full list of options) go to
+// https://next-auth.js.org/configuration/options
+async function auth(req: NextRequest, res: NextResponse) {
+ const { searchParams } = new URL(req.url as string);
+ const isDefaultSigninPage = searchParams.get("nextauth")?.includes("signin");
+
+ if (isDefaultSigninPage) {
+ providers.pop();
+ }
+
+ return await NextAuth(req, res as unknown as { params: { nextauth: string[] } }, authOptions);
+}
+
+export { auth as GET, auth as POST };
diff --git a/packages/nextjs/app/siwe/page.tsx b/packages/nextjs/app/siwe/page.tsx
new file mode 100644
index 0000000..34fcf06
--- /dev/null
+++ b/packages/nextjs/app/siwe/page.tsx
@@ -0,0 +1,38 @@
+"use client";
+
+import { useEffect } from "react";
+import { useRouter } from "next/navigation";
+import { useConnectModal } from "@rainbow-me/rainbowkit";
+import { useAccount } from "wagmi";
+import { useAuthSession } from "~~/hooks/useAuthSession";
+import { useHandleLogin } from "~~/hooks/useHandleLogin";
+
+export default function Siwe() {
+ const { isConnected } = useAccount();
+ const { openConnectModal } = useConnectModal();
+ const { handleLogin } = useHandleLogin();
+ const { isAuthenticated } = useAuthSession();
+ const router = useRouter();
+
+ useEffect(() => {
+ if (isAuthenticated) {
+ router.push("/");
+ }
+ }, [router, isAuthenticated]);
+
+ return (
+
+ );
+}
diff --git a/packages/nextjs/components/Header.tsx b/packages/nextjs/components/Header.tsx
index 5b0355b..c1cc6c3 100644
--- a/packages/nextjs/components/Header.tsx
+++ b/packages/nextjs/components/Header.tsx
@@ -4,9 +4,10 @@ import React, { useCallback, useRef, useState } from "react";
import Image from "next/image";
import Link from "next/link";
import { usePathname } from "next/navigation";
-import { Bars3Icon, BugAntIcon } from "@heroicons/react/24/outline";
+import { Bars3Icon, BugAntIcon, LockClosedIcon } from "@heroicons/react/24/outline";
import { FaucetButton, RainbowKitCustomConnectButton } from "~~/components/scaffold-eth";
import { useOutsideClick } from "~~/hooks/scaffold-eth";
+import { useAuthSession } from "~~/hooks/useAuthSession";
type HeaderMenuLink = {
label: string;
@@ -23,6 +24,15 @@ export const menuLinks: HeaderMenuLink[] = [
label: "Submit",
href: "/submit",
},
+ {
+ label: "Admin",
+ href: "/admin",
+ icon: ,
+ },
+ {
+ label: "Siwe",
+ href: "/siwe",
+ },
{
label: "Debug Contracts",
href: "/debug",
@@ -32,10 +42,23 @@ export const menuLinks: HeaderMenuLink[] = [
export const HeaderMenuLinks = () => {
const pathname = usePathname();
+ const { isAdmin, data: session, isAuthenticated } = useAuthSession();
return (
<>
{menuLinks.map(({ label, href, icon }) => {
+ if (!session && label !== "Siwe") {
+ return null;
+ }
+
+ if (isAuthenticated && label === "Siwe") {
+ return null;
+ }
+
+ if (label === "Admin" && !isAdmin) {
+ return null;
+ }
+
const isActive = pathname === href;
return (
diff --git a/packages/nextjs/components/ScaffoldEthAppWithProviders.tsx b/packages/nextjs/components/ScaffoldEthAppWithProviders.tsx
index f67c2f7..fb56289 100644
--- a/packages/nextjs/components/ScaffoldEthAppWithProviders.tsx
+++ b/packages/nextjs/components/ScaffoldEthAppWithProviders.tsx
@@ -2,7 +2,9 @@
import { useEffect, useState } from "react";
import { RainbowKitProvider, darkTheme, lightTheme } from "@rainbow-me/rainbowkit";
+import { RainbowKitSiweNextAuthProvider } from "@rainbow-me/rainbowkit-siwe-next-auth";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import { SessionProvider } from "next-auth/react";
import { useTheme } from "next-themes";
import { Toaster } from "react-hot-toast";
import { WagmiProvider } from "wagmi";
@@ -47,15 +49,19 @@ export const ScaffoldEthAppWithProviders = ({ children }: { children: React.Reac
return (
-
-
-
- {children}
-
-
+
+
+
+
+
+ {children}
+
+
+
+
);
};
diff --git a/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx b/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx
index a9e3125..01a5b94 100644
--- a/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx
+++ b/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx
@@ -1,5 +1,6 @@
import { useRef, useState } from "react";
import { NetworkOptions } from "./NetworkOptions";
+import { signOut } from "next-auth/react";
import CopyToClipboard from "react-copy-to-clipboard";
import { getAddress } from "viem";
import { Address } from "viem";
@@ -125,7 +126,10 @@ export const AddressInfoDropdown = ({
diff --git a/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/index.tsx b/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/index.tsx
index aec40a9..f563ed7 100644
--- a/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/index.tsx
+++ b/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/index.tsx
@@ -1,14 +1,18 @@
"use client";
// @refresh reset
+import { useEffect } from "react";
import { Balance } from "../Balance";
import { AddressInfoDropdown } from "./AddressInfoDropdown";
import { AddressQRCodeModal } from "./AddressQRCodeModal";
import { WrongNetworkDropdown } from "./WrongNetworkDropdown";
import { ConnectButton } from "@rainbow-me/rainbowkit";
+import { signOut } from "next-auth/react";
import { Address } from "viem";
+import { useAccount } from "wagmi";
import { useNetworkColor } from "~~/hooks/scaffold-eth";
import { useTargetNetwork } from "~~/hooks/scaffold-eth/useTargetNetwork";
+import { useAuthSession } from "~~/hooks/useAuthSession";
import { getBlockExplorerAddressLink } from "~~/utils/scaffold-eth";
/**
@@ -17,6 +21,14 @@ import { getBlockExplorerAddressLink } from "~~/utils/scaffold-eth";
export const RainbowKitCustomConnectButton = ({ fullWidth }: { fullWidth?: boolean }) => {
const networkColor = useNetworkColor();
const { targetNetwork } = useTargetNetwork();
+ const { address, isConnected } = useAccount();
+ const { address: sessionAddress } = useAuthSession();
+
+ useEffect(() => {
+ if (isConnected && sessionAddress && sessionAddress !== address) {
+ signOut();
+ }
+ }, [address, isConnected, sessionAddress]);
return (
diff --git a/packages/nextjs/hooks/useAuthSession.ts b/packages/nextjs/hooks/useAuthSession.ts
new file mode 100644
index 0000000..a3366fb
--- /dev/null
+++ b/packages/nextjs/hooks/useAuthSession.ts
@@ -0,0 +1,11 @@
+import { UseSessionOptions, useSession } from "next-auth/react";
+
+export const useAuthSession = (options?: UseSessionOptions) => {
+ const sessionData = useSession(options);
+
+ const isAdmin = sessionData?.data?.user?.role === "admin";
+ const address = sessionData?.data?.user?.address;
+ const isAuthenticated = sessionData.status === "authenticated";
+
+ return { ...sessionData, isAdmin, address, isAuthenticated };
+};
diff --git a/packages/nextjs/hooks/useHandleLogin.ts b/packages/nextjs/hooks/useHandleLogin.ts
new file mode 100644
index 0000000..18c1fb7
--- /dev/null
+++ b/packages/nextjs/hooks/useHandleLogin.ts
@@ -0,0 +1,34 @@
+import { useCallback } from "react";
+import { getCsrfToken, signIn } from "next-auth/react";
+import { SiweMessage } from "siwe";
+import { useAccount, useSignMessage } from "wagmi";
+
+export const useHandleLogin = () => {
+ const { signMessageAsync } = useSignMessage();
+ const { address, chain } = useAccount();
+
+ const handleLogin = useCallback(async () => {
+ try {
+ const message = new SiweMessage({
+ domain: window.location.host,
+ address: address,
+ statement: "Sign in with Ethereum to the app.",
+ uri: window.location.origin,
+ version: "1",
+ chainId: chain?.id,
+ nonce: await getCsrfToken(),
+ });
+ const signature = await signMessageAsync({
+ message: message.prepareMessage(),
+ });
+ signIn("credentials", {
+ message: JSON.stringify(message),
+ signature,
+ });
+ } catch (error) {
+ console.log(error);
+ }
+ }, [address, chain?.id, signMessageAsync]);
+
+ return { handleLogin };
+};
diff --git a/packages/nextjs/middleware.ts b/packages/nextjs/middleware.ts
new file mode 100644
index 0000000..00b6c0a
--- /dev/null
+++ b/packages/nextjs/middleware.ts
@@ -0,0 +1,23 @@
+import { NextResponse } from "next/server";
+import type { NextRequest } from "next/server";
+import { getToken } from "next-auth/jwt";
+
+const SIGN_IN_PAGE = "/siwe";
+
+export async function middleware(request: NextRequest) {
+ const token = await getToken({ req: request, secret: process.env.NEXTAUTH_SECRET });
+
+ // If no token and the request is not for the sign-in page, redirect to the sign-in page
+ if (!token && request.nextUrl.pathname !== SIGN_IN_PAGE) {
+ const url = request.nextUrl.clone();
+ url.pathname = SIGN_IN_PAGE;
+ return NextResponse.redirect(url);
+ }
+
+ // If a token is found or the request is for the sign-in page, proceed as normal
+ return NextResponse.next();
+}
+
+export const config = {
+ matcher: "/((?!api|static|.*\\..*|_next).*)",
+};
diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json
index 5f4ebe8..7fc61bb 100644
--- a/packages/nextjs/package.json
+++ b/packages/nextjs/package.json
@@ -18,6 +18,7 @@
"dependencies": {
"@heroicons/react": "~2.0.11",
"@rainbow-me/rainbowkit": "2.1.2",
+ "@rainbow-me/rainbowkit-siwe-next-auth": "^0.4.1",
"@tanstack/react-query": "~5.28.6",
"@uniswap/sdk-core": "~4.0.1",
"@uniswap/v2-sdk": "~3.0.1",
@@ -26,7 +27,9 @@
"daisyui": "4.5.0",
"dotenv": "^16.4.5",
"drizzle-orm": "^0.32.1",
+ "ethers": "^6.13.1",
"next": "~14.0.4",
+ "next-auth": "^4.24.7",
"next-themes": "~0.2.1",
"nprogress": "~0.2.0",
"pg": "^8.12.0",
@@ -35,6 +38,7 @@
"react-copy-to-clipboard": "~5.1.0",
"react-dom": "~18.2.0",
"react-hot-toast": "~2.4.0",
+ "siwe": "^2.3.2",
"use-debounce": "~8.0.4",
"usehooks-ts": "2.13.0",
"viem": "2.17.4",
diff --git a/packages/nextjs/services/database/repositories/builders.ts b/packages/nextjs/services/database/repositories/builders.ts
index ad7bc2c..736d914 100644
--- a/packages/nextjs/services/database/repositories/builders.ts
+++ b/packages/nextjs/services/database/repositories/builders.ts
@@ -13,5 +13,5 @@ export async function getBuilderById(id: string) {
}
export async function createBuilder(builder: BuilderInsert) {
- return await db.insert(builders).values(builder);
+ return await db.insert(builders).values(builder).returning({ id: builders.id, role: builders.role });
}
diff --git a/packages/nextjs/types/next-auth/next-auth.d.ts b/packages/nextjs/types/next-auth/next-auth.d.ts
new file mode 100644
index 0000000..4aac85b
--- /dev/null
+++ b/packages/nextjs/types/next-auth/next-auth.d.ts
@@ -0,0 +1,23 @@
+import "next-auth";
+import { DefaultUser } from "next-auth";
+import { DefaultJWT } from "next-auth/jwt";
+
+declare module "next-auth" {
+ export interface Session {
+ user: {
+ address?: string | null;
+ role?: string | null;
+ };
+ expires: ISODateString;
+ }
+
+ export interface User extends DefaultUser {
+ role?: string | null;
+ address?: string | null;
+ }
+
+ export interface JWT extends DefaultJWT {
+ role?: string | null;
+ address?: string | null;
+ }
+}
diff --git a/packages/nextjs/utils/auth.ts b/packages/nextjs/utils/auth.ts
new file mode 100644
index 0000000..1be6c87
--- /dev/null
+++ b/packages/nextjs/utils/auth.ts
@@ -0,0 +1,78 @@
+import { cookies } from "next/headers";
+import { AuthOptions, JWT, Session, User } from "next-auth";
+import CredentialsProvider from "next-auth/providers/credentials";
+import { getCsrfToken } from "next-auth/react";
+import { SiweMessage } from "siwe";
+import { createBuilder, getBuilderById } from "~~/services/database/repositories/builders";
+
+export const providers = [
+ CredentialsProvider({
+ name: "Ethereum",
+ credentials: {
+ message: {
+ label: "Message",
+ type: "text",
+ placeholder: "0x0",
+ },
+ signature: {
+ label: "Signature",
+ type: "text",
+ placeholder: "0x0",
+ },
+ },
+ async authorize(credentials) {
+ try {
+ const siwe = new SiweMessage(JSON.parse(credentials?.message || "{}"));
+ const nextAuthUrl = new URL(process.env.NEXTAUTH_URL as string);
+
+ const result = await siwe.verify({
+ signature: credentials?.signature || "",
+ domain: nextAuthUrl.host,
+ nonce: await getCsrfToken({
+ req: {
+ headers: {
+ cookie: cookies().toString(),
+ },
+ },
+ }),
+ });
+
+ if (result.success) {
+ let user = await getBuilderById(siwe.address);
+
+ if (!user) {
+ // Create a new user if don't exist
+ user = (await createBuilder({ id: siwe.address, role: "user" }))[0];
+ }
+
+ return { id: user.id, role: user.role };
+ }
+ return null;
+ } catch (e) {
+ return null;
+ }
+ },
+ }),
+];
+
+export const authOptions: AuthOptions = {
+ // https://next-auth.js.org/configuration/providers/oauth
+ providers,
+ session: {
+ strategy: "jwt",
+ },
+ secret: process.env.NEXTAUTH_SECRET,
+ callbacks: {
+ async jwt({ token, user }: { token: JWT; user: User }) {
+ if (user) {
+ token.role = user.role;
+ }
+ return token;
+ },
+ async session({ session, token }: { session: Session; token: JWT }) {
+ session.user.address = token.sub;
+ session.user.role = token.role;
+ return session;
+ },
+ },
+} as const;
diff --git a/yarn.lock b/yarn.lock
index 7e4b0b9..3fdd7af 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -12,6 +12,13 @@ __metadata:
languageName: node
linkType: hard
+"@adraffy/ens-normalize@npm:1.10.1":
+ version: 1.10.1
+ resolution: "@adraffy/ens-normalize@npm:1.10.1"
+ checksum: 0836f394ea256972ec19a0b5e78cb7f5bcdfd48d8a32c7478afc94dd53ae44c04d1aa2303d7f3077b4f3ac2323b1f557ab9188e8059978748fdcd83e04a80dcc
+ languageName: node
+ linkType: hard
+
"@alloc/quick-lru@npm:^5.2.0":
version: 5.2.0
resolution: "@alloc/quick-lru@npm:5.2.0"
@@ -133,6 +140,15 @@ __metadata:
languageName: node
linkType: hard
+"@babel/runtime@npm:^7.20.13":
+ version: 7.25.0
+ resolution: "@babel/runtime@npm:7.25.0"
+ dependencies:
+ regenerator-runtime: ^0.14.0
+ checksum: 4a2a374a58eb01aaa65c5762606e90b3a1f448e0c637d42278b6cc0b42a9f5399b5f381ba9f237ee087da2860d14dd2d1de7bddcbe18be6a3cafba97e44bed64
+ languageName: node
+ linkType: hard
+
"@babel/template@npm:^7.24.7":
version: 7.24.7
resolution: "@babel/template@npm:7.24.7"
@@ -1887,7 +1903,7 @@ __metadata:
languageName: node
linkType: hard
-"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:~1.4.0":
+"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1.1.2, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:~1.4.0":
version: 1.4.0
resolution: "@noble/hashes@npm:1.4.0"
checksum: 8ba816ae26c90764b8c42493eea383716396096c5f7ba6bea559993194f49d80a73c081f315f4c367e51bd2d5891700bcdfa816b421d24ab45b41cb03e4f3342
@@ -2261,6 +2277,13 @@ __metadata:
languageName: node
linkType: hard
+"@panva/hkdf@npm:^1.0.2":
+ version: 1.2.1
+ resolution: "@panva/hkdf@npm:1.2.1"
+ checksum: a4a9d1812f88f02bc163b365524bbaa5239cc4711e5e7be1bda68dabae1c896cf1cd12520949b0925a6910733d1afcb25ab51fd3cf06f0f69aee988fffebf56e
+ languageName: node
+ linkType: hard
+
"@parcel/watcher-android-arm64@npm:2.4.1":
version: 2.4.1
resolution: "@parcel/watcher-android-arm64@npm:2.4.1"
@@ -2420,6 +2443,18 @@ __metadata:
languageName: node
linkType: hard
+"@rainbow-me/rainbowkit-siwe-next-auth@npm:^0.4.1":
+ version: 0.4.1
+ resolution: "@rainbow-me/rainbowkit-siwe-next-auth@npm:0.4.1"
+ peerDependencies:
+ "@rainbow-me/rainbowkit": 2.0.x || 2.1.x
+ next-auth: ">=4.21.0 <5"
+ react: ">=18"
+ siwe: ^2.1.4
+ checksum: ce885be3f12df968c5dc223e18bd0d21375da872e70bb2244b4524b9e01239268df3b419001912974a435ca05a628ed81847c15d5b44bd7854ca89148df175d5
+ languageName: node
+ linkType: hard
+
"@rainbow-me/rainbowkit@npm:2.1.0":
version: 2.1.0
resolution: "@rainbow-me/rainbowkit@npm:2.1.0"
@@ -2622,6 +2657,7 @@ __metadata:
dependencies:
"@heroicons/react": ~2.0.11
"@rainbow-me/rainbowkit": 2.1.2
+ "@rainbow-me/rainbowkit-siwe-next-auth": ^0.4.1
"@tanstack/react-query": ~5.28.6
"@trivago/prettier-plugin-sort-imports": ~4.3.0
"@types/node": ^17.0.45
@@ -2645,7 +2681,9 @@ __metadata:
eslint-config-next: ~14.0.4
eslint-config-prettier: ~9.1.0
eslint-plugin-prettier: ~5.1.3
+ ethers: ^6.13.1
next: ~14.0.4
+ next-auth: ^4.24.7
next-themes: ~0.2.1
nprogress: ~0.2.0
pg: ^8.12.0
@@ -2656,6 +2694,7 @@ __metadata:
react-copy-to-clipboard: ~5.1.0
react-dom: ~18.2.0
react-hot-toast: ~2.4.0
+ siwe: ^2.3.2
tailwindcss: ~3.4.3
tsx: ^4.16.2
type-fest: ~4.6.0
@@ -2781,6 +2820,18 @@ __metadata:
languageName: node
linkType: hard
+"@spruceid/siwe-parser@npm:^2.1.2":
+ version: 2.1.2
+ resolution: "@spruceid/siwe-parser@npm:2.1.2"
+ dependencies:
+ "@noble/hashes": ^1.1.2
+ apg-js: ^4.3.0
+ uri-js: ^4.4.1
+ valid-url: ^1.0.9
+ checksum: feb0806ed3da945192245e6ac20108a1e4ef79184a04967bf251307e7315ca824862b016bd8e2100a739e8ee76ef768f088314601adbf3e847bb7692fcb33ac0
+ languageName: node
+ linkType: hard
+
"@stablelib/aead@npm:^1.0.1":
version: 1.0.1
resolution: "@stablelib/aead@npm:1.0.1"
@@ -4657,6 +4708,13 @@ __metadata:
languageName: node
linkType: hard
+"apg-js@npm:^4.3.0":
+ version: 4.4.0
+ resolution: "apg-js@npm:4.4.0"
+ checksum: 81f9753ef8ec102d6ec7ceddd57a5aee2e7e238ac5877165f9ac6cb5cb8ec73ec5070e040d3af04822bc0de754699b0199c39159ed0d8453d6bb99812a326deb
+ languageName: node
+ linkType: hard
+
"aproba@npm:^1.0.3 || ^2.0.0":
version: 2.0.0
resolution: "aproba@npm:2.0.0"
@@ -5894,6 +5952,13 @@ __metadata:
languageName: node
linkType: hard
+"cookie@npm:^0.5.0":
+ version: 0.5.0
+ resolution: "cookie@npm:0.5.0"
+ checksum: 1f4bd2ca5765f8c9689a7e8954183f5332139eb72b6ff783d8947032ec1fdf43109852c178e21a953a30c0dd42257828185be01b49d1eb1a67fd054ca588a180
+ languageName: node
+ linkType: hard
+
"copy-to-clipboard@npm:^3.3.1":
version: 3.3.3
resolution: "copy-to-clipboard@npm:3.3.3"
@@ -7995,6 +8060,21 @@ __metadata:
languageName: node
linkType: hard
+"ethers@npm:^6.13.1":
+ version: 6.13.2
+ resolution: "ethers@npm:6.13.2"
+ dependencies:
+ "@adraffy/ens-normalize": 1.10.1
+ "@noble/curves": 1.2.0
+ "@noble/hashes": 1.3.2
+ "@types/node": 18.15.13
+ aes-js: 4.0.0-beta.5
+ tslib: 2.4.0
+ ws: 8.17.1
+ checksum: 981860c736c7ae121774ad38ea07e3611ce524a77d2fcb77db499b65afe0cbe8f344cd5204d94b68b316349ff770fd2a7d9c8b2039da41c072f98d9864099925
+ languageName: node
+ linkType: hard
+
"ethers@npm:~6.10.0":
version: 6.10.0
resolution: "ethers@npm:6.10.0"
@@ -10028,6 +10108,13 @@ __metadata:
languageName: node
linkType: hard
+"jose@npm:^4.15.5":
+ version: 4.15.9
+ resolution: "jose@npm:4.15.9"
+ checksum: 41abe1c99baa3cf8a78ebbf93da8f8e50e417b7a26754c4afa21865d87527b8ac2baf66de2c5f6accc3f7d7158658dae7364043677236ea1d07895b040097f15
+ languageName: node
+ linkType: hard
+
"js-sdsl@npm:^4.1.4":
version: 4.4.2
resolution: "js-sdsl@npm:4.4.2"
@@ -11157,6 +11244,31 @@ __metadata:
languageName: node
linkType: hard
+"next-auth@npm:^4.24.7":
+ version: 4.24.7
+ resolution: "next-auth@npm:4.24.7"
+ dependencies:
+ "@babel/runtime": ^7.20.13
+ "@panva/hkdf": ^1.0.2
+ cookie: ^0.5.0
+ jose: ^4.15.5
+ oauth: ^0.9.15
+ openid-client: ^5.4.0
+ preact: ^10.6.3
+ preact-render-to-string: ^5.1.19
+ uuid: ^8.3.2
+ peerDependencies:
+ next: ^12.2.5 || ^13 || ^14
+ nodemailer: ^6.6.5
+ react: ^17.0.2 || ^18
+ react-dom: ^17.0.2 || ^18
+ peerDependenciesMeta:
+ nodemailer:
+ optional: true
+ checksum: e7849ecf86394d86f08730b96f869c9353b6cc003794fcd953db2949f1ab06a2ce4f7a67312fdb8c7b3bbe7e99a8a215f11ef4eba5ec93f6c3e8ac1954e8b3e6
+ languageName: node
+ linkType: hard
+
"next-themes@npm:~0.2.1":
version: 0.2.1
resolution: "next-themes@npm:0.2.1"
@@ -11455,6 +11567,13 @@ __metadata:
languageName: node
linkType: hard
+"oauth@npm:^0.9.15":
+ version: 0.9.15
+ resolution: "oauth@npm:0.9.15"
+ checksum: 957c0d8d85300398dcb0e293953650c0fc3facc795bee8228238414f19f59cef5fd4ee8d17a972c142924c10c5f6ec50ef80f77f4a6cc6e3c98f9d22c027801c
+ languageName: node
+ linkType: hard
+
"obj-multiplex@npm:^1.0.0":
version: 1.0.0
resolution: "obj-multiplex@npm:1.0.0"
@@ -11473,6 +11592,13 @@ __metadata:
languageName: node
linkType: hard
+"object-hash@npm:^2.2.0":
+ version: 2.2.0
+ resolution: "object-hash@npm:2.2.0"
+ checksum: 55ba841e3adce9c4f1b9b46b41983eda40f854e0d01af2802d3ae18a7085a17168d6b81731d43fdf1d6bcbb3c9f9c56d22c8fea992203ad90a38d7d919bc28f1
+ languageName: node
+ linkType: hard
+
"object-hash@npm:^3.0.0":
version: 3.0.0
resolution: "object-hash@npm:3.0.0"
@@ -11593,6 +11719,13 @@ __metadata:
languageName: node
linkType: hard
+"oidc-token-hash@npm:^5.0.3":
+ version: 5.0.3
+ resolution: "oidc-token-hash@npm:5.0.3"
+ checksum: 35fa19aea9ff2c509029ec569d74b778c8a215b92bd5e6e9bc4ebbd7ab035f44304ff02430a6397c3fb7c1d15ebfa467807ca0bcd31d06ba610b47798287d303
+ languageName: node
+ linkType: hard
+
"on-exit-leak-free@npm:^0.2.0":
version: 0.2.0
resolution: "on-exit-leak-free@npm:0.2.0"
@@ -11647,6 +11780,18 @@ __metadata:
languageName: node
linkType: hard
+"openid-client@npm:^5.4.0":
+ version: 5.6.5
+ resolution: "openid-client@npm:5.6.5"
+ dependencies:
+ jose: ^4.15.5
+ lru-cache: ^6.0.0
+ object-hash: ^2.2.0
+ oidc-token-hash: ^5.0.3
+ checksum: 2240079f761173b10635ce5fefbac04b6820f54e00d588ab2afdddb6c0f0ab6568e663cf1ab6a4a2297fbdbb73e42d78b8190f91dba7e1b80d287b2127fcbc7c
+ languageName: node
+ linkType: hard
+
"optionator@npm:^0.8.1":
version: 0.8.3
resolution: "optionator@npm:0.8.3"
@@ -12347,7 +12492,18 @@ __metadata:
languageName: node
linkType: hard
-"preact@npm:^10.16.0":
+"preact-render-to-string@npm:^5.1.19":
+ version: 5.2.6
+ resolution: "preact-render-to-string@npm:5.2.6"
+ dependencies:
+ pretty-format: ^3.8.0
+ peerDependencies:
+ preact: ">=10"
+ checksum: be8d5d8fb502d422c503e68af7bcccb6facd942f3ae9a4d093ebe3f1d4f0b15c540624bdac434d53a2a8e8fb7afa4606383414e937c40933ca43445470a026ff
+ languageName: node
+ linkType: hard
+
+"preact@npm:^10.16.0, preact@npm:^10.6.3":
version: 10.23.1
resolution: "preact@npm:10.23.1"
checksum: fb7d28c7da1442ed80435444326fa2dcb563d4af05f631611fff8c08fb46a6f209d1a780aa7638a0bc2ac4d7a351f3304ff97bebfd6f5ee5bc4feb880d84a34f
@@ -12402,6 +12558,13 @@ __metadata:
languageName: node
linkType: hard
+"pretty-format@npm:^3.8.0":
+ version: 3.8.0
+ resolution: "pretty-format@npm:3.8.0"
+ checksum: 21a114d43ef06978f8f7f6212be4649b0b094f05d9b30e14e37550bf35c8ca24d8adbca9e5adc4cc15d9eaf7a1e7a30478a4dc37b30982bfdf0292a5b385484c
+ languageName: node
+ linkType: hard
+
"pretty-ms@npm:7.0.1":
version: 7.0.1
resolution: "pretty-ms@npm:7.0.1"
@@ -13473,6 +13636,20 @@ __metadata:
languageName: node
linkType: hard
+"siwe@npm:^2.3.2":
+ version: 2.3.2
+ resolution: "siwe@npm:2.3.2"
+ dependencies:
+ "@spruceid/siwe-parser": ^2.1.2
+ "@stablelib/random": ^1.0.1
+ uri-js: ^4.4.1
+ valid-url: ^1.0.9
+ peerDependencies:
+ ethers: ^5.6.8 || ^6.0.8
+ checksum: f49356ceaf2dff256cb5379a41cd75461dcd67cc478e0f3338943aa094d4d92d35b8295e6a987afbd16fdb6c870ffb4d43adf9ff79085aaddab58084df8f188c
+ languageName: node
+ linkType: hard
+
"slash@npm:^3.0.0":
version: 3.0.0
resolution: "slash@npm:3.0.0"
@@ -15026,7 +15203,7 @@ __metadata:
languageName: node
linkType: hard
-"uri-js@npm:^4.2.2":
+"uri-js@npm:^4.2.2, uri-js@npm:^4.4.1":
version: 4.4.1
resolution: "uri-js@npm:4.4.1"
dependencies:
@@ -15166,6 +15343,13 @@ __metadata:
languageName: node
linkType: hard
+"valid-url@npm:^1.0.9":
+ version: 1.0.9
+ resolution: "valid-url@npm:1.0.9"
+ checksum: 3ecb030559404441c2cf104cbabab8770efb0f36d117db03d1081052ef133015a68806148ce954bb4dd0b5c42c14b709a88783c93d66b0916cb67ba771c98702
+ languageName: node
+ linkType: hard
+
"valtio@npm:1.11.2":
version: 1.11.2
resolution: "valtio@npm:1.11.2"