Nachdem wir im ersten Teil dieses Blogs untersucht haben, wie ein Authentifizierungssystem in Next.js 14 mit NextAuth.js (Auth.js) implementiert wird, ist es wichtig, den nächsten Schritt zu unternehmen, um die Gültigkeit der Benutzerinformationen sicherzustellen: die E-Mail-Validierung.
Dieser Prozess ist nicht nur ein zusätzlicher Schritt zur Sicherheit unserer Anwendung, sondern auch ein wesentlicher Bestandteil, um sicherzustellen, dass die Interaktionen zwischen dem Benutzer und der Plattform legitim und sicher sind.
In diesem zweiten Teil konzentrieren wir uns auf die Integration der E-Mail-Validierung durch das Senden von E-Mails, die Verwendung von Resend zum Senden von E-Mails und React Email zum Erstellen attraktiver und funktionaler E-Mail-Vorlagen.
Stellen Sie sicher, dass in Ihrem Projekt bereits das im ersten Teil des Blogs beschriebene Authentifizierungssystem implementiert ist. Dazu gehört, dass Next.js 14 und NextAuth richtig konfiguriert sind.
Installieren Sie die im Projekt benötigten Abhängigkeiten. Dieses Mal verwenden wir pnpm
Sie können den Paketmanager Ihrer Wahl verwenden.
pnpm add resend react-email @react-email/components
2. Erstellen Sie folgende Struktur für das Projekt:
... ├── emails/ │ └── verification-template.tsx ... ├── src/ │ ├── actions/ │ │ ├── email-actions.tsx │ │ └── auth-actions.tsx │ ├── app/ │ │ ... │ │ ├── (primary)/ │ │ │ ├── auth/ │ │ │ │ └── verify-email/ │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ │ ... │ ├── components/ │ │ └── auth/ │ │ ├── signin-form.tsx │ │ ├── signup-form.tsx │ │ ... │ ... │ ├── utils.ts │ ... ... ├── .env ...
Mit React Email können Sie E-Mail-Vorlagen mit JSX erstellen, was die Erstellung attraktiver und konsistenter E-Mails mit Ihrer Marke erleichtert.
Lassen Sie uns eine einfache E-Mail-Vorlage als React-Komponente erstellen. In diesem Fall erstellen wir die Vorlage, die dem Benutzer zur Bestätigung seiner E-Mail gesendet wird.
emails/verification-template.tsx
:
// Import React and necessary components from @react-email/components import * as React from 'react'; import { Body, Button, Container, Head, Hr, Html, Img, Preview, Section, Text } from '@react-email/components'; import { getBaseUrl } from '@/utils'; // Obtain the base URL using the imported function const baseUrl = getBaseUrl(); // Define the properties expected by the VerificationTemplate component interface VerificationTemplateProps { username: string; emailVerificationToken: string; } // Define the VerificationTemplate component that takes the defined properties export const VerificationTemplate = ({ username, emailVerificationToken }: VerificationTemplateProps) => ( <Html> <Head /> <Preview>Preview text that appears in the email client before opening the email.</Preview> <Body style={main}> <Container style={container}> <Img src='my-logo.png' alt='My SaaS' style={logo} /> <Text style={title}>Hi {username}!</Text> <Text style={title}>Welcome to Starter Kit for building a SaaS</Text> <Text style={paragraph}>Please verify your email, with the link below:</Text> <Section style={btnContainer}> {/* Button that takes the user to the verification link */} <Button style={button} href={`${baseUrl}/auth/verify-email?token=${emailVerificationToken}`} > Click here to verify </Button> </Section> <Hr style={hr} /> <Text style={footer}>Something in the footer.</Text> </Container> </Body> </Html> ); // Styles applied to different parts of the email for customization const main = { backgroundColor: '#020817', color: '#ffffff', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif', }; const container = { margin: '0 auto', padding: '20px 0 48px', }; ...
Diese Komponente erstellt eine HTML-E-Mail-Vorlage, die Stile und dynamische Inhalte enthält.
Eigenschaften werden definiert, um username
und emailVerificationToken
zu erhalten. Diese Eigenschaften werden verwendet, um die E-Mail für den Benutzer anzupassen und den Bestätigungslink zu generieren.
Zum Validieren und Testen der Vorlage bietet React Email einen Befehl zum lokalen Ausführen eines Servers, der die von uns im E-Mail-Ordner erstellten Vorlagen verfügbar macht.
Wir erstellen das Skript im package.json
, um den Server auszuführen.
{ "scripts": { "dev": "email dev" } }
2. Wir führen das Skript aus und dadurch wird der Server auf localhost
ausgeführt. Wir sehen einen Bildschirm wie den folgenden mit allen erstellten Vorlagen.
In unserem Fall haben wir nur eine Vorlage. Wie Sie unten sehen können, haben wir eine Vorschau der E-Mail, die an den Benutzer gesendet wird.
API KEY
zur .env
-Datei hinzu.
... # resend RESEND_API_KEY="re_jYiFaXXXXXXXXXXXXXXXXXXX"
4. Um die Funktion zum Senden von E-Mails zu erstellen, könnten wir Endpunkte im Ordner api/
erstellen und http
Anfragen stellen. Dieses Mal werden wir dies jedoch tun, indem wir das Potenzial der Serveraktionen nutzen.
actions/email-actions.ts
:
'use server' import React from 'react' import { Resend } from 'resend' // Creates an instance of Resend using the API KEY const resend = new Resend(process.env.RESEND_API_KEY) // Defines the data structure for an email. interface Email { to: string[] // An array of email addresses to which to send the email. subject: string // The subject of the email. react: React.ReactElement // The body of the email as a React element. } export const sendEmail = async (payload: Email) => { const { error } = await resend.emails.send({ from: 'My SaaS <[email protected]>', // Defines the sender's address. ...payload, // Expands the contents of 'payload' to include 'to', 'subject', and 'react'. }) if (error) { console.error('Error sending email', error) return null } console.log('Email sent successfully') return true }
Hinweis: Zum Testen in der freien Entwicklung müssen Sie als Absender die E-Mail „[email protected]“ verwenden, andernfalls müssten Sie eine benutzerdefinierte Domäne hinzufügen.
5. Senden Sie die E-Mail, wenn Sie einen neuen Benutzer registrieren.
actions/auth-actions.ts
:
... import { sendEmail } from './email-actions' import VerificationTemplate from '../../emails/verification-template' // Import a utility function to generate a secure token. import { generateSecureToken } from '@/utils' export async function registerUser(user: Partial<User>) { try { // Creates a new user in the database with the provided data. // Passwords are hashed using bcrypt for security. const createdUser = await prisma.user.create({ data: { ...user, password: await bcrypt.hash(user.password as string, 10), } as User, }) // Generates a secure token to be used for email verification. const emailVerificationToken = generateSecureToken() // Updates the newly created user with the email verification token. await prisma.user.update({ where: { id: createdUser.id, }, data: { emailVerificationToken, }, }) // Sends a verification email to the new user using the sendEmail function. await sendEmail({ to: ['your Resend registered email', createdUser.email], subject: 'Verify your email address', react: React.createElement(VerificationTemplate, { username: createdUser.username, emailVerificationToken }), }) return createdUser } catch (error) { console.log(error) if (error instanceof Prisma.PrismaClientKnownRequestError) { if (error.code === 'P2002') { // Returns a custom error message if the email already exists in the database. return { error: 'Email already exists.' } } } return { error: 'An unexpected error occurred.' } } }
Sobald der Benutzer erstellt und das Verifizierungstoken generiert wurde, sendet die Funktion eine E-Mail an den neuen Benutzer.
Diese E-Mail wird mithilfe der React-Komponente VerificationTemplate
erstellt und mit dem Namen und dem Verifizierungstoken des Benutzers personalisiert. Dieser Schritt ist entscheidend, um zu verifizieren, dass die E-Mail-Adresse des Benutzers gültig ist und vom Benutzer kontrolliert wird.
Sobald die E-Mail an den Benutzer gesendet wurde, enthält diese einen Link, der ihn zurück zur Site führt. Um die E-Mail zu validieren, müssen wir hierfür die Seite erstellen.
(primary)/auth/verify-email/page.tsx
:
/* All imports */ // Defines the prop types for the VerifyEmailPage component. interface VerifyEmailPageProps { searchParams: { [key: string]: string | string[] | undefined } } export default async function VerifyEmailPage({ searchParams }: VerifyEmailPageProps) { let message = 'Verifying email...' let verified = false if (searchParams.token) { // Checks if a verification token is provided in the URL. // Attempts to find a user in the database with the provided email verification token. const user = await prisma.user.findUnique({ where: { emailVerificationToken: searchParams.token as string, }, }) // Conditionally updates the message and verified status based on the user lookup. if (!user) { message = 'User not found. Check your email for the verification link.' } else { // If the user is found, updates the user record to mark the email as verified. await prisma.user.update({ where: { emailVerificationToken: searchParams.token as string, }, data: { emailVerified: true, emailVerificationToken: null, // Clears the verification token. }, }) message = `Email verified! ${user.email}` verified = true // Sets the verified status to true. } } else { // Updates the message if no verification token is found. message = 'No email verification token found. Check your email.' } return ( <div className='grid place-content-center py-40'> <Card className='max-w-sm text-center'> <CardHeader> <CardTitle>Email Verification</CardTitle> </CardHeader> <CardContent> <div className='w-full grid place-content-center py-4'> {verified ? <EmailCheckIcon size={56} /> : <EmailWarningIcon size={56} />} </div> <p className='text-lg text-muted-foreground' style={{ textWrap: 'balance' }}> {message} </p> </CardContent> <CardFooter> {verified && ( // Displays a sign-in link if the email is successfully verified. <Link href={'/auth/signin'} className='bg-primary text-white text-sm font-medium hover:bg-primary/90 h-10 px-4 py-2 rounded-lg w-full text-center'> Sign in </Link> )} </CardFooter> </Card> </div> ) }
Nach der erfolgreichen Validierung der E-Mail des Benutzers wird die folgende Meldung angezeigt.
Jetzt implementieren wir eine letzte Validierung für den Fall, dass der Benutzer sich anmelden möchte und seine E-Mail noch nicht bestätigt hat.
components/auth/sigin-form.tsx
:
... async function onSubmit(values: InputType) { try { setIsLoading(true) const response = await signIn('credentials', { redirect: false, email: values.email, password: values.password, }) if (!response?.ok) { // if the email is not verified we will show a message to the user. if (response?.error === 'EmailNotVerified') { toast({ title: 'Please, verify your email first.', variant: 'warning', }) return } toast({ title: 'Something went wrong!', description: response?.error, variant: 'destructive', }) return } toast({ title: 'Welcome back! ', description: 'Redirecting you to your dashboard!', }) router.push(callbackUrl ? callbackUrl : '/') } catch (error) { console.log({ error }) toast({ title: 'Something went wrong!', description: "We couldn't create your account. Please try again later!", variant: 'destructive', }) } finally { setIsLoading(false) } } ...
Das ist es! 🎉
Der Benutzer kann seine E-Mail bestätigen und seine Registrierung in unserer Anwendung abschließen.
🧑💻
Wir wissen bereits, wie man mit React Email und Resend E-Mails erstellt und versendet. Mit diesem Prozess können Sie Ihr Wissen über React nutzen, um E-Mails effizient zu gestalten und gleichzeitig einen vertrauten und produktiven Workflow beizubehalten.
Sie können mit verschiedenen Komponenten und Eigenschaften experimentieren, um E-Mails zu erstellen, die perfekt zu den Anforderungen Ihrer Projekte passen.
Möchten Sie mit dem Autor Kontakt aufnehmen?
Ich liebe es, über 𝕏 mit Freunden auf der ganzen Welt in Kontakt zu bleiben.
Auch hier erschienen