diff --git a/packages/auth/src/config.ts b/packages/auth/src/config.ts index 95f8abd..09d6234 100644 --- a/packages/auth/src/config.ts +++ b/packages/auth/src/config.ts @@ -95,14 +95,27 @@ export const authConfig: NextAuthConfig = { // @ts-ignore const { createTransport } = await import("nodemailer"); const transport = createTransport(provider.server); - const { host } = new URL(url); + + // Parse the NextAuth callback URL to extract token and callbackUrl + const parsedUrl = new URL(url); + const host = parsedUrl.host; + const token = parsedUrl.searchParams.get("token") || ""; + const callbackUrl = parsedUrl.searchParams.get("callbackUrl") || "/dashboard"; + + // Build an intermediate /verify URL that prevents email scanners + // from consuming the one-time token via pre-fetch GET requests + const verifyUrl = new URL("/verify", parsedUrl.origin); + verifyUrl.searchParams.set("token", token); + verifyUrl.searchParams.set("email", identifier); + verifyUrl.searchParams.set("callbackUrl", callbackUrl); + const safeUrl = verifyUrl.toString(); const result = await transport.sendMail({ to: identifier, from: provider.from, subject: `Sign in to ${host}`, - text: `Sign in to ${host}\n${url}\n\n`, - html: html({ url, host }), + text: `Sign in to ${host}\n${safeUrl}\n\n`, + html: html({ url: safeUrl, host }), }); const failed = result.rejected.concat(result.pending).filter(Boolean); diff --git a/sites/mainweb/app/(portal)/verify/page.tsx b/sites/mainweb/app/(portal)/verify/page.tsx new file mode 100644 index 0000000..b489255 --- /dev/null +++ b/sites/mainweb/app/(portal)/verify/page.tsx @@ -0,0 +1,90 @@ +'use client'; + +import React, { Suspense, useState } from 'react'; +import { useSearchParams } from 'next/navigation'; +import Background from '@/components/portal/Background'; + +function VerifyContent() { + const searchParams = useSearchParams(); + const [verifying, setVerifying] = useState(false); + + // Build the actual NextAuth callback URL from the search params + const callbackUrl = searchParams?.get('callbackUrl') || '/dashboard'; + const token = searchParams?.get('token') || ''; + const email = searchParams?.get('email') || ''; + + const handleVerify = () => { + setVerifying(true); + // Redirect to the actual NextAuth email callback + const params = new URLSearchParams({ + callbackUrl, + token, + email, + }); + window.location.href = `/api/auth/callback/nodemailer?${params.toString()}`; + }; + + return ( +
+ Secure_Authentication // Email_Verification +
+ ++ Click the button below to complete your sign-in. +
+ {email && ( ++ {email} +
+ )} ++ Error: No verification token found. Please request a new sign-in link. +
+ )} + ++ Query_Security_Protocols_Active +
+