بناء مجموعة بداية SaaS
المصادقة وإدارة المستخدمين
5 دقيقة للقراءة
ما ستبنيه
نظام مصادقة كامل مع:
- OAuth لـ Google وGitHub
- مصادقة رابط البريد السحري
- إدارة الجلسات مع JWT
- المسارات المحمية والوسيط
- إدارة ملف المستخدم
- التحكم في الوصول القائم على المنظمة
الوقت للإكمال: ~45 دقيقة بمساعدة AI
الخطوة 1: تثبيت NextAuth.js v5
اطلب من AI إعداد Auth.js (NextAuth.js v5):
Set up Auth.js (NextAuth.js v5) for Next.js 15 with:
- Google OAuth provider
- GitHub OAuth provider
- Email magic link with Resend
- Drizzle adapter for PostgreSQL
- JWT session strategy
- Custom sign-in and sign-up pages
Include all necessary files:
- src/lib/auth/config.ts
- src/lib/auth/index.ts
- src/app/api/auth/[...nextauth]/route.ts
- Environment variables needed
AI يُنشئ إعداد المصادقة:
// src/lib/auth/config.ts
import { NextAuthConfig } from 'next-auth';
import Google from 'next-auth/providers/google';
import GitHub from 'next-auth/providers/github';
import Resend from 'next-auth/providers/resend';
import { DrizzleAdapter } from '@auth/drizzle-adapter';
import { db } from '@/lib/db';
import { env } from '@/lib/env';
export const authConfig: NextAuthConfig = {
adapter: DrizzleAdapter(db),
session: { strategy: 'jwt' },
pages: {
signIn: '/login',
signUp: '/signup',
error: '/auth-error',
},
providers: [
Google({
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
}),
GitHub({
clientId: env.GITHUB_CLIENT_ID,
clientSecret: env.GITHUB_CLIENT_SECRET,
}),
Resend({
apiKey: env.RESEND_API_KEY,
from: 'noreply@yourdomain.com',
}),
],
callbacks: {
async jwt({ token, user, account }) {
if (user) {
token.id = user.id;
}
return token;
},
async session({ session, token }) {
if (session.user) {
session.user.id = token.id as string;
}
return session;
},
},
};
الخطوة 2: إنشاء مكونات واجهة المصادقة
اطلب من AI بناء واجهة المصادقة:
Create authentication UI components:
1. src/components/auth/social-buttons.tsx
- Google sign-in button with icon
- GitHub sign-in button with icon
- Loading states
- Error handling
2. src/components/auth/email-form.tsx
- Email input with validation
- Submit button with loading state
- Success message after sending magic link
3. src/components/auth/auth-card.tsx
- Wrapper card for auth pages
- Logo, title, description
- Divider between social and email
Use shadcn/ui components.
Add proper TypeScript types.
Include accessibility attributes.
مثال على المكون المُولد:
// src/components/auth/social-buttons.tsx
'use client';
import { useState } from 'react';
import { signIn } from 'next-auth/react';
import { Button } from '@/components/ui/button';
import { Icons } from '@/components/ui/icons';
interface SocialButtonsProps {
callbackUrl?: string;
}
export function SocialButtons({ callbackUrl = '/dashboard' }: SocialButtonsProps) {
const [isGoogleLoading, setIsGoogleLoading] = useState(false);
const [isGitHubLoading, setIsGitHubLoading] = useState(false);
const handleGoogleSignIn = async () => {
setIsGoogleLoading(true);
await signIn('google', { callbackUrl });
};
const handleGitHubSignIn = async () => {
setIsGitHubLoading(true);
await signIn('github', { callbackUrl });
};
return (
<div className="grid gap-3">
<Button
variant="outline"
onClick={handleGoogleSignIn}
disabled={isGoogleLoading || isGitHubLoading}
className="w-full"
>
{isGoogleLoading ? (
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
) : (
<Icons.google className="mr-2 h-4 w-4" />
)}
المتابعة مع Google
</Button>
<Button
variant="outline"
onClick={handleGitHubSignIn}
disabled={isGoogleLoading || isGitHubLoading}
className="w-full"
>
{isGitHubLoading ? (
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
) : (
<Icons.gitHub className="mr-2 h-4 w-4" />
)}
المتابعة مع GitHub
</Button>
</div>
);
}
الخطوة 3: بناء صفحات تسجيل الدخول والتسجيل
Create authentication pages:
1. src/app/(auth)/login/page.tsx
- AuthCard with "Welcome back" title
- SocialButtons component
- Divider "or continue with email"
- EmailForm component
- Link to signup page
2. src/app/(auth)/signup/page.tsx
- AuthCard with "Create an account" title
- Same components as login
- Link to login page
- Terms of service checkbox
3. src/app/(auth)/layout.tsx
- Centered layout with gradient background
- Logo at top
- Mobile-responsive
Make them beautiful and professional.
الخطوة 4: وسيط حماية المسارات
اطلب من AI إنشاء حماية المسارات:
Create middleware for route protection:
1. src/middleware.ts
- Protect /dashboard/* routes
- Redirect unauthenticated users to /login
- Redirect authenticated users from /login to /dashboard
- Handle API route protection
2. src/lib/auth/get-session.ts
- Server-side session helper
- Type-safe session with user data
Include proper Next.js 15 middleware configuration.
AI يُنشئ:
// src/middleware.ts
import { auth } from '@/lib/auth';
import { NextResponse } from 'next/server';
const publicRoutes = ['/', '/pricing', '/about', '/login', '/signup'];
const authRoutes = ['/login', '/signup'];
export default auth((req) => {
const { nextUrl, auth: session } = req;
const isLoggedIn = !!session?.user;
const isPublicRoute = publicRoutes.includes(nextUrl.pathname);
const isAuthRoute = authRoutes.includes(nextUrl.pathname);
const isDashboardRoute = nextUrl.pathname.startsWith('/dashboard');
// إعادة توجيه المستخدمين المسجلين بعيداً عن صفحات المصادقة
if (isLoggedIn && isAuthRoute) {
return NextResponse.redirect(new URL('/dashboard', nextUrl));
}
// حماية مسارات لوحة التحكم
if (!isLoggedIn && isDashboardRoute) {
const loginUrl = new URL('/login', nextUrl);
loginUrl.searchParams.set('callbackUrl', nextUrl.pathname);
return NextResponse.redirect(loginUrl);
}
return NextResponse.next();
});
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};
الخطوة 5: إدارة المنظمات
أنشئ دعم المنظمات متعددة المستأجرين:
Create organization management system:
1. src/lib/organizations/actions.ts (Server Actions)
- createOrganization(name: string)
- inviteMember(orgId: string, email: string, role: 'admin' | 'member')
- removeMember(orgId: string, userId: string)
- updateMemberRole(orgId: string, userId: string, role: string)
- getOrganizationMembers(orgId: string)
2. src/components/dashboard/organization-switcher.tsx
- Dropdown to switch between user's organizations
- "Create new organization" option
- Show current org name and logo
3. src/hooks/use-organization.ts
- Get current organization from URL or session
- Provide org context to components
Include proper error handling and optimistic updates.
الخطوة 6: إعدادات ملف المستخدم
Create user profile management:
1. src/app/(dashboard)/settings/profile/page.tsx
- Avatar upload (with image optimization)
- Name editing
- Email display (read-only)
- Connected accounts (OAuth providers)
- Delete account option
2. src/lib/users/actions.ts
- updateProfile(data: { name?: string; avatarUrl?: string })
- uploadAvatar(file: File)
- deleteAccount()
3. src/components/dashboard/avatar-upload.tsx
- Drag and drop support
- Preview before upload
- Crop functionality (optional)
- Loading state during upload
Use Uploadthing or Cloudinary for image uploads.
الخطوة 7: التحكم في الوصول القائم على الأدوار
نفّذ RBAC:
Create role-based access control system:
1. src/lib/permissions.ts
- Define permission types (read, write, delete, admin)
- Map roles to permissions
- hasPermission(role, permission) helper
2. src/components/auth/permission-gate.tsx
- Component to conditionally render based on permission
- Props: permission, fallback (optional)
3. Update organization member actions to check permissions
Example usage:
<PermissionGate permission="org:write">
<InviteMemberButton />
</PermissionGate>
AI يُنشئ:
// src/lib/permissions.ts
export type Permission =
| 'org:read'
| 'org:write'
| 'org:delete'
| 'org:invite'
| 'org:manage_members'
| 'billing:read'
| 'billing:write';
export type Role = 'owner' | 'admin' | 'member';
const rolePermissions: Record<Role, Permission[]> = {
owner: [
'org:read', 'org:write', 'org:delete', 'org:invite', 'org:manage_members',
'billing:read', 'billing:write',
],
admin: [
'org:read', 'org:write', 'org:invite', 'org:manage_members',
'billing:read',
],
member: [
'org:read',
],
};
export function hasPermission(role: Role, permission: Permission): boolean {
return rolePermissions[role]?.includes(permission) ?? false;
}
export function getPermissions(role: Role): Permission[] {
return rolePermissions[role] ?? [];
}
الخطوة 8: اختبار تدفق المصادقة
أنشئ قائمة تحقق للاختبار:
Test the complete authentication flow:
1. تسجيل الدخول OAuth
- [ ] تسجيل الدخول بـ Google يُنشئ مستخدماً جديداً
- [ ] تسجيل الدخول بـ GitHub يُنشئ مستخدماً جديداً
- [ ] المستخدم الموجود يسجل الدخول بشكل صحيح
- [ ] الجلسة تستمر عبر التحديثات
2. رابط البريد السحري
- [ ] البريد يُرسل بنجاح
- [ ] الرابط السحري يُسجل دخول المستخدم
- [ ] الروابط غير الصالحة/المنتهية تُظهر خطأ
3. المسارات المحمية
- [ ] /dashboard يُعيد التوجيه إلى /login عند عدم المصادقة
- [ ] /login يُعيد التوجيه إلى /dashboard عند المصادقة
- [ ] مسارات API تُرجع 401 للطلبات غير المصادق عليها
4. المنظمات
- [ ] المستخدمون الجدد يمكنهم إنشاء منظمات
- [ ] مبدل المنظمات يعمل
- [ ] يمكن دعوة وإزالة الأعضاء
- [ ] صلاحيات الأدوار تُطبق
Create test accounts and verify each flow.
قائمة التحقق الأمني
قبل الإطلاق، تحقق من:
- حماية CSRF مُفعلة (افتراضي NextAuth)
- كوكيز الجلسة آمنة في الإنتاج
- تحديد المعدل على نقاط نهاية المصادقة
- متطلبات تعقيد كلمة المرور (إذا كنت تستخدم بيانات الاعتماد)
- التحقق من البريد قبل الوصول
- تسجيل التدقيق للإجراءات الحساسة
- متغيرات البيئة غير مكشوفة على جانب العميل
النقاط الرئيسية
- Auth.js v5 يُبسط المصادقة - متوافق مع Edge، يعمل مع App Router
- متعدد المستأجرين من البداية - المنظمات تُمكن B2B SaaS
- RBAC يتوسع - الصلاحيات القائمة على الأدوار تنمو مع تطبيقك
- Server Actions قوية - تحولات آمنة النوع مع إعادة التحقق
- اختبر المصادقة جيداً - أخطاء الأمان مكلفة
ما التالي
في الدرس التالي، ستدمج Stripe للفوترة، بما في ذلك إدارة الاشتراكات وتدفقات الدفع وwebhooks.
:::