بناء مجموعة بداية 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)
  • كوكيز الجلسة آمنة في الإنتاج
  • تحديد المعدل على نقاط نهاية المصادقة
  • متطلبات تعقيد كلمة المرور (إذا كنت تستخدم بيانات الاعتماد)
  • التحقق من البريد قبل الوصول
  • تسجيل التدقيق للإجراءات الحساسة
  • متغيرات البيئة غير مكشوفة على جانب العميل

النقاط الرئيسية

  1. Auth.js v5 يُبسط المصادقة - متوافق مع Edge، يعمل مع App Router
  2. متعدد المستأجرين من البداية - المنظمات تُمكن B2B SaaS
  3. RBAC يتوسع - الصلاحيات القائمة على الأدوار تنمو مع تطبيقك
  4. Server Actions قوية - تحولات آمنة النوع مع إعادة التحقق
  5. اختبر المصادقة جيداً - أخطاء الأمان مكلفة

ما التالي

في الدرس التالي، ستدمج Stripe للفوترة، بما في ذلك إدارة الاشتراكات وتدفقات الدفع وwebhooks.

:::

اختبار

الوحدة 1: بناء مجموعة بداية SaaS

خذ الاختبار