diff --git a/.changeset/five-hairs-fix.md b/.changeset/five-hairs-fix.md new file mode 100644 index 00000000000..bae9d6e59e3 --- /dev/null +++ b/.changeset/five-hairs-fix.md @@ -0,0 +1,5 @@ +--- +"@thirdweb-dev/auth": patch +--- + +Add support for Next.js App Router diff --git a/packages/auth/package.json b/packages/auth/package.json index 4d7c8827830..92e58163d6d 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -111,7 +111,7 @@ "express": "^4.18.1", "fastify": "^4.24.2", "mocha": "^10.2.0", - "next": "^12.3.4", + "next": "^13.4", "next-auth": "^4.22.3", "prettier": "^3.1.1", "typescript": "^5.3.3" diff --git a/packages/auth/src/next/common/types.ts b/packages/auth/src/next/common/types.ts new file mode 100644 index 00000000000..c03d866df75 --- /dev/null +++ b/packages/auth/src/next/common/types.ts @@ -0,0 +1,62 @@ +import type { GenericAuthWallet } from "@thirdweb-dev/wallets"; +import { z } from "zod"; + +import { Json, LoginPayloadOutputSchema, User } from "../../core"; + +export const PayloadBodySchema = z.object({ + address: z.string(), + chainId: z.string().optional(), +}); + +export const ActiveBodySchema = z.object({ + address: z.string(), +}); + +export const LoginPayloadBodySchema = z.object({ + payload: LoginPayloadOutputSchema, +}); + +export type ThirdwebAuthRoute = + | "payload" + | "login" + | "logout" + | "user" + | "switch-account"; + +export type ThirdwebAuthUser< + TData extends Json = Json, + TSession extends Json = Json, +> = User & { + data?: TData; +}; + +export type ThirdwebAuthConfigShared = { + domain: string; + wallet: GenericAuthWallet; + authOptions?: { + statement?: string; + uri?: string; + version?: string; + chainId?: string; + resources?: string[]; + validateNonce?: + | ((nonce: string) => void) + | ((nonce: string) => Promise); + validateTokenId?: + | ((tokenId: string) => void) + | ((tokenId: string) => Promise); + loginPayloadDurationInSeconds?: number; + tokenDurationInSeconds?: number; + refreshIntervalInSeconds?: number; + }; + cookieOptions?: { + domain?: string; + path?: string; + sameSite?: "lax" | "strict" | "none"; + secure?: boolean; + }; +}; + +export type ThirdwebNextContext = { + params?: Record +}; diff --git a/packages/auth/src/next/index.ts b/packages/auth/src/next/index.ts index afe6edd6fd7..968f396937a 100644 --- a/packages/auth/src/next/index.ts +++ b/packages/auth/src/next/index.ts @@ -1,77 +1,21 @@ -import { Json, ThirdwebAuth as ThirdwebAuthSDK } from "../core"; -import { getUser } from "./helpers/user"; -import payloadHandler from "./routes/payload"; -import loginHandler from "./routes/login"; -import logoutHandler from "./routes/logout"; -import userHandler from "./routes/user"; -import switchAccountHandler from "./routes/switch-account"; -import { +// Next.js Pages Router support +// default and backward compatible +export { ThirdwebAuth } from './router-pages' +export type { ThirdwebAuthConfig, ThirdwebAuthContext, - ThirdwebAuthRoute, -} from "./types"; -import { NextRequest } from "next/server"; -import { - GetServerSidePropsContext, - NextApiRequest, - NextApiResponse, -} from "next/types"; - -export * from "./types"; - -async function ThirdwebAuthRouter( - req: NextApiRequest, - res: NextApiResponse, - ctx: ThirdwebAuthContext, -) { - // Catch-all route must be named with [...thirdweb] - const { thirdweb } = req.query; - const action = thirdweb?.[0] as ThirdwebAuthRoute; - - switch (action) { - case "payload": - return await payloadHandler(req, res, ctx); - case "login": - return await loginHandler(req, res, ctx); - case "user": - return await userHandler(req, res, ctx); - case "logout": - return await logoutHandler(req, res, ctx); - case "switch-account": - return await switchAccountHandler(req, res, ctx); - default: - return res.status(400).json({ - message: "Invalid route for authentication.", - }); - } -} +} from './router-pages/types' -export function ThirdwebAuth< - TData extends Json = Json, - TSession extends Json = Json, ->(cfg: ThirdwebAuthConfig) { - const ctx = { - ...cfg, - auth: new ThirdwebAuthSDK(cfg.wallet, cfg.domain), - }; +// Next.js App Router support +export { ThirdwebAuth as ThirdwebAuthAppRouter } from './router-app' +export type { + ThirdwebAuthConfig as ThirdwebAuthAppRouterConfig, + ThirdwebAuthContext as ThirdwebAuthAppRouterContext, +} from './router-app/types' - function ThirdwebAuthHandler( - ...args: [] | [NextApiRequest, NextApiResponse] - ) { - if (args.length === 0) { - return async (req: NextApiRequest, res: NextApiResponse) => - await ThirdwebAuthRouter(req, res, ctx as ThirdwebAuthContext); - } - - return ThirdwebAuthRouter(args[0], args[1], ctx as ThirdwebAuthContext); - } - - return { - ThirdwebAuthHandler, - getUser: ( - req: GetServerSidePropsContext["req"] | NextRequest | NextApiRequest, - ) => { - return getUser(req, ctx); - }, - }; -} +// common types +export type { + ThirdwebAuthRoute, + ThirdwebAuthUser, + ThirdwebNextContext, +} from './common/types' diff --git a/packages/auth/src/next/router-app/helpers/user.ts b/packages/auth/src/next/router-app/helpers/user.ts new file mode 100644 index 00000000000..ea8720df8c0 --- /dev/null +++ b/packages/auth/src/next/router-app/helpers/user.ts @@ -0,0 +1,75 @@ +import { cookies, headers } from "next/headers"; + +import { + THIRDWEB_AUTH_ACTIVE_ACCOUNT_COOKIE, + THIRDWEB_AUTH_TOKEN_COOKIE_PREFIX, +} from "../../../constants"; +import type { Json } from "../../../core"; +import type { ThirdwebAuthUser } from "../../common/types"; +import type { ThirdwebAuthContext } from "../types"; + +export function getCookie(cookie: string): string | undefined { + return cookies().get(cookie)?.value; +} + +export function getActiveCookie(): string | undefined { + const activeAccount = getCookie(THIRDWEB_AUTH_ACTIVE_ACCOUNT_COOKIE); + if (activeAccount) { + return `${THIRDWEB_AUTH_TOKEN_COOKIE_PREFIX}_${activeAccount}`; + } + + // If active account is not present, then use the old default + return THIRDWEB_AUTH_TOKEN_COOKIE_PREFIX; +} + +export function getToken(): string | undefined { + const headerList = headers(); + + if (headerList.has("authorization")) { + const authorizationHeader = headerList.get("authorization")?.split(" "); + if (authorizationHeader?.length === 2) { + return authorizationHeader[1]; + } + } + + const activeCookie = getActiveCookie(); + if (!activeCookie) { + return undefined; + } + + return getCookie(activeCookie); +} + +export async function getUser< + TData extends Json = Json, + TSession extends Json = Json, +>( + ctx: ThirdwebAuthContext +): Promise | null> { + const token = getToken(); + if (!token) { + return null; + } + + let authenticatedUser: ThirdwebAuthUser; + try { + authenticatedUser = await ctx.auth.authenticate(token, { + validateTokenId: async (tokenId: string) => { + if (ctx.authOptions?.validateTokenId) { + await ctx.authOptions?.validateTokenId(tokenId); + } + }, + }); + } catch (err) { + return null; + } + + if (ctx.callbacks?.onUser) { + const data = await ctx.callbacks.onUser(authenticatedUser); + if (data) { + return { ...authenticatedUser, data }; + } + } + + return authenticatedUser; +} diff --git a/packages/auth/src/next/router-app/index.ts b/packages/auth/src/next/router-app/index.ts new file mode 100644 index 00000000000..8eef2c0725f --- /dev/null +++ b/packages/auth/src/next/router-app/index.ts @@ -0,0 +1,76 @@ +import type { NextRequest } from "next/server"; + +import type { Json } from "../../core"; +import { ThirdwebAuth as ThirdwebAuthSDK } from "../../core"; +import { getUser } from "./helpers/user"; +import payloadHandler from "./routes/payload"; +import loginHandler from "./routes/login"; +import logoutHandler from "./routes/logout"; +import userHandler from "./routes/user"; +import switchAccountHandler from "./routes/switch-account"; +import type { + ThirdwebAuthConfig, + ThirdwebAuthContext, +} from "./types"; +import type { + ThirdwebAuthRoute, + ThirdwebNextContext, +} from "../common/types"; + +export type { + ThirdwebAuthConfig, + ThirdwebAuthContext, +} from "./types"; + +export async function ThirdwebAuthRouter( + req: NextRequest, + ctx: ThirdwebNextContext, + authCtx: ThirdwebAuthContext, +) { + // Catch-all route must be named with [...thirdweb] + const action = ctx.params?.thirdweb?.[0] as ThirdwebAuthRoute; + + switch (action) { + case "payload": + return await payloadHandler(req, authCtx); + case "login": + return await loginHandler(req, authCtx); + case "user": + return await userHandler(req, authCtx); + case "logout": + return await logoutHandler(req, authCtx); + case "switch-account": + return await switchAccountHandler(req, authCtx); + default: + return Response.json( + { message: "Invalid route for authentication." }, + { status: 400}, + ); + } +} + +export function ThirdwebAuth< + TData extends Json = Json, + TSession extends Json = Json, +>(cfg: ThirdwebAuthConfig) { + const authCtx = { + ...cfg, + auth: new ThirdwebAuthSDK(cfg.wallet, cfg.domain), + }; + + async function ThirdwebAuthHandler(...args: [] | [NextRequest, ThirdwebNextContext]) { + if (args.length === 0) { + return async (req: NextRequest, ctx: ThirdwebNextContext): Promise => + await ThirdwebAuthRouter(req, ctx, authCtx as ThirdwebAuthContext); + } + + return ThirdwebAuthRouter(args[0], args[1], authCtx as ThirdwebAuthContext); + } + + return { + ThirdwebAuthHandler, + getUser: async () => { + return await getUser(authCtx); + }, + }; +} diff --git a/packages/auth/src/next/router-app/routes/login.ts b/packages/auth/src/next/router-app/routes/login.ts new file mode 100644 index 00000000000..758d0fd6534 --- /dev/null +++ b/packages/auth/src/next/router-app/routes/login.ts @@ -0,0 +1,123 @@ +import type { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; + +import { + THIRDWEB_AUTH_ACTIVE_ACCOUNT_COOKIE, + THIRDWEB_AUTH_TOKEN_COOKIE_PREFIX, +} from "../../../constants"; +import type { GenerateOptionsWithOptionalDomain } from "../../../core"; +import type { ThirdwebAuthContext } from "../types"; +import { LoginPayloadBodySchema } from "../types"; + +export default async function handler( + req: NextRequest, + ctx: ThirdwebAuthContext, +) { + if (req.method !== "POST") { + return Response.json( + { error: "Invalid method. Only POST supported" }, + { status: 405 }, + ); + } + + const reqBody = await req.json(); + const parsedPayload = LoginPayloadBodySchema.safeParse(reqBody); + + // Get signed login payload from the frontend + if (!parsedPayload.success) { + return Response.json( + { error: "Invalid login payload" }, + { status: 400 }, + ); + } + + const payload = parsedPayload.data.payload; + + const validateNonce = async (nonce: string) => { + if (ctx.authOptions?.validateNonce) { + await ctx.authOptions?.validateNonce(nonce); + } + }; + + const getSession = async (address: string) => { + if (ctx.callbacks?.onLogin) { + return ctx.callbacks.onLogin(address); + } + }; + + const expirationTime = ctx.authOptions?.tokenDurationInSeconds + ? new Date(Date.now() + 1000 * ctx.authOptions.tokenDurationInSeconds) + : undefined; + + const generateOptions: GenerateOptionsWithOptionalDomain = { + verifyOptions: { + statement: ctx.authOptions?.statement, + uri: ctx.authOptions?.uri, + version: ctx.authOptions?.version, + chainId: ctx.authOptions?.chainId, + validateNonce, + resources: ctx.authOptions?.resources, + }, + expirationTime, + session: getSession, + }; + + let token: string; + try { + // Generate an access token with the SDK using the signed payload + token = await ctx.auth.generate(payload, generateOptions); + } catch (err: any) { + if (err.message) { + return Response.json( + { error: err.message }, + { status: 400 }, + ); + } else if (typeof err === "string") { + return Response.json( + { error: err }, + { status: 400 }, + ); + } else { + return Response.json( + { error: "Invalid login payload" }, + { status: 400 }, + ); + } + } + + if (ctx.callbacks?.onToken) { + await ctx.callbacks.onToken(token); + } + + const { + payload: { exp }, + } = ctx.auth.parseToken(token); + + // Securely set httpOnly cookie on request to prevent XSS on frontend + // And set path to / to enable thirdweb_auth_token usage on all endpoints + const response = NextResponse.json({ token }, { status: 200 }); + response.cookies.set({ + name: `${THIRDWEB_AUTH_TOKEN_COOKIE_PREFIX}_${payload.payload.address}`, + value: token, + domain: ctx.cookieOptions?.domain, + path: ctx.cookieOptions?.path || "/", + sameSite: ctx.cookieOptions?.sameSite || "none", + expires: new Date(exp * 1000), + httpOnly: true, + secure: ctx.cookieOptions?.secure || true, + }); + + response.cookies.set({ + name: THIRDWEB_AUTH_ACTIVE_ACCOUNT_COOKIE, + value: payload.payload.address, + domain: ctx.cookieOptions?.domain, + path: ctx.cookieOptions?.path || "/", + sameSite: ctx.cookieOptions?.sameSite || "none", + expires: new Date(exp * 1000), + httpOnly: true, + secure: ctx.cookieOptions?.secure || true, + }); + + // Send token in body and as cookie for frontend and backend use cases + return response; +} diff --git a/packages/auth/src/next/router-app/routes/logout.ts b/packages/auth/src/next/router-app/routes/logout.ts new file mode 100644 index 00000000000..7a015365fee --- /dev/null +++ b/packages/auth/src/next/router-app/routes/logout.ts @@ -0,0 +1,51 @@ +import type { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; + +import { getActiveCookie, getUser } from "../helpers/user"; +import type { ThirdwebAuthContext } from "../types"; + +export default async function handler( + req: NextRequest, + ctx: ThirdwebAuthContext, +) { + if (req.method !== "POST") { + return Response.json( + { error: "Invalid method. Only POST supported" }, + { status: 405 }, + ); + } + + const activeCookie = getActiveCookie(); + if (!activeCookie) { + return Response.json( + { error: "No logged in user to logout" }, + { status: 400 }, + ); + } + + if (ctx.callbacks?.onLogout) { + const user = await getUser(ctx); + if (user) { + await ctx.callbacks.onLogout(user); + } + } + + // Set the access token to 'none' and expire in 5 seconds + const response = NextResponse.json( + { message: "Successfully logged out" }, + { status: 200 }, + ); + + response.cookies.set({ + name: activeCookie, + value: '', + domain: ctx.cookieOptions?.domain, + path: ctx.cookieOptions?.path || "/", + sameSite: ctx.cookieOptions?.sameSite || "none", + expires: new Date(Date.now() + 5 * 1000), + httpOnly: true, + secure: ctx.cookieOptions?.secure || true, + }); + + return response; +} diff --git a/packages/auth/src/next/router-app/routes/payload.ts b/packages/auth/src/next/router-app/routes/payload.ts new file mode 100644 index 00000000000..501baebf1f7 --- /dev/null +++ b/packages/auth/src/next/router-app/routes/payload.ts @@ -0,0 +1,44 @@ +import type { NextRequest } from "next/server"; + +import type { ThirdwebAuthContext } from "../types"; +import { PayloadBodySchema } from "../types"; + +export default async function handler( + req: NextRequest, + ctx: ThirdwebAuthContext, +) { + if (req.method !== "POST") { + return Response.json( + { error: "Invalid method. Only POST supported" }, + { status: 405 }, + ); + } + + const reqBody = await req.json(); + const parsedPayload = PayloadBodySchema.safeParse(reqBody); + + if (!parsedPayload.success) { + return Response.json( + { error: "Please provide an address" }, + { status: 400 }, + ); + } + + // TODO: Add nonce generation + custom expiration + invalid before + const payload = await ctx.auth.payload({ + address: parsedPayload.data.address, + statement: ctx.authOptions?.statement, + uri: ctx.authOptions?.uri, + version: ctx.authOptions?.version, + chainId: parsedPayload.data.chainId || ctx.authOptions?.chainId, + resources: ctx.authOptions?.resources, + expirationTime: ctx.authOptions?.loginPayloadDurationInSeconds + ? new Date( + Date.now() + 1000 * ctx.authOptions.loginPayloadDurationInSeconds, + ) + : undefined, + }); + + const response = Response.json({ payload }, { status: 200 }); + return response; +} diff --git a/packages/auth/src/next/router-app/routes/switch-account.ts b/packages/auth/src/next/router-app/routes/switch-account.ts new file mode 100644 index 00000000000..8f9d874d130 --- /dev/null +++ b/packages/auth/src/next/router-app/routes/switch-account.ts @@ -0,0 +1,68 @@ +import type { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; + +import type { ThirdwebAuthContext } from "../types"; +import { ActiveBodySchema } from "../types"; +import { + THIRDWEB_AUTH_ACTIVE_ACCOUNT_COOKIE, + THIRDWEB_AUTH_DEFAULT_TOKEN_DURATION_IN_SECONDS, + THIRDWEB_AUTH_TOKEN_COOKIE_PREFIX, +} from "../../../constants"; +import { getCookie } from "../helpers/user"; + +export default async function handler( + req: NextRequest, + ctx: ThirdwebAuthContext, +) { + if (req.method !== "POST") { + return Response.json( + { error: "Invalid method. Only POST supported" }, + { status: 405 }, + ); + } + + const reqBody = await req.json(); + const parsedPayload = ActiveBodySchema.safeParse(reqBody); + + if (!parsedPayload.success) { + return Response.json( + { error: "Please provide an address" }, + { status: 400 }, + ); + } + + let cookieExpiration: Date; + + const cookie = getCookie(`${THIRDWEB_AUTH_TOKEN_COOKIE_PREFIX}_${parsedPayload.data.address}`); + if (cookie) { + // If the new account is already logged in, get the expiration time from the cookie + const { + payload: { exp }, + } = ctx.auth.parseToken(cookie); + cookieExpiration = new Date(exp * 1000); + } else if (ctx.authOptions?.tokenDurationInSeconds) { + // Otherwise, if we have a token duration in seconds, set it to that + cookieExpiration = new Date( + Date.now() + 1000 * ctx.authOptions.tokenDurationInSeconds, + ); + } else { + // Otherwise, just default to 24 hours + cookieExpiration = new Date( + Date.now() + 1000 * THIRDWEB_AUTH_DEFAULT_TOKEN_DURATION_IN_SECONDS, + ); + } + + const response = NextResponse.json('', { status: 200 }); + response.cookies.set({ + name: THIRDWEB_AUTH_ACTIVE_ACCOUNT_COOKIE, + value: parsedPayload.data.address, + domain: ctx.cookieOptions?.domain, + path: ctx.cookieOptions?.path || "/", + sameSite: ctx.cookieOptions?.sameSite || "none", + expires: cookieExpiration, + httpOnly: true, + secure: ctx.cookieOptions?.secure || true, + }); + + return response; +} diff --git a/packages/auth/src/next/router-app/routes/user.ts b/packages/auth/src/next/router-app/routes/user.ts new file mode 100644 index 00000000000..0da6d6266f3 --- /dev/null +++ b/packages/auth/src/next/router-app/routes/user.ts @@ -0,0 +1,76 @@ +import type { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; + +import { + THIRDWEB_AUTH_ACTIVE_ACCOUNT_COOKIE, + THIRDWEB_AUTH_DEFAULT_REFRESH_INTERVAL_IN_SECONDS, + THIRDWEB_AUTH_TOKEN_COOKIE_PREFIX, +} from "../../../constants"; +import { getToken, getUser } from "../helpers/user"; +import type { ThirdwebAuthContext } from "../types"; + +export default async function handler( + req: NextRequest, + ctx: ThirdwebAuthContext, +) { + if (req.method !== "GET") { + return Response.json( + { error: "Invalid method. Only GET supported" }, + { status: 405 }, + ); + } + + const user = await getUser(ctx); + + const response = NextResponse.json(user, { status: 200 }); + + // Importantly, make sure the user was actually logged in before refreshing + if (user) { + const token = getToken(); + if (token) { + const payload = ctx.auth.parseToken(token); + + const refreshDate = ctx.authOptions?.refreshIntervalInSeconds + ? new Date( + payload.payload.iat * 1000 + + ctx.authOptions.refreshIntervalInSeconds * 1000, + ) + : new Date( + payload.payload.iat * 1000 + + THIRDWEB_AUTH_DEFAULT_REFRESH_INTERVAL_IN_SECONDS * 1000, + ); + + if (new Date() > refreshDate) { + const expirationTime = ctx.authOptions?.tokenDurationInSeconds + ? new Date(Date.now() + 1000 * ctx.authOptions.tokenDurationInSeconds) + : undefined; + const refreshedToken = await ctx.auth.refresh(token, expirationTime); + const refreshedPayload = ctx.auth.parseToken(refreshedToken); + + response.cookies.set({ + name: `${THIRDWEB_AUTH_TOKEN_COOKIE_PREFIX}_${user.address}`, + value: refreshedToken, + domain: ctx.cookieOptions?.domain, + path: ctx.cookieOptions?.path || "/", + sameSite: ctx.cookieOptions?.sameSite || "none", + expires: new Date(refreshedPayload.payload.exp * 1000), + httpOnly: true, + secure: ctx.cookieOptions?.secure || true, + }); + + response.cookies.set({ + name: THIRDWEB_AUTH_ACTIVE_ACCOUNT_COOKIE, + value: user.address, + domain: ctx.cookieOptions?.domain, + path: ctx.cookieOptions?.path || "/", + sameSite: ctx.cookieOptions?.sameSite || "none", + expires: new Date(refreshedPayload.payload.exp * 1000), + httpOnly: true, + secure: ctx.cookieOptions?.secure || true, + }); + } + } + } + + return response; +} diff --git a/packages/auth/src/next/router-app/types/index.ts b/packages/auth/src/next/router-app/types/index.ts new file mode 100644 index 00000000000..277f5761bf5 --- /dev/null +++ b/packages/auth/src/next/router-app/types/index.ts @@ -0,0 +1,35 @@ +import type { Json, ThirdwebAuth, User } from "../../../core"; +import type { ThirdwebAuthConfigShared } from '../../common/types' + +export { + ActiveBodySchema, + LoginPayloadBodySchema, + PayloadBodySchema +} from '../../common/types' + +export type ThirdwebAuthConfig< + TData extends Json = Json, + TSession extends Json = Json, +> = ThirdwebAuthConfigShared & { + callbacks?: { + onLogin?: + | ((address: string) => void | TSession) + | ((address: string) => Promise); + onToken?: + | ((token: string) => void) + | ((token: string) => Promise); + onUser?: + | ((user: User) => void | TData) + | ((user: User) => Promise); + onLogout?: + | ((user: User) => void) + | ((user: User) => Promise); + }; +}; + +export type ThirdwebAuthContext< + TData extends Json = Json, + TSession extends Json = Json, +> = Omit, "wallet">, "domain"> & { + auth: ThirdwebAuth; +}; diff --git a/packages/auth/src/next/helpers/user.ts b/packages/auth/src/next/router-pages/helpers/user.ts similarity index 86% rename from packages/auth/src/next/helpers/user.ts rename to packages/auth/src/next/router-pages/helpers/user.ts index c9870861788..ae3177de7bd 100644 --- a/packages/auth/src/next/helpers/user.ts +++ b/packages/auth/src/next/router-pages/helpers/user.ts @@ -1,18 +1,19 @@ import { THIRDWEB_AUTH_ACTIVE_ACCOUNT_COOKIE, THIRDWEB_AUTH_TOKEN_COOKIE_PREFIX, -} from "../../constants"; -import { Json } from "../../core"; -import { ThirdwebAuthContext, ThirdwebAuthUser } from "../types"; -import { GetServerSidePropsContext, NextApiRequest } from "next"; -import { NextRequest } from "next/server"; +} from "../../../constants"; +import type { Json } from "../../../core"; +import type { ThirdwebAuthContext } from "../types"; +import type { ThirdwebAuthUser } from "../../common/types"; +import type { GetServerSidePropsContext, NextApiRequest } from "next"; +import type { NextRequest } from "next/server"; export function getCookie( req: GetServerSidePropsContext["req"] | NextRequest | NextApiRequest, cookie: string, ): string | undefined { if (typeof req.cookies.get === "function") { - return req.cookies.get(cookie); + return req.cookies.get(cookie)?.value; } return (req.cookies as any)[cookie]; diff --git a/packages/auth/src/next/router-pages/index.ts b/packages/auth/src/next/router-pages/index.ts new file mode 100644 index 00000000000..05b6c805879 --- /dev/null +++ b/packages/auth/src/next/router-pages/index.ts @@ -0,0 +1,77 @@ +import { Json, ThirdwebAuth as ThirdwebAuthSDK } from "../../core"; +import { getUser } from "./helpers/user"; +import payloadHandler from "./routes/payload"; +import loginHandler from "./routes/login"; +import logoutHandler from "./routes/logout"; +import userHandler from "./routes/user"; +import switchAccountHandler from "./routes/switch-account"; +import { + ThirdwebAuthConfig, + ThirdwebAuthContext, + ThirdwebAuthRoute, +} from "./types"; +import { NextRequest } from "next/server"; +import { + GetServerSidePropsContext, + NextApiRequest, + NextApiResponse, +} from "next/types"; + +export * from "./types"; + +async function ThirdwebAuthRouter( + req: NextApiRequest, + res: NextApiResponse, + ctx: ThirdwebAuthContext, +) { + // Catch-all route must be named with [...thirdweb] + const { thirdweb } = req.query; + const action = thirdweb?.[0] as ThirdwebAuthRoute; + + switch (action) { + case "payload": + return await payloadHandler(req, res, ctx); + case "login": + return await loginHandler(req, res, ctx); + case "user": + return await userHandler(req, res, ctx); + case "logout": + return await logoutHandler(req, res, ctx); + case "switch-account": + return await switchAccountHandler(req, res, ctx); + default: + return res.status(400).json({ + message: "Invalid route for authentication.", + }); + } +} + +export function ThirdwebAuth< + TData extends Json = Json, + TSession extends Json = Json, +>(cfg: ThirdwebAuthConfig) { + const ctx = { + ...cfg, + auth: new ThirdwebAuthSDK(cfg.wallet, cfg.domain), + }; + + function ThirdwebAuthHandler( + ...args: [] | [NextApiRequest, NextApiResponse] + ) { + if (args.length === 0) { + return async (req: NextApiRequest, res: NextApiResponse) => + await ThirdwebAuthRouter(req, res, ctx as ThirdwebAuthContext); + } + + return ThirdwebAuthRouter(args[0], args[1], ctx as ThirdwebAuthContext); + } + + return { + ThirdwebAuthHandler, + getUser: ( + req: GetServerSidePropsContext["req"] | NextRequest | NextApiRequest, + ) => { + return getUser(req, ctx); + }, + }; +} diff --git a/packages/auth/src/next/routes/login.ts b/packages/auth/src/next/router-pages/routes/login.ts similarity index 92% rename from packages/auth/src/next/routes/login.ts rename to packages/auth/src/next/router-pages/routes/login.ts index 262cbb6eca5..75708da356e 100644 --- a/packages/auth/src/next/routes/login.ts +++ b/packages/auth/src/next/router-pages/routes/login.ts @@ -1,11 +1,12 @@ import { THIRDWEB_AUTH_ACTIVE_ACCOUNT_COOKIE, THIRDWEB_AUTH_TOKEN_COOKIE_PREFIX, -} from "../../constants"; -import { GenerateOptionsWithOptionalDomain } from "../../core"; -import { LoginPayloadBodySchema, ThirdwebAuthContext } from "../types"; +} from "../../../constants"; +import type { GenerateOptionsWithOptionalDomain } from "../../../core"; +import type { ThirdwebAuthContext } from "../types"; +import { LoginPayloadBodySchema } from "../types"; import { serialize } from "cookie"; -import { NextApiRequest, NextApiResponse } from "next"; +import type { NextApiRequest, NextApiResponse } from "next"; export default async function handler( req: NextApiRequest, diff --git a/packages/auth/src/next/routes/logout.ts b/packages/auth/src/next/router-pages/routes/logout.ts similarity index 83% rename from packages/auth/src/next/routes/logout.ts rename to packages/auth/src/next/router-pages/routes/logout.ts index 5fccb39c194..56134ad2b72 100644 --- a/packages/auth/src/next/routes/logout.ts +++ b/packages/auth/src/next/router-pages/routes/logout.ts @@ -1,7 +1,7 @@ import { getActiveCookie, getUser } from "../helpers/user"; -import { ThirdwebAuthContext } from "../types"; +import type { ThirdwebAuthContext } from "../types"; import { serialize } from "cookie"; -import { NextApiRequest, NextApiResponse } from "next"; +import type { NextApiRequest, NextApiResponse } from "next"; export default async function handler( req: NextApiRequest, @@ -10,14 +10,14 @@ export default async function handler( ) { if (req.method !== "POST") { return res.status(405).json({ - error: "Invalid method. Only POST supported.", + error: "Invalid method. Only POST supported", }); } const activeCookie = getActiveCookie(req); if (!activeCookie) { return res.status(400).json({ - error: "No logged in user to logout.", + error: "No logged in user to logout", }); } diff --git a/packages/auth/src/next/routes/payload.ts b/packages/auth/src/next/router-pages/routes/payload.ts similarity index 82% rename from packages/auth/src/next/routes/payload.ts rename to packages/auth/src/next/router-pages/routes/payload.ts index 15e22927488..bcfd1a95237 100644 --- a/packages/auth/src/next/routes/payload.ts +++ b/packages/auth/src/next/router-pages/routes/payload.ts @@ -1,5 +1,6 @@ -import { PayloadBodySchema, ThirdwebAuthContext } from "../types"; -import { NextApiRequest, NextApiResponse } from "next"; +import type { ThirdwebAuthContext } from "../types"; +import { PayloadBodySchema } from "../types"; +import type { NextApiRequest, NextApiResponse } from "next"; export default async function handler( req: NextApiRequest, @@ -8,7 +9,7 @@ export default async function handler( ) { if (req.method !== "POST") { return res.status(405).json({ - error: "Invalid method. Only POST supported.", + error: "Invalid method. Only POST supported", }); } diff --git a/packages/auth/src/next/routes/switch-account.ts b/packages/auth/src/next/router-pages/routes/switch-account.ts similarity index 88% rename from packages/auth/src/next/routes/switch-account.ts rename to packages/auth/src/next/router-pages/routes/switch-account.ts index b368a485c60..70af5e8533a 100644 --- a/packages/auth/src/next/routes/switch-account.ts +++ b/packages/auth/src/next/router-pages/routes/switch-account.ts @@ -1,12 +1,13 @@ import { serialize } from "cookie"; import { getCookie } from "../helpers/user"; -import { ActiveBodySchema, ThirdwebAuthContext } from "../types"; -import { NextApiRequest, NextApiResponse } from "next"; +import type { ThirdwebAuthContext } from "../types"; +import { ActiveBodySchema } from "../types"; +import type { NextApiRequest, NextApiResponse } from "next"; import { THIRDWEB_AUTH_ACTIVE_ACCOUNT_COOKIE, THIRDWEB_AUTH_DEFAULT_TOKEN_DURATION_IN_SECONDS, THIRDWEB_AUTH_TOKEN_COOKIE_PREFIX, -} from "../../constants"; +} from "../../../constants"; export default async function handler( req: NextApiRequest, @@ -15,7 +16,7 @@ export default async function handler( ) { if (req.method !== "POST") { return res.status(405).json({ - error: "Invalid method. Only POST supported.", + error: "Invalid method. Only POST supported", }); } diff --git a/packages/auth/src/next/routes/user.ts b/packages/auth/src/next/router-pages/routes/user.ts similarity index 91% rename from packages/auth/src/next/routes/user.ts rename to packages/auth/src/next/router-pages/routes/user.ts index 7e7128b89ff..344af24d8c4 100644 --- a/packages/auth/src/next/routes/user.ts +++ b/packages/auth/src/next/router-pages/routes/user.ts @@ -3,10 +3,10 @@ import { THIRDWEB_AUTH_ACTIVE_ACCOUNT_COOKIE, THIRDWEB_AUTH_DEFAULT_REFRESH_INTERVAL_IN_SECONDS, THIRDWEB_AUTH_TOKEN_COOKIE_PREFIX, -} from "../../constants"; +} from "../../../constants"; import { getToken, getUser } from "../helpers/user"; -import { ThirdwebAuthContext } from "../types"; -import { NextApiRequest, NextApiResponse } from "next"; +import type { ThirdwebAuthContext } from "../types"; +import type { NextApiRequest, NextApiResponse } from "next"; export default async function handler( req: NextApiRequest, @@ -14,8 +14,8 @@ export default async function handler( ctx: ThirdwebAuthContext, ) { if (req.method !== "GET") { - return res.status(400).json({ - error: "Invalid method. Only GET supported.", + return res.status(405).json({ + error: "Invalid method. Only GET supported", }); } diff --git a/packages/auth/src/next/router-pages/types/index.ts b/packages/auth/src/next/router-pages/types/index.ts new file mode 100644 index 00000000000..3d0babb54c8 --- /dev/null +++ b/packages/auth/src/next/router-pages/types/index.ts @@ -0,0 +1,48 @@ +import type { Json, ThirdwebAuth, User } from "../../../core"; +import type { GetServerSidePropsContext, NextApiRequest } from "next"; +import type { NextRequest } from "next/server"; +import type { ThirdwebAuthConfigShared } from '../../common/types' + +export { + ActiveBodySchema, + LoginPayloadBodySchema, + PayloadBodySchema +} from '../../common/types' + +type RequestType = + | GetServerSidePropsContext["req"] + | NextRequest + | NextApiRequest; + +export type ThirdwebAuthConfig< + TData extends Json = Json, + TSession extends Json = Json, +> = ThirdwebAuthConfigShared & { + callbacks?: { + onLogin?: + | ((address: string, req: NextApiRequest) => void | TSession) + | ((address: string, req: NextApiRequest) => Promise); + onToken?: + | ((token: string, req: NextApiRequest) => void) + | ((token: string, req: NextApiRequest) => Promise); + onUser?: + | (( + user: User, + req: TRequestType, + ) => void | TData) + | (( + user: User, + req: TRequestType, + ) => Promise); + onLogout?: + | ((user: User, req: NextApiRequest) => void) + | ((user: User, req: NextApiRequest) => Promise); + }; +}; + +export type ThirdwebAuthContext< + TData extends Json = Json, + TSession extends Json = Json, +> = Omit, "wallet">, "domain"> & { + auth: ThirdwebAuth; +}; diff --git a/packages/auth/src/next/types/index.ts b/packages/auth/src/next/types/index.ts deleted file mode 100644 index 9831700710e..00000000000 --- a/packages/auth/src/next/types/index.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { Json, LoginPayloadOutputSchema, ThirdwebAuth, User } from "../../core"; -import type { GenericAuthWallet } from "@thirdweb-dev/wallets"; -import { GetServerSidePropsContext, NextApiRequest } from "next"; -import { NextRequest } from "next/server"; -import { z } from "zod"; - -export const PayloadBodySchema = z.object({ - address: z.string(), - chainId: z.string().optional(), -}); - -export const ActiveBodySchema = z.object({ - address: z.string(), -}); - -export const LoginPayloadBodySchema = z.object({ - payload: LoginPayloadOutputSchema, -}); - -type RequestType = - | GetServerSidePropsContext["req"] - | NextRequest - | NextApiRequest; - -export type ThirdwebAuthRoute = - | "payload" - | "login" - | "logout" - | "user" - | "switch-account"; - -export type ThirdwebAuthUser< - TData extends Json = Json, - TSession extends Json = Json, -> = User & { - data?: TData; -}; - -export type ThirdwebAuthConfig< - TData extends Json = Json, - TSession extends Json = Json, -> = { - domain: string; - wallet: GenericAuthWallet; - authOptions?: { - statement?: string; - uri?: string; - version?: string; - chainId?: string; - resources?: string[]; - validateNonce?: - | ((nonce: string) => void) - | ((nonce: string) => Promise); - validateTokenId?: - | ((tokenId: string) => void) - | ((tokenId: string) => Promise); - loginPayloadDurationInSeconds?: number; - tokenDurationInSeconds?: number; - refreshIntervalInSeconds?: number; - }; - cookieOptions?: { - domain?: string; - path?: string; - sameSite?: "lax" | "strict" | "none"; - secure?: boolean; - }; - callbacks?: { - onLogin?: - | ((address: string, req: NextApiRequest) => void | TSession) - | ((address: string, req: NextApiRequest) => Promise); - onToken?: - | ((token: string, req: NextApiRequest) => void) - | ((token: string, req: NextApiRequest) => Promise); - onUser?: - | (( - user: User, - req: TRequestType, - ) => void | TData) - | (( - user: User, - req: TRequestType, - ) => Promise); - onLogout?: - | ((user: User, req: NextApiRequest) => void) - | ((user: User, req: NextApiRequest) => Promise); - }; -}; - -export type ThirdwebAuthContext< - TData extends Json = Json, - TSession extends Json = Json, -> = Omit, "wallet">, "domain"> & { - auth: ThirdwebAuth; -}; diff --git a/packages/wallets/src/evm/wallets/abstract.ts b/packages/wallets/src/evm/wallets/abstract.ts index 26c457cbc20..5a47087dfd2 100644 --- a/packages/wallets/src/evm/wallets/abstract.ts +++ b/packages/wallets/src/evm/wallets/abstract.ts @@ -40,7 +40,7 @@ export interface WalletEvents { } const EIP1271_ABI = [ - "function isValidSignature(bytes32 _message, bytes _signature) public view returns (bytes4)", + "function isValidSignature(bytes32 _hash, bytes _signature) public view returns (bytes4)", ]; const EIP1271_MAGICVALUE = "0x1626ba7e"; @@ -50,8 +50,21 @@ export async function checkContractWalletSignature( address: string, chainId: number, ): Promise { - //TODO: A provider should be passed in instead of creating a new one here. - const provider = new providers.JsonRpcProvider(chainIdToThirdwebRpc(chainId)); + // TODO: remove below `skipFetchSetup` logic when ethers.js v6 support arrives + let _skipFetchSetup = false; + if ( + typeof globalThis !== "undefined" && + "TW_SKIP_FETCH_SETUP" in globalThis && + typeof (globalThis as any).TW_SKIP_FETCH_SETUP === "boolean" + ) { + _skipFetchSetup = (globalThis as any).TW_SKIP_FETCH_SETUP as boolean; + } + + //TODO: A provider should be passed in instead of creating a new one here. + const provider = new providers.JsonRpcProvider({ + url: chainIdToThirdwebRpc(chainId), + skipFetchSetup: _skipFetchSetup, + }); const walletContract = new Contract(address, EIP1271_ABI, provider); const _hashMessage = utils.hashMessage(message); try { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 455ad4c7eed..d74fb671b9c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -184,11 +184,11 @@ importers: specifier: ^10.2.0 version: 10.2.0 next: - specifier: ^12.3.4 - version: 12.3.4(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0) + specifier: ^13.4 + version: 13.4.12(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0) next-auth: specifier: ^4.22.3 - version: 4.22.3(next@12.3.4)(react-dom@18.2.0)(react@18.2.0) + version: 4.22.3(next@13.4.12)(react-dom@18.2.0)(react@18.2.0) prettier: specifier: ^3.1.1 version: 3.1.1 @@ -1837,7 +1837,7 @@ importers: version: 5.7.2 next: specifier: ^13.4.12 - version: 13.4.12(react-dom@18.2.0)(react@18.2.0) + version: 13.4.12(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0) react: specifier: ^18.2.0 version: 18.2.0 @@ -9635,62 +9635,20 @@ packages: resolution: {integrity: sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw==} dev: false - /@next/env@12.3.4: - resolution: {integrity: sha512-H/69Lc5Q02dq3o+dxxy5O/oNxFsZpdL6WREtOOtOM1B/weonIwDXkekr1KV5DPVPr12IHFPrMrcJQ6bgPMfn7A==} - dev: true - /@next/env@13.4.12: resolution: {integrity: sha512-RmHanbV21saP/6OEPBJ7yJMuys68cIf8OBBWd7+uj40LdpmswVAwe1uzeuFyUsd6SfeITWT3XnQfn6wULeKwDQ==} - dev: false /@next/eslint-plugin-next@13.4.12: resolution: {integrity: sha512-6rhK9CdxEgj/j1qvXIyLTWEaeFv7zOK8yJMulz3Owel0uek0U9MJCGzmKgYxM3aAUBo3gKeywCZKyQnJKto60A==} dependencies: glob: 7.1.7 - /@next/swc-android-arm-eabi@12.3.4: - resolution: {integrity: sha512-cM42Cw6V4Bz/2+j/xIzO8nK/Q3Ly+VSlZJTa1vHzsocJRYz8KT6MrreXaci2++SIZCF1rVRCDgAg5PpqRibdIA==} - engines: {node: '>= 10'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@next/swc-android-arm64@12.3.4: - resolution: {integrity: sha512-5jf0dTBjL+rabWjGj3eghpLUxCukRhBcEJgwLedewEA/LJk2HyqCvGIwj5rH+iwmq1llCWbOky2dO3pVljrapg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@next/swc-darwin-arm64@12.3.4: - resolution: {integrity: sha512-DqsSTd3FRjQUR6ao0E1e2OlOcrF5br+uegcEGPVonKYJpcr0MJrtYmPxd4v5T6UCJZ+XzydF7eQo5wdGvSZAyA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - /@next/swc-darwin-arm64@13.4.12: resolution: {integrity: sha512-deUrbCXTMZ6ZhbOoloqecnUeNpUOupi8SE2tx4jPfNS9uyUR9zK4iXBvH65opVcA/9F5I/p8vDXSYbUlbmBjZg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] requiresBuild: true - dev: false - optional: true - - /@next/swc-darwin-x64@12.3.4: - resolution: {integrity: sha512-PPF7tbWD4k0dJ2EcUSnOsaOJ5rhT3rlEt/3LhZUGiYNL8KvoqczFrETlUx0cUYaXe11dRA3F80Hpt727QIwByQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true optional: true /@next/swc-darwin-x64@13.4.12: @@ -9699,34 +9657,6 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true - dev: false - optional: true - - /@next/swc-freebsd-x64@12.3.4: - resolution: {integrity: sha512-KM9JXRXi/U2PUM928z7l4tnfQ9u8bTco/jb939pdFUHqc28V43Ohd31MmZD1QzEK4aFlMRaIBQOWQZh4D/E5lQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /@next/swc-linux-arm-gnueabihf@12.3.4: - resolution: {integrity: sha512-3zqD3pO+z5CZyxtKDTnOJ2XgFFRUBciOox6EWkoZvJfc9zcidNAQxuwonUeNts6Xbm8Wtm5YGIRC0x+12YH7kw==} - engines: {node: '>= 10'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@next/swc-linux-arm64-gnu@12.3.4: - resolution: {integrity: sha512-kiX0vgJGMZVv+oo1QuObaYulXNvdH/IINmvdZnVzMO/jic/B8EEIGlZ8Bgvw8LCjH3zNVPO3mGrdMvnEEPEhKA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true optional: true /@next/swc-linux-arm64-gnu@13.4.12: @@ -9735,16 +9665,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: false - optional: true - - /@next/swc-linux-arm64-musl@12.3.4: - resolution: {integrity: sha512-EETZPa1juczrKLWk5okoW2hv7D7WvonU+Cf2CgsSoxgsYbUCZ1voOpL4JZTOb6IbKMDo6ja+SbY0vzXZBUMvkQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true optional: true /@next/swc-linux-arm64-musl@13.4.12: @@ -9753,16 +9673,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: false - optional: true - - /@next/swc-linux-x64-gnu@12.3.4: - resolution: {integrity: sha512-4csPbRbfZbuWOk3ATyWcvVFdD9/Rsdq5YHKvRuEni68OCLkfy4f+4I9OBpyK1SKJ00Cih16NJbHE+k+ljPPpag==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true optional: true /@next/swc-linux-x64-gnu@13.4.12: @@ -9771,16 +9681,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: false - optional: true - - /@next/swc-linux-x64-musl@12.3.4: - resolution: {integrity: sha512-YeBmI+63Ro75SUiL/QXEVXQ19T++58aI/IINOyhpsRL1LKdyfK/35iilraZEFz9bLQrwy1LYAR5lK200A9Gjbg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true optional: true /@next/swc-linux-x64-musl@13.4.12: @@ -9789,16 +9689,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: false - optional: true - - /@next/swc-win32-arm64-msvc@12.3.4: - resolution: {integrity: sha512-Sd0qFUJv8Tj0PukAYbCCDbmXcMkbIuhnTeHm9m4ZGjCf6kt7E/RMs55Pd3R5ePjOkN7dJEuxYBehawTR/aPDSQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true optional: true /@next/swc-win32-arm64-msvc@13.4.12: @@ -9807,16 +9697,6 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true - dev: false - optional: true - - /@next/swc-win32-ia32-msvc@12.3.4: - resolution: {integrity: sha512-rt/vv/vg/ZGGkrkKcuJ0LyliRdbskQU+91bje+PgoYmxTZf/tYs6IfbmgudBJk6gH3QnjHWbkphDdRQrseRefQ==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true optional: true /@next/swc-win32-ia32-msvc@13.4.12: @@ -9825,16 +9705,6 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true - dev: false - optional: true - - /@next/swc-win32-x64-msvc@12.3.4: - resolution: {integrity: sha512-DQ20JEfTBZAgF8QCjYfJhv2/279M6onxFjdG/+5B0Cyj00/EdBxiWb2eGGFgQhrBbNv/lsvzFbbi0Ptf8Vw/bg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true optional: true /@next/swc-win32-x64-msvc@13.4.12: @@ -9843,7 +9713,6 @@ packages: cpu: [x64] os: [win32] requiresBuild: true - dev: false optional: true /@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1: @@ -12700,17 +12569,10 @@ packages: resolution: {integrity: sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==} dev: true - /@swc/helpers@0.4.11: - resolution: {integrity: sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw==} - dependencies: - tslib: 2.5.0 - dev: true - /@swc/helpers@0.5.1: resolution: {integrity: sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==} dependencies: tslib: 2.5.0 - dev: false /@swc/types@0.1.5: resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==} @@ -15016,7 +14878,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.21.5 - caniuse-lite: 1.0.30001517 + caniuse-lite: 1.0.30001576 fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -15861,7 +15723,7 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001517 + caniuse-lite: 1.0.30001576 electron-to-chromium: 1.4.475 node-releases: 2.0.13 update-browserslist-db: 1.0.11(browserslist@4.21.5) @@ -15872,7 +15734,7 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001517 + caniuse-lite: 1.0.30001576 electron-to-chromium: 1.4.475 node-releases: 2.0.13 update-browserslist-db: 1.0.11(browserslist@4.21.9) @@ -16213,9 +16075,6 @@ packages: /caniuse-lite@1.0.30001466: resolution: {integrity: sha512-ewtFBSfWjEmxUgNBSZItFSmVtvk9zkwkl1OfRZlKA8slltRN+/C/tuGVrF9styXkN36Yu3+SeJ1qkXxDEyNZ5w==} - /caniuse-lite@1.0.30001517: - resolution: {integrity: sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==} - /caniuse-lite@1.0.30001576: resolution: {integrity: sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg==} @@ -16477,7 +16336,6 @@ packages: /client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} - dev: false /clipboardy@3.0.0: resolution: {integrity: sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==} @@ -24861,7 +24719,7 @@ packages: resolution: {integrity: sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==} dev: false - /next-auth@4.22.3(next@12.3.4)(react-dom@18.2.0)(react@18.2.0): + /next-auth@4.22.3(next@13.4.12)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-XAgy9xV3J2eJOXrQhmxdjV6MLM29ibm6WtMXc3KY6IPZeApf+SuBuPvlqCUfbu5YsAzlg9WSw6u01dChTfeZOA==} peerDependencies: next: ^12.2.5 || ^13 @@ -24876,7 +24734,7 @@ packages: '@panva/hkdf': 1.0.4 cookie: 0.5.0 jose: 4.13.1 - next: 12.3.4(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0) + next: 13.4.12(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0) oauth: 0.9.15 openid-client: 5.4.0 preact: 10.13.1 @@ -24890,52 +24748,7 @@ packages: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} dev: false - /next@12.3.4(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-VcyMJUtLZBGzLKo3oMxrEF0stxh8HwuW976pAzlHhI3t8qJ4SROjCrSh1T24bhrbjw55wfZXAbXPGwPt5FLRfQ==} - engines: {node: '>=12.22.0'} - hasBin: true - peerDependencies: - fibers: '>= 3.1.0' - node-sass: ^6.0.0 || ^7.0.0 - react: ^17.0.2 || ^18.0.0-0 - react-dom: ^17.0.2 || ^18.0.0-0 - sass: ^1.3.0 - peerDependenciesMeta: - fibers: - optional: true - node-sass: - optional: true - sass: - optional: true - dependencies: - '@next/env': 12.3.4 - '@swc/helpers': 0.4.11 - caniuse-lite: 1.0.30001466 - postcss: 8.4.14 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - styled-jsx: 5.0.7(@babel/core@7.23.7)(react@18.2.0) - use-sync-external-store: 1.2.0(react@18.2.0) - optionalDependencies: - '@next/swc-android-arm-eabi': 12.3.4 - '@next/swc-android-arm64': 12.3.4 - '@next/swc-darwin-arm64': 12.3.4 - '@next/swc-darwin-x64': 12.3.4 - '@next/swc-freebsd-x64': 12.3.4 - '@next/swc-linux-arm-gnueabihf': 12.3.4 - '@next/swc-linux-arm64-gnu': 12.3.4 - '@next/swc-linux-arm64-musl': 12.3.4 - '@next/swc-linux-x64-gnu': 12.3.4 - '@next/swc-linux-x64-musl': 12.3.4 - '@next/swc-win32-arm64-msvc': 12.3.4 - '@next/swc-win32-ia32-msvc': 12.3.4 - '@next/swc-win32-x64-msvc': 12.3.4 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - dev: true - - /next@13.4.12(react-dom@18.2.0)(react@18.2.0): + /next@13.4.12(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-eHfnru9x6NRmTMcjQp6Nz0J4XH9OubmzOa7CkWL+AUrUxpibub3vWwttjduu9No16dug1kq04hiUUpo7J3m3Xw==} engines: {node: '>=16.8.0'} hasBin: true @@ -24960,7 +24773,7 @@ packages: postcss: 8.4.14 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - styled-jsx: 5.1.1(react@18.2.0) + styled-jsx: 5.1.1(@babel/core@7.23.7)(react@18.2.0) watchpack: 2.4.0 zod: 3.21.4 optionalDependencies: @@ -24976,7 +24789,6 @@ packages: transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - dev: false /nice-try@1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} @@ -29427,24 +29239,7 @@ packages: webpack: 5.76.2 dev: false - /styled-jsx@5.0.7(@babel/core@7.23.7)(react@18.2.0): - resolution: {integrity: sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' - peerDependenciesMeta: - '@babel/core': - optional: true - babel-plugin-macros: - optional: true - dependencies: - '@babel/core': 7.23.7 - react: 18.2.0 - dev: true - - /styled-jsx@5.1.1(react@18.2.0): + /styled-jsx@5.1.1(@babel/core@7.23.7)(react@18.2.0): resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} peerDependencies: @@ -29457,9 +29252,9 @@ packages: babel-plugin-macros: optional: true dependencies: + '@babel/core': 7.23.7 client-only: 0.0.1 react: 18.2.0 - dev: false /stylehacks@5.1.1(postcss@8.4.21): resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} @@ -32288,7 +32083,6 @@ packages: /zod@3.21.4: resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} - dev: false /zod@3.22.3: resolution: {integrity: sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==}