احراز هویت نام کاربری و رمز عبور با Better_Auth، Next.js، Prisma، Shadcn و TailwindCSS

Summarize this content to 400 words in Persian Lang
در این راهنما اجرا خواهیم کرد احراز هویت نام کاربری و رمز عبور برای یک برنامه وب مدرن با استفاده از پشته ای قوی و ایمن از ابزار. این ویژگی موجود را تکمیل خواهد کرد احراز هویت ایمیل و رمز عبور در برنامه. در پایان این آموزش، برنامه شما به کاربران اجازه می دهد تا با هر کدام از آنها وارد سیستم شوند نام کاربری یا ایمیل، با اختیاری احراز هویت دو عاملی مبتنی بر OTP (2FA) برای امنیت بیشتر.
پشته فناوری
در اینجا پشته فناوری است که ما استفاده خواهیم کرد:
Better_Auth نسخه 1: یک کتابخانه احراز هویت TypeScript سبک و قابل توسعه.
Next.js: یک فریمورک قدرتمند React برای ساخت برنامه های کاربردی رندر شده توسط سرور.
پریسما: یک ORM مدرن برای تعامل کارآمد و ایمن با پایگاه داده.
ShadCN: یک کتابخانه مؤلفه ابزار اول برای توسعه سریع رابط کاربری.
TailwindCSS: یک چارچوب CSS محبوب برای ساخت رابط های کاربری مدرن.
ارسال مجدد: یک سرویس ایمیل قابل اعتماد برای ارسال OTP.
پیش نیازها
قبل از ادامه، مطمئن شوید که موارد زیر را آماده کرده اید:
Node.js (نسخه LTS) نصب شده است.
مدیر بسته مانند npm، نخ، یا pnpm (استفاده خواهیم کرد pnpm در این راهنما).
الف نمونه پایگاه داده PostgreSQL (محلی یا میزبانی شده، مانند سوپا بیس یا PlanetScale).
اگر به صورت محلی کار می کنید، داکر یک راه عالی برای تنظیم این است.
آشنایی با TypeScript، Next.js، و پریسما.
شبیه سازی پروژه آغازگر:
این راهنما بر اساس عملکردهایی مانند احراز هویت ایمیل-گذرواژه و تایید ایمیل. شما می توانید:
با دنبال کردن این راهنماها از ابتدا شروع کنید:
یا پروژه شروع کننده را شبیه سازی کنید:
git clone -b feat-username https://github.com/Daanish2003/better_auth_nextjs.git
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
به دایرکتوری پروژه بروید و وابستگی ها را نصب کنید:
pnpm install
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
راه اندازی
1. پیکربندی کنید .env فایل
ایجاد یک .env در ریشه پروژه خود فایل کنید و این تنظیمات را اضافه کنید:
# Authentication settings
BETTER_AUTH_SECRET=”your-secret-key” # Replace with a secure key
BETTER_AUTH_URL=”http://localhost:3000″
NEXT_PUBLIC_APP_URL=”http://localhost:3000″
# Database settings
POSTGRES_PASSWORD=”your-password”
POSTGRES_USER=”your-username”
DATABASE_URL=”postgresql://your-username:your-password@localhost:5432/mydb?schema=public”
# Resend API Key
RESEND_API_KEY=”your-resend-api-key”
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
اگر از Docker برای PostgreSQL استفاده می کنید، کانتینر را راه اندازی کنید:
docker compose up -d
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
توجه:
قبل از اینکه به پیاده سازی احراز هویت نام کاربری و رمز عبور در برنامه کامل پشته شما بپردازیم. به اطلاع شما میرسانم که در این وبلاگ احراز هویت نام کاربری و رمز عبور با احراز هویت ایمیل و رمز عبور موجود را پیادهسازی میکنیم. به طوری که کاربر می تواند با استفاده از ایمیل یا نام کاربری وارد سیستم شود.
مرحله 1: به روز رسانی schema.prisma فایل
باز کنید schema.prismaرا در پوشه پروژه خود قرار دهید و سپس آن را به روز کنید userمدل در طرحواره با اضافه کردن username ستون با رشته نوع اختیاری به آن.
model User {
id String @id @default(cuid())
name String
email String
emailVerified Boolean @default(false)
image String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
twoFactorEnabled Boolean @default(false)
// Add only username column with type of string and ignore other columns if you already implemented user model
username String?
isAnonymous Boolean?
Session Session[]
Account Account[]
TwoFactor TwoFactor[] // if you implemented 2FA add this column
@@unique([email])
@@map(“user”)
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
سپس با استفاده از دستور زیر فایل prisma را تولید و انتقال دهید
pnpx prisma generate
pnpx prisma migrate dev –name username
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
مرحله 2: به روز رسانی auth.ts و auth-client.tsفایل
باز کنید auth.ts در مخزن پروژه خود فایل کنید و سپس آن را اضافه کنید username()پلاگین به آرایه پلاگین از تابع betterAuth.
// src/lib/auth.ts
import { betterAuth } from “better-auth”
import { username } from “better-auth/plugins”
const auth = betterAuth({
// other config options
plugins: [
// other plugins
username()
]
})
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
سپس، باز کنید auth-client.ts در مخزن پروژه خود فایل کنید و اضافه کنید usernameClient() عملکرد پلاگین به آرایه پلاگین
// src/lib/auth-client.ts
import { createAuthClient } from “better-auth/client”
import { usernameClient } from “better-auth/client/plugins”
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL,
plugins: [
usernameClient()
]
})
export const {
signIn,
signOut,
signUp,
useSession
} = authClient;
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
مرحله 3: به روز رسانی login-schema.ts و signup-schema.ts فایل
باز کنید login-schema.tsدر مخزن پروژه خود فایل کنید و طرحواره را با کد زیر به روز کنید.
// src/helpers/zod/login-schema.ts
import { z } from “zod”;
const LoginSchema = z
.object({
// checks if the input given by the user is email or username
emailOrUsername: z
.string()
.min(1, { message: “Email or username is required” }),
password: z
.string()
.min(8, { message: “Password must be at least 8 characters long” })
.max(20, { message: “Password must be at most 20 characters long” }),
})
.refine(
(data) =>
// checks if the email or username is valid
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.emailOrUsername) || /^[a-zA-Z0-9_.]+$/.test(data.emailOrUsername),
{
message: “Provide a valid email or username”,
path: [“emailOrUsername”],
}
);
export default LoginSchema
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
سپس، را باز کنید signup-schema.ts در مخزن پروژه خود فایل کنید و فیلد نام کاربری و نوع آن را در طرح zod اضافه کنید.
// src/helpers/zod/signup-schema.ts
export const SignupSchema = z
.object({
name: z
.string()
.min(2, { message: “Minimum 2 characters are required” })
.max(20, { message: “Maximum of 20 characters are allowed” }),
email: z
.string()
.email({ message: “Invalid email address” })
.min(1, { message: “Email is required” }),
password: z
.string()
.min(8, { message: “Password must be at least 8 characters long” })
.max(20, { message: “Password must be at most 20 characters long” }),
// Added the username field to check its type
username: z
.string()
.min(3, { message: “Username must be at least 3 characters long” })
.max(25, { message: “Username must be at most 25 characters long” })
.regex(/^[a-zA-Z0-9_.]+$/, { message: “Username can only contain letters, numbers, underscores, and dots” }),
})
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
مرحله 4: ایجاد کنید generateUsername فایل
ایجاد یک generate-username.ts داخل helper/auth/ پوشه و کد را از زیر وارد کنید
// helpers/auth/generate-username.ts
export const generateUsername = (name: string) => {
const randomNumbers = Math.floor(1000 + Math.random() * 9000); // Generate a random 4-digit number
return `${name.replace(/\s+/g, ”).toLowerCase()}${randomNumbers}`;
};
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
GenerateUsernameتابع برای تولید نام کاربری تصادفی از نام داده شده کاربر استفاده می شود.
مرحله 5: مؤلفه SignIn و مؤلفه Signup را به روز کنید
جزء ورود به سیستم:
// src/components/auth/sign-in.tsx
“use client”
import React from ‘react’
import CardWrapper from ‘../card-wrapper’
import FormError from ‘../form-error’
import { FormSuccess } from ‘../form-success’
import { FcGoogle } from ‘react-icons/fc’
import SocialButton from ‘./social-button’
import { FaGithub } from ‘react-icons/fa’
import { useAuthState } from ‘@/hooks/useAuthState’
import { useForm } from ‘react-hook-form’
import { zodResolver } from ‘@hookform/resolvers/zod’
import { z } from ‘zod’
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from ‘../ui/form’
import { Input } from ‘../ui/input’
import { Button } from ‘../ui/button’
import { SignupSchema } from ‘@/helpers/zod/signup-schema’
import { signUp } from ‘@/lib/auth-client’
import { generateUsername } from ‘@/helpers/auth/generate-username’
const SignUp = () => {
const { error, success, loading, setLoading, setError, setSuccess, resetState } = useAuthState();
const form = useForm<z.infer<typeof SignupSchema>>({
resolver: zodResolver(SignupSchema),
defaultValues: {
name: ”,
email: ”,
password: ”,
username: ”, // Added username field
}
})
const onSubmit = async (values: z.infer<typeof SignupSchema>) => {
try {
await signUp.email({
name: values.name,
email: values.email,
password: values.password,
username: values.username, // Add the username fields
callbackURL: ‘/’
}, {
onResponse: () => {
setLoading(false)
},
onRequest: () => {
resetState()
setLoading(true)
},
onSuccess: () => {
setSuccess(“Verification link has been sent to your mail”)
},
onError: (ctx) => {
setError(ctx.error.message);
},
});
} catch (error) {
console.error(error)
setError(“Something went wrong”)
}
}
return (
<CardWrapper
cardTitle=’SignUp’
cardDescription=’Create an new account’
cardFooterLink=’/signin’
cardFooterDescription=’Already have an account?’
cardFooterLinkTitle=’Signin’
>
<Form {…form}>
{/* Make changes to only name field of the form*/}
<form className=’space-y-4′ onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name=”name”
render={({ field }) => (
<FormItem>
<FormLabel>NameFormLabel>
<FormControl>
<Input
disabled={loading}
type=”text”
placeholder=’john’
{…field}
onChange={(e) => {
field.onChange(e); // Update form state
const username = generateUsername(e.target.value);
form.setValue(‘username’, username); // Auto-fill username
}}
/>
FormControl>
<FormMessage />
FormItem>
)}
/>
{/* All other exisitng formfields like email and password */}
form>
Form>
CardWrapper>
)
}
export default SignUp
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
منطق نام کاربری را به آن اضافه کنید SignUp:
تولید خودکار نام های کاربری بر اساس نام کاربر.
جزء ثبت نام
“use client”;
import React from “react”;
import { useForm } from “react-hook-form”;
import { zodResolver } from “@hookform/resolvers/zod”;
import { z } from “zod”;
import { useRouter } from “next/navigation”;
import Link from “next/link”;
import CardWrapper from “../card-wrapper”;
import FormError from “../form-error”;
import { FormSuccess } from “../form-success”;
import { FcGoogle } from “react-icons/fc”;
import { FaGithub } from “react-icons/fa”;
import SocialButton from “./social-button”;
import LoginSchema from “@/helpers/zod/login-schema”;
import { useAuthState } from “@/hooks/useAuthState”;
import { signIn } from “@/lib/auth-client”;
import { requestOTP } from “@/helpers/auth/request-otp”;
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from “../ui/form”;
import { Input } from “../ui/input”;
import { Button } from “../ui/button”;
const SignIn = () => {
const router = useRouter();
// handles error, success, loading and resetState of a form
const { error, success, loading, setSuccess, setError, setLoading, resetState } = useAuthState();
const form = useForm<z.infer<typeof LoginSchema>>({
resolver: zodResolver(LoginSchema),
defaultValues: {
emailOrUsername: “”, // update the field email to emailOrUsername
password: “”,
},
});
// Check if the value is an email
const isEmail = (value: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
const onSubmit = async (values: z.infer<typeof LoginSchema>) => {
const { emailOrUsername, password } = values;
// Determine if the input is an email or username
const isEmailInput = isEmail(emailOrUsername);
try {
// resetState of a form after resubmit
resetState();
setLoading(true);
if (isEmailInput) {
// if user has given email and its true then signin using email and password
// also send 2FA code to the user’s email, if enabled
await signIn.email(
{ email: emailOrUsername, password },
{
onRequest: () => setLoading(true),
onResponse: () => setLoading(false),
onSuccess: async (ctx) => {
if (ctx.data.twoFactorRedirect) {
const res = await requestOTP(); // request otp for 2fa
if (res?.data) {
setSuccess(“OTP has been sent to your email”);
router.push(“two-factor”);
} else if (res?.error) {
setError(res.error.message);
}
} else { // if didn’t enable 2FA show loggedIn message
setSuccess(“Logged in successfully”);
router.replace(“/”);
}
},
onError: (ctx) => {
const errorMessage =
ctx.error.status === 403
? “Please verify your email address”
: ctx.error.message;
setError(errorMessage);
},
}
);
} else {
// if user has given username then signin using username and password
// also send 2FA code to the user’s email, if enabled
await signIn.username(
{ username: emailOrUsername, password },
{
onRequest: () => setLoading(true),
onResponse: () => setLoading(false),
onSuccess: async (ctx) => {
if (ctx.data.twoFactorRedirect) {
const res = await requestOTP();
if (res?.data) {
setSuccess(“OTP has been sent to your email”);
router.push(“two-factor”);
} else if (res?.error) {
setError(res.error.message);
}
} else {
setSuccess(“Logged in successfully”);
router.replace(“/”);
}
},
onError: (ctx) => {
const errorMessage =
ctx.error.status === 403
? “Please verify your email address”
: ctx.error.message;
setError(errorMessage);
},
}
);
}
} catch (err) { // catch the error
console.error(err);
setError(“Something went wrong. Please try again.”);
} finally { // set the loading to false after submitting the form
setLoading(false);
}
};
return (
<CardWrapper
cardTitle=”Sign In”
{/* change the cardDescription for email to email or password */}
cardDescription=”Enter your email or username below to login to your account”
cardFooterDescription=”Don’t have an account?”
cardFooterLink=”/signup”
cardFooterLinkTitle=”Sign up”
>
<Form {…form}>
<form className=”space-y-4″ onSubmit={form.handleSubmit(onSubmit)}>
{/* Email or Username Field */}
<FormField
control={form.control}
name=”emailOrUsername”
render={({ field }) => (
<FormItem>
<FormLabel>Email or UsernameFormLabel>
<FormControl>
<Input
disabled={loading}
type=”text” // update the type from email to text
placeholder=”Enter email or username” // update the placeholder
{…field}
/>
FormControl>
<FormMessage />
FormItem>
)}
/>
{/* All other exisitng formfields like password and form message */}
form>
Form>
CardWrapper>
);
};
export default SignIn;
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
ورود با نام کاربری یا ایمیل را فعال کنید SignIn:
را به روز کنید email میدان به emailOrUsername.
برای تعیین اینکه آیا ورودی یک ایمیل یا نام کاربری است از یک تابع ابزار استفاده کنید.
مرحله 6: برنامه خود را اجرا کنید:
سرور توسعه خود را راه اندازی کنید:
pnpm dev
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
به سمت خود حرکت کنید sign-up مسیر و sign-in مسیر و سعی کنید ابتدا کاربر را با ایمیل، نام و رمز عبور ثبت نام کنید و سپس از ایمیل یا نام کاربری استفاده کنید.
نتیجه گیری
تبریک می گویم! 🎉 شما با موفقیت یک احراز هویت نام کاربری و رمز عبور سیستم در برنامه شما کاربران شما اکنون می توانند با ایمیل یا نام کاربری خود وارد سیستم شوند و این تجربه را انعطاف پذیرتر و کاربر پسندتر می کند.
لینک های وبلاگ:
احراز هویت دو عاملی با استفاده از BetterAuth: https://dev.to/daanish2003/two-factor-authentication-using-betterauth-nextjs-prisma-shadcn-and-resend-1b5p
Forgot and Reset Password with BetterAuth: https://dev.to/daanish2003/forgot-and-reset-password-using-betterauth-nextjs-and-resend-ilj
وبلاگ تأیید ایمیل: https://dev.to/daanish2003/email-verification-using-betterauth-nextjs-and-resend-37gn
ایمیل و رمز عبور با Better_Auth: https://dev.to/daanish2003/email-and-password-auth-using-betterauth-nextjs-prisma-shadcn-and-tailwindcss-hgc
وبلاگ OAuth: https://dev.to/daanish2003/oauth-using-betterauth-nextjs-prisma-shadcn-and-tailwindcss-45bp
Better_Auth Docs: https://www.better-auth.com/
اسناد pnpm: https://pnpm.io/
Docker Docs: https://docs.docker.com/
Prisma Docs: https://www.prisma.io/docs/getting-started
Shadcn Docs: https://ui.shadcn.com/
اسناد Next.js: https://nextjs.org/
Tailwindcss Docs: https://tailwindcss.com/
مخزن Github: https://github.com/Daanish2003/better_auth_nextjs
در این راهنما اجرا خواهیم کرد احراز هویت نام کاربری و رمز عبور برای یک برنامه وب مدرن با استفاده از پشته ای قوی و ایمن از ابزار. این ویژگی موجود را تکمیل خواهد کرد احراز هویت ایمیل و رمز عبور در برنامه. در پایان این آموزش، برنامه شما به کاربران اجازه می دهد تا با هر کدام از آنها وارد سیستم شوند نام کاربری یا ایمیل، با اختیاری احراز هویت دو عاملی مبتنی بر OTP (2FA) برای امنیت بیشتر.
پشته فناوری
در اینجا پشته فناوری است که ما استفاده خواهیم کرد:
- Better_Auth نسخه 1: یک کتابخانه احراز هویت TypeScript سبک و قابل توسعه.
- Next.js: یک فریمورک قدرتمند React برای ساخت برنامه های کاربردی رندر شده توسط سرور.
- پریسما: یک ORM مدرن برای تعامل کارآمد و ایمن با پایگاه داده.
- ShadCN: یک کتابخانه مؤلفه ابزار اول برای توسعه سریع رابط کاربری.
- TailwindCSS: یک چارچوب CSS محبوب برای ساخت رابط های کاربری مدرن.
- ارسال مجدد: یک سرویس ایمیل قابل اعتماد برای ارسال OTP.
پیش نیازها
قبل از ادامه، مطمئن شوید که موارد زیر را آماده کرده اید:
- Node.js (نسخه LTS) نصب شده است.
- مدیر بسته مانند npm، نخ، یا pnpm (استفاده خواهیم کرد
pnpm
در این راهنما). - الف نمونه پایگاه داده PostgreSQL (محلی یا میزبانی شده، مانند سوپا بیس یا PlanetScale).
- اگر به صورت محلی کار می کنید، داکر یک راه عالی برای تنظیم این است.
- آشنایی با TypeScript، Next.js، و پریسما.
شبیه سازی پروژه آغازگر:
این راهنما بر اساس عملکردهایی مانند احراز هویت ایمیل-گذرواژه و تایید ایمیل. شما می توانید:
- با دنبال کردن این راهنماها از ابتدا شروع کنید:
- یا پروژه شروع کننده را شبیه سازی کنید:
git clone -b feat-username https://github.com/Daanish2003/better_auth_nextjs.git
به دایرکتوری پروژه بروید و وابستگی ها را نصب کنید:
pnpm install
راه اندازی
1. پیکربندی کنید .env
فایل
ایجاد یک .env
در ریشه پروژه خود فایل کنید و این تنظیمات را اضافه کنید:
# Authentication settings
BETTER_AUTH_SECRET="your-secret-key" # Replace with a secure key
BETTER_AUTH_URL="http://localhost:3000"
NEXT_PUBLIC_APP_URL="http://localhost:3000"
# Database settings
POSTGRES_PASSWORD="your-password"
POSTGRES_USER="your-username"
DATABASE_URL="postgresql://your-username:your-password@localhost:5432/mydb?schema=public"
# Resend API Key
RESEND_API_KEY="your-resend-api-key"
اگر از Docker برای PostgreSQL استفاده می کنید، کانتینر را راه اندازی کنید:
docker compose up -d
توجه:
قبل از اینکه به پیاده سازی احراز هویت نام کاربری و رمز عبور در برنامه کامل پشته شما بپردازیم. به اطلاع شما میرسانم که در این وبلاگ احراز هویت نام کاربری و رمز عبور با احراز هویت ایمیل و رمز عبور موجود را پیادهسازی میکنیم. به طوری که کاربر می تواند با استفاده از ایمیل یا نام کاربری وارد سیستم شود.
مرحله 1: به روز رسانی schema.prisma
فایل
باز کنید schema.prisma
را در پوشه پروژه خود قرار دهید و سپس آن را به روز کنید user
مدل در طرحواره با اضافه کردن username
ستون با رشته نوع اختیاری به آن.
model User {
id String @id @default(cuid())
name String
email String
emailVerified Boolean @default(false)
image String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
twoFactorEnabled Boolean @default(false)
// Add only username column with type of string and ignore other columns if you already implemented user model
username String?
isAnonymous Boolean?
Session Session[]
Account Account[]
TwoFactor TwoFactor[] // if you implemented 2FA add this column
@@unique([email])
@@map("user")
}
سپس با استفاده از دستور زیر فایل prisma را تولید و انتقال دهید
pnpx prisma generate
pnpx prisma migrate dev --name username
مرحله 2: به روز رسانی auth.ts
و auth-client.ts
فایل
باز کنید auth.ts
در مخزن پروژه خود فایل کنید و سپس آن را اضافه کنید username()
پلاگین به آرایه پلاگین از تابع betterAuth.
// src/lib/auth.ts
import { betterAuth } from "better-auth"
import { username } from "better-auth/plugins"
const auth = betterAuth({
// other config options
plugins: [
// other plugins
username()
]
})
سپس، باز کنید auth-client.ts
در مخزن پروژه خود فایل کنید و اضافه کنید usernameClient()
عملکرد پلاگین به آرایه پلاگین
// src/lib/auth-client.ts
import { createAuthClient } from "better-auth/client"
import { usernameClient } from "better-auth/client/plugins"
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL,
plugins: [
usernameClient()
]
})
export const {
signIn,
signOut,
signUp,
useSession
} = authClient;
مرحله 3: به روز رسانی login-schema.ts
و signup-schema.ts
فایل
باز کنید login-schema.ts
در مخزن پروژه خود فایل کنید و طرحواره را با کد زیر به روز کنید.
// src/helpers/zod/login-schema.ts
import { z } from "zod";
const LoginSchema = z
.object({
// checks if the input given by the user is email or username
emailOrUsername: z
.string()
.min(1, { message: "Email or username is required" }),
password: z
.string()
.min(8, { message: "Password must be at least 8 characters long" })
.max(20, { message: "Password must be at most 20 characters long" }),
})
.refine(
(data) =>
// checks if the email or username is valid
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.emailOrUsername) || /^[a-zA-Z0-9_.]+$/.test(data.emailOrUsername),
{
message: "Provide a valid email or username",
path: ["emailOrUsername"],
}
);
export default LoginSchema
سپس، را باز کنید signup-schema.ts
در مخزن پروژه خود فایل کنید و فیلد نام کاربری و نوع آن را در طرح zod اضافه کنید.
// src/helpers/zod/signup-schema.ts
export const SignupSchema = z
.object({
name: z
.string()
.min(2, { message: "Minimum 2 characters are required" })
.max(20, { message: "Maximum of 20 characters are allowed" }),
email: z
.string()
.email({ message: "Invalid email address" })
.min(1, { message: "Email is required" }),
password: z
.string()
.min(8, { message: "Password must be at least 8 characters long" })
.max(20, { message: "Password must be at most 20 characters long" }),
// Added the username field to check its type
username: z
.string()
.min(3, { message: "Username must be at least 3 characters long" })
.max(25, { message: "Username must be at most 25 characters long" })
.regex(/^[a-zA-Z0-9_.]+$/, { message: "Username can only contain letters, numbers, underscores, and dots" }),
})
مرحله 4: ایجاد کنید generateUsername
فایل
ایجاد یک generate-username.ts
داخل helper/auth/
پوشه و کد را از زیر وارد کنید
// helpers/auth/generate-username.ts
export const generateUsername = (name: string) => {
const randomNumbers = Math.floor(1000 + Math.random() * 9000); // Generate a random 4-digit number
return `${name.replace(/\s+/g, '').toLowerCase()}${randomNumbers}`;
};
GenerateUsername
تابع برای تولید نام کاربری تصادفی از نام داده شده کاربر استفاده می شود.
مرحله 5: مؤلفه SignIn و مؤلفه Signup را به روز کنید
جزء ورود به سیستم:
// src/components/auth/sign-in.tsx
"use client"
import React from 'react'
import CardWrapper from '../card-wrapper'
import FormError from '../form-error'
import { FormSuccess } from '../form-success'
import { FcGoogle } from 'react-icons/fc'
import SocialButton from './social-button'
import { FaGithub } from 'react-icons/fa'
import { useAuthState } from '@/hooks/useAuthState'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '../ui/form'
import { Input } from '../ui/input'
import { Button } from '../ui/button'
import { SignupSchema } from '@/helpers/zod/signup-schema'
import { signUp } from '@/lib/auth-client'
import { generateUsername } from '@/helpers/auth/generate-username'
const SignUp = () => {
const { error, success, loading, setLoading, setError, setSuccess, resetState } = useAuthState();
const form = useForm<z.infer<typeof SignupSchema>>({
resolver: zodResolver(SignupSchema),
defaultValues: {
name: '',
email: '',
password: '',
username: '', // Added username field
}
})
const onSubmit = async (values: z.infer<typeof SignupSchema>) => {
try {
await signUp.email({
name: values.name,
email: values.email,
password: values.password,
username: values.username, // Add the username fields
callbackURL: '/'
}, {
onResponse: () => {
setLoading(false)
},
onRequest: () => {
resetState()
setLoading(true)
},
onSuccess: () => {
setSuccess("Verification link has been sent to your mail")
},
onError: (ctx) => {
setError(ctx.error.message);
},
});
} catch (error) {
console.error(error)
setError("Something went wrong")
}
}
return (
<CardWrapper
cardTitle='SignUp'
cardDescription='Create an new account'
cardFooterLink='/signin'
cardFooterDescription='Already have an account?'
cardFooterLinkTitle='Signin'
>
<Form {...form}>
{/* Make changes to only name field of the form*/}
<form className='space-y-4' onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>NameFormLabel>
<FormControl>
<Input
disabled={loading}
type="text"
placeholder='john'
{...field}
onChange={(e) => {
field.onChange(e); // Update form state
const username = generateUsername(e.target.value);
form.setValue('username', username); // Auto-fill username
}}
/>
FormControl>
<FormMessage />
FormItem>
)}
/>
{/* All other exisitng formfields like email and password */}
form>
Form>
CardWrapper>
)
}
export default SignUp
منطق نام کاربری را به آن اضافه کنید SignUp
:
- تولید خودکار نام های کاربری بر اساس نام کاربر.
جزء ثبت نام
"use client";
import React from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { useRouter } from "next/navigation";
import Link from "next/link";
import CardWrapper from "../card-wrapper";
import FormError from "../form-error";
import { FormSuccess } from "../form-success";
import { FcGoogle } from "react-icons/fc";
import { FaGithub } from "react-icons/fa";
import SocialButton from "./social-button";
import LoginSchema from "@/helpers/zod/login-schema";
import { useAuthState } from "@/hooks/useAuthState";
import { signIn } from "@/lib/auth-client";
import { requestOTP } from "@/helpers/auth/request-otp";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "../ui/form";
import { Input } from "../ui/input";
import { Button } from "../ui/button";
const SignIn = () => {
const router = useRouter();
// handles error, success, loading and resetState of a form
const { error, success, loading, setSuccess, setError, setLoading, resetState } = useAuthState();
const form = useForm<z.infer<typeof LoginSchema>>({
resolver: zodResolver(LoginSchema),
defaultValues: {
emailOrUsername: "", // update the field email to emailOrUsername
password: "",
},
});
// Check if the value is an email
const isEmail = (value: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
const onSubmit = async (values: z.infer<typeof LoginSchema>) => {
const { emailOrUsername, password } = values;
// Determine if the input is an email or username
const isEmailInput = isEmail(emailOrUsername);
try {
// resetState of a form after resubmit
resetState();
setLoading(true);
if (isEmailInput) {
// if user has given email and its true then signin using email and password
// also send 2FA code to the user's email, if enabled
await signIn.email(
{ email: emailOrUsername, password },
{
onRequest: () => setLoading(true),
onResponse: () => setLoading(false),
onSuccess: async (ctx) => {
if (ctx.data.twoFactorRedirect) {
const res = await requestOTP(); // request otp for 2fa
if (res?.data) {
setSuccess("OTP has been sent to your email");
router.push("two-factor");
} else if (res?.error) {
setError(res.error.message);
}
} else { // if didn't enable 2FA show loggedIn message
setSuccess("Logged in successfully");
router.replace("/");
}
},
onError: (ctx) => {
const errorMessage =
ctx.error.status === 403
? "Please verify your email address"
: ctx.error.message;
setError(errorMessage);
},
}
);
} else {
// if user has given username then signin using username and password
// also send 2FA code to the user's email, if enabled
await signIn.username(
{ username: emailOrUsername, password },
{
onRequest: () => setLoading(true),
onResponse: () => setLoading(false),
onSuccess: async (ctx) => {
if (ctx.data.twoFactorRedirect) {
const res = await requestOTP();
if (res?.data) {
setSuccess("OTP has been sent to your email");
router.push("two-factor");
} else if (res?.error) {
setError(res.error.message);
}
} else {
setSuccess("Logged in successfully");
router.replace("/");
}
},
onError: (ctx) => {
const errorMessage =
ctx.error.status === 403
? "Please verify your email address"
: ctx.error.message;
setError(errorMessage);
},
}
);
}
} catch (err) { // catch the error
console.error(err);
setError("Something went wrong. Please try again.");
} finally { // set the loading to false after submitting the form
setLoading(false);
}
};
return (
<CardWrapper
cardTitle="Sign In"
{/* change the cardDescription for email to email or password */}
cardDescription="Enter your email or username below to login to your account"
cardFooterDescription="Don't have an account?"
cardFooterLink="/signup"
cardFooterLinkTitle="Sign up"
>
<Form {...form}>
<form className="space-y-4" onSubmit={form.handleSubmit(onSubmit)}>
{/* Email or Username Field */}
<FormField
control={form.control}
name="emailOrUsername"
render={({ field }) => (
<FormItem>
<FormLabel>Email or UsernameFormLabel>
<FormControl>
<Input
disabled={loading}
type="text" // update the type from email to text
placeholder="Enter email or username" // update the placeholder
{...field}
/>
FormControl>
<FormMessage />
FormItem>
)}
/>
{/* All other exisitng formfields like password and form message */}
form>
Form>
CardWrapper>
);
};
export default SignIn;
ورود با نام کاربری یا ایمیل را فعال کنید SignIn
:
- را به روز کنید
email
میدان بهemailOrUsername
. - برای تعیین اینکه آیا ورودی یک ایمیل یا نام کاربری است از یک تابع ابزار استفاده کنید.
مرحله 6: برنامه خود را اجرا کنید:
سرور توسعه خود را راه اندازی کنید:
pnpm dev
به سمت خود حرکت کنید sign-up
مسیر و sign-in
مسیر و سعی کنید ابتدا کاربر را با ایمیل، نام و رمز عبور ثبت نام کنید و سپس از ایمیل یا نام کاربری استفاده کنید.
نتیجه گیری
تبریک می گویم! 🎉 شما با موفقیت یک احراز هویت نام کاربری و رمز عبور سیستم در برنامه شما کاربران شما اکنون می توانند با ایمیل یا نام کاربری خود وارد سیستم شوند و این تجربه را انعطاف پذیرتر و کاربر پسندتر می کند.
لینک های وبلاگ:
احراز هویت دو عاملی با استفاده از BetterAuth: https://dev.to/daanish2003/two-factor-authentication-using-betterauth-nextjs-prisma-shadcn-and-resend-1b5p
Forgot and Reset Password with BetterAuth: https://dev.to/daanish2003/forgot-and-reset-password-using-betterauth-nextjs-and-resend-ilj
وبلاگ تأیید ایمیل: https://dev.to/daanish2003/email-verification-using-betterauth-nextjs-and-resend-37gn
ایمیل و رمز عبور با Better_Auth: https://dev.to/daanish2003/email-and-password-auth-using-betterauth-nextjs-prisma-shadcn-and-tailwindcss-hgc
وبلاگ OAuth: https://dev.to/daanish2003/oauth-using-betterauth-nextjs-prisma-shadcn-and-tailwindcss-45bp
Better_Auth Docs: https://www.better-auth.com/
اسناد pnpm: https://pnpm.io/
Docker Docs: https://docs.docker.com/
Prisma Docs: https://www.prisma.io/docs/getting-started
Shadcn Docs: https://ui.shadcn.com/
اسناد Next.js: https://nextjs.org/
Tailwindcss Docs: https://tailwindcss.com/
مخزن Github: https://github.com/Daanish2003/better_auth_nextjs