OAuth

سجّل الدخول باستخدام Google وGitHub وغيرهما من مزوّدي OAuth.

ملاحظة: هذا محتوى تجريبي/مؤقت لأغراض العرض التوضيحي.

اسمح للمستخدمين بتسجيل الدخول باستخدام حساباتهم الحالية لدى Google وGitHub وغيرهما من المزوّدين.

المزوّدون المدعومون

تدعم Supabase العديد من مزوّدي OAuth:

  • Google
  • GitHub
  • GitLab
  • Bitbucket
  • Azure
  • Facebook
  • Twitter
  • Discord
  • Slack
  • وغيرها...

إعداد OAuth

الإعداد في لوحة تحكم Supabase

  1. انتقل إلى Authentication ← Providers
  2. فعّل المزوّد الذي تريده (مثل Google)
  3. أضف بيانات اعتماد OAuth الخاصة بك:
    • معرّف العميل (Client ID)
    • سرّ العميل (Client Secret)
    • عنوان URL لإعادة التوجيه (Redirect URL): https://your-project.supabase.co/auth/v1/callback

إعداد Google OAuth

  1. انتقل إلى Google Cloud Console
  2. أنشئ مشروعًا جديدًا أو اختر مشروعًا موجودًا
  3. فعّل Google+ API
  4. أنشئ بيانات اعتماد OAuth 2.0
  5. أضف عناوين URI المعتمدة لإعادة التوجيه:
    • الإنتاج: https://your-project.supabase.co/auth/v1/callback
    • التطوير: http://localhost:54321/auth/v1/callback

إعداد GitHub OAuth

  1. انتقل إلى GitHub Settings ← Developer Settings ← OAuth Apps
  2. انقر على «New OAuth App»
  3. املأ التفاصيل:
    • اسم التطبيق (Application name): تطبيقك
    • عنوان URL للصفحة الرئيسية (Homepage URL): https://yourapp.com
    • عنوان URL لاستدعاء التفويض (Authorization callback URL): https://your-project.supabase.co/auth/v1/callback
  4. انسخ معرّف العميل (Client ID) وسرّ العميل (Client Secret) إلى Supabase

التنفيذ

زر تسجيل الدخول عبر OAuth

'use client';

import { signInWithOAuthAction } from '../_lib/actions';

export function OAuthButtons() {
  const handleGoogleSignIn = async () => {
    await signInWithOAuthAction('google');
  };

  const handleGitHubSignIn = async () => {
    await signInWithOAuthAction('github');
  };

  return (
    <div className="space-y-2">
      <button
        onClick={handleGoogleSignIn}
        className="w-full flex items-center justify-center gap-2 border rounded-lg p-2"
      >
        <GoogleIcon />
        Continue with Google
      </button>

      <button
        onClick={handleGitHubSignIn}
        className="w-full flex items-center justify-center gap-2 border rounded-lg p-2"
      >
        <GitHubIcon />
        Continue with GitHub
      </button>
    </div>
  );
}

إجراء الخادم

'use server';

import { enhanceAction } from '@kit/next/actions';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { z } from 'zod';

const OAuthProviderSchema = z.enum([
  'google',
  'github',
  'gitlab',
  'azure',
  'facebook',
]);

export const signInWithOAuthAction = enhanceAction(
  async (provider) => {
    const client = getSupabaseServerClient();
    const origin = process.env.NEXT_PUBLIC_SITE_URL!;

    const { data, error } = await client.auth.signInWithOAuth({
      provider,
      options: {
        redirectTo: `${origin}/auth/callback`,
      },
    });

    if (error) throw error;

    // إعادة التوجيه إلى مزوّد OAuth
    redirect(data.url);
  },
  {
    schema: OAuthProviderSchema,
  }
);

معالج الاستدعاء العكسي لـ OAuth

// app/auth/callback/route.ts
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  const requestUrl = new URL(request.url);
  const code = requestUrl.searchParams.get('code');

  if (code) {
    const cookieStore = cookies();
    const supabase = createRouteHandlerClient({ cookies: () => cookieStore });

    await supabase.auth.exchangeCodeForSession(code);
  }

  // إعادة التوجيه إلى الصفحة الرئيسية
  return NextResponse.redirect(new URL('/home', request.url));
}

تخصيص مسار OAuth

النطاقات

اطلب أذونات محددة:

await client.auth.signInWithOAuth({
  provider: 'google',
  options: {
    scopes: 'email profile https://www.googleapis.com/auth/calendar',
  },
});

معاملات الاستعلام

مرّر معاملات مخصصة:

await client.auth.signInWithOAuth({
  provider: 'azure',
  options: {
    queryParams: {
      prompt: 'consent',
      access_type: 'offline',
    },
  },
});

تخطي إعادة التوجيه في المتصفح

لتطبيقات الأجهزة المحمولة أو المسارات المخصصة:

const { data } = await client.auth.signInWithOAuth({
  provider: 'google',
  options: {
    skipBrowserRedirect: true,
  },
});

// يحتوي data.url على عنوان URL الخاص بـ OAuth
// تعامل مع إعادة التوجيه يدويًا

ربط الحسابات

ربط مزوّدين إضافيين

اسمح للمستخدمين بربط عدة حسابات OAuth:

export const linkOAuthProviderAction = enhanceAction(
  async (provider) => {
    const client = getSupabaseServerClient();
    const user = await requireAuth();

    const { data, error } = await client.auth.linkIdentity({
      provider,
    });

    if (error) throw error;

    redirect(data.url);
  },
  { schema: OAuthProviderSchema, auth: true }
);

إلغاء ربط المزوّدين

export const unlinkOAuthProviderAction = enhanceAction(
  async ({ provider, identityId }) => {
    const client = getSupabaseServerClient();

    const { error } = await client.auth.unlinkIdentity({
      identity_id: identityId,
    });

    if (error) throw error;

    revalidatePath('/settings/security');
  },
  {
    schema: z.object({
      provider: z.string(),
      identityId: z.string(),
    }),
    auth: true,
  }
);

عرض الهويات المرتبطة

import { getSupabaseServerClient } from '@kit/supabase/server-client';

export async function getLinkedIdentities() {
  const client = getSupabaseServerClient();
  const { data: { user } } = await client.auth.getUser();

  return user?.identities || [];
}

بيانات المستخدم من OAuth

الوصول إلى بيانات المزوّد

const { data: { user } } = await client.auth.getUser();

// البيانات الوصفية للمستخدم من المزوّد
const {
  full_name,
  avatar_url,
  email,
} = user.user_metadata;

// البيانات الخاصة بالمزوّد
const identities = user.identities || [];
const googleIdentity = identities.find(i => i.provider === 'google');

console.log(googleIdentity?.identity_data);

تخزين بيانات إضافية

export const completeOAuthProfileAction = enhanceAction(
  async (data) => {
    const client = getSupabaseServerClient();
    const user = await requireAuth();

    // تحديث البيانات الوصفية للمستخدم
    await client.auth.updateUser({
      data: {
        username: data.username,
        bio: data.bio,
      },
    });

    // تحديث الملف الشخصي في قاعدة البيانات
    await client.from('profiles').upsert({
      id: user.id,
      username: data.username,
      bio: data.bio,
      avatar_url: user.user_metadata.avatar_url,
    });

    redirect('/home');
  },
  { schema: ProfileSchema, auth: true }
);

الإعداد

تفعيل OAuth في ملف الإعداد

// config/auth.config.ts
export const authConfig = {
  providers: {
    emailPassword: true,
    oAuth: ['google', 'github'],
  },
};

العرض الشرطي

import { authConfig } from '~/config/auth.config';

export function AuthProviders() {
  return (
    <>
      {authConfig.providers.emailPassword && <EmailPasswordForm />}

      {authConfig.providers.oAuth?.includes('google') && (
        <GoogleSignInButton />
      )}

      {authConfig.providers.oAuth?.includes('github') && (
        <GitHubSignInButton />
      )}
    </>
  );
}

استكشاف الأخطاء وإصلاحها

عدم تطابق عنوان URI لإعادة التوجيه

تأكد من التطابق التام لعناوين URI الخاصة بإعادة التوجيه:

  • تحقق من Supabase Dashboard ← Authentication ← URL Configuration
  • تحقق من إعدادات تطبيق OAuth في وحدة تحكم المزوّد
  • استخدم عناوين URL مطابقة تمامًا (بما في ذلك http/https)

البريد الإلكتروني مفقود

لا يشارك بعض المزوّدين البريد الإلكتروني افتراضيًا:

const { data: { user } } = await client.auth.getUser();

if (!user.email) {
  // اطلب البريد الإلكتروني بشكل منفصل أو اطلبه من المستخدم
  redirect('/auth/complete-profile');
}

تحديد معدل الطلبات

قد يحدّ مزوّدو OAuth من معدل الطلبات:

  • خزّن رموز OAuth مؤقتًا بالشكل المناسب
  • لا تُرسل طلبات تفويض مفرطة
  • تعامل مع أخطاء تجاوز معدل الطلبات بسلاسة

أفضل الممارسات

  1. اطلب الحد الأدنى من النطاقات - اطلب فقط ما تحتاج إليه
  2. تعامل مع الأخطاء بسلاسة - قد يفشل OAuth لأسباب عديدة
  3. تحقق من عناوين البريد الإلكتروني - لا يتحقق بعض المزوّدين من البريد الإلكتروني
  4. ادعم ربط الحسابات - اسمح للمستخدمين بربط عدة مزوّدين
  5. وفّر خيارًا بديلًا - قدّم دائمًا تسجيل الدخول بالبريد الإلكتروني/كلمة المرور كخيار احتياطي
  6. سجّل أحداث OAuth - تتبّع عمليات تسجيل الدخول ومحاولات الربط
  7. اختبر بدقة - اختبر باستخدام حسابات حقيقية لدى المزوّدين