تنفيذ OAuth2 في Django و FastAPI: دليل عملي كامل

تنفيذ OAuth2 في Django و FastAPI: دليل عملي كامل

في السنوات الأخيرة أصبح بروتوكول OAuth2 هو المعيار الفعلي لتنفيذ تسجيل الدخول عبر حسابات خارجية مثل Google و GitHub. في هذا الدليل سنشرح آلية عمل OAuth2 بشكل مبسط، ثم ننتقل إلى تطبيقه عمليًا في كل من Django وFastAPI لتسجيل الدخول باستخدام Google و GitHub، مع التركيز على الجوانب الأمنية وأفضل الممارسات.

الكلمة المفتاحية التي سنبني عليها الشرح هي: OAuth2 Django FastAPI، وسنحاول تغطية كل ما تحتاجه لتطبيق تسجيل الدخول الخارجي في مشاريعك الحالية أو القادمة.

ما هو OAuth2؟ شرح مبسط

OAuth2 هو بروتوكول تفويض (Authorization Protocol)، يسمح لتطبيقك بالوصول إلى بيانات مستخدم في خدمة خارجية (مثل Google أو GitHub) بدون أن يعرف تطبيقك كلمة مرور المستخدم. بدلًا من ذلك يعتمد التطبيق على رمز وصول Access Token يتم إصداره من مزود الهوية (Google, GitHub...).

المفاهيم الأساسية في OAuth2

  • Client (التطبيق العميل): تطبيقك (Django أو FastAPI) الذي يريد الوصول إلى بيانات المستخدم.
  • Resource Owner (مالك المورد): المستخدم نفسه.
  • Authorization Server: مثل Google أو GitHub؛ يقوم بعملية تسجيل الدخول وإصدار الرموز.
  • Redirect URI: عنوان URL يعود إليه المستخدم بعد تسجيل الدخول في مزود الهوية.
  • Authorization Code: كود مؤقت يعيده مزود الهوية إلى تطبيقك، يستخدم لاستبداله بـ Access Token.
  • Access Token: رمز يثبت أن تطبيقك مخوّل للوصول إلى بيانات معينة للمستخدم.

كيف تعمل عملية تسجيل الدخول باستخدام OAuth2؟

  1. المستخدم يضغط على زر "تسجيل الدخول عبر Google" في موقعك.
  2. تطبيقك يعيد توجيه المستخدم إلى صفحة تسجيل الدخول الخاصة بـ Google مع بعض البيانات (client_id, redirect_uri, scope...).
  3. المستخدم يدخل بياناته ويوافق على منح الصلاحية لتطبيقك.
  4. Google يعيد المستخدم إلى redirect_uri عندك مع authorization code.
  5. سيرفر تطبيقك يرسل هذا الكود إلى Google من الخلفية ويطلب Access Token.
  6. بعد حصوله على Access Token، يطلب تطبيقك بيانات المستخدم (البريد، الاسم...) من Google.
  7. يتم إنشاء حساب (أو ربطه) في قاعدة بياناتك، وتسجيل الدخول للمستخدم في تطبيقك.

قبل أن تبدأ في تطبيق OAuth2 في Django أو FastAPI يُفضل أن تكون لديك خلفية عن مصادقة المستخدمين وإدارة الجلسات في Django، ويمكنك مراجعة: دليل شامل حول إطار Django لبناء تطبيقات الويب.

إعداد بيانات OAuth2 في Google و GitHub

قبل كتابة الكود، تحتاج إلى إنشاء تطبيق في لوحة التحكم الخاصة بكل مزود هوية.

إنشاء تطبيق OAuth2 في Google

  1. اذهب إلى Google Cloud Console.
  2. أنشئ مشروعًا جديدًا أو اختر مشروعًا موجودًا.
  3. من قسم APIs & Services > Credentials، اختر Create Credentials > OAuth client ID.
  4. اختر نوع التطبيق (عادة Web application).
  5. أضف Authorized redirect URIs مثل:
    • http://localhost:8000/auth/google/callback/ لمشروع Django.
    • http://localhost:8000/auth/google/callback لمشروع FastAPI.
  6. احفظ Client ID وClient Secret.

إنشاء تطبيق OAuth في GitHub

  1. اذهب إلى GitHub > Settings > Developer settings > OAuth Apps.
  2. اختر New OAuth App.
  3. حدد:
    • Homepage URL مثل: http://localhost:8000.
    • Authorization callback URL مثل:
      • http://localhost:8000/auth/github/callback/ لـ Django.
      • http://localhost:8000/auth/github/callback لـ FastAPI.
  4. احفظ Client ID وClient Secret.

تنفيذ OAuth2 في Django باستخدام مكتبة social-auth-app-django

في Django يمكننا إما أن نطبق تدفق OAuth2 يدويًا، أو نعتمد على مكتبة جاهزة. الأكثر استخدامًا هي: social-auth-app-django التي تبسط الكثير من التفاصيل.

تثبيت وإعداد المكتبة في Django

أولًا، قم بتثبيت الحزمة:

pip install social-auth-app-django

ثم أضفها إلى INSTALLED_APPS في settings.py:

INSTALLED_APPS = [
    ...
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'social_django',
    ...
]

أضف Authentication Backends التي ستستخدم OAuth2:

AUTHENTICATION_BACKENDS = (
    'social_core.backends.google.GoogleOAuth2',
    'social_core.backends.github.GithubOAuth2',
    'django.contrib.auth.backends.ModelBackend',
)

أضف إعدادات المفاتيح (يمكن وضعها في متغيرات بيئة واستخدام os.environ):

SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = 'GOOGLE_CLIENT_ID'
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = 'GOOGLE_CLIENT_SECRET'

SOCIAL_AUTH_GITHUB_KEY = 'GITHUB_CLIENT_ID'
SOCIAL_AUTH_GITHUB_SECRET = 'GITHUB_CLIENT_SECRET'

أضف مسارات social_django في urls.py الرئيسي:

from django.urls import path, include

urlpatterns = [
    ...
    path('auth/', include('social_django.urls', namespace='social')),
]

الآن ستجد مسارات مثل: /auth/login/google-oauth2/ و /auth/complete/google-oauth2/ وغيرها متاحة تلقائيًا.

تهيئة قوالب تسجيل الدخول في Django

في قالب تسجيل الدخول يمكنك إضافة أزرار لتسجيل الدخول عبر Google و GitHub:

<a href="{% url 'social:begin' 'google-oauth2' %}">
    تسجيل الدخول باستخدام Google
</a>

<a href="{% url 'social:begin' 'github' %}">
    تسجيل الدخول باستخدام GitHub
</a>

عند الضغط على الزر، سيتم توجيه المستخدم إلى Google أو GitHub، وبعد التفويض سيعود إلى تطبيقك، والمكتبة ستتعامل تلقائيًا مع إنشاء/تحديث المستخدم وتسجيل الدخول.

التخزين في قاعدة البيانات والتخصيص

تقوم social-auth-app-django بإنشاء جداول لإدارة الارتباط بين المستخدمين المحليين وحساباتهم على مزودات OAuth2. يمكنك تشغيل:

python manage.py migrate

إذا أردت تخصيص عملية إنشاء المستخدم (مثل ربطه بجروب أو ضبط صلاحيات)، يمكنك استخدام SOCIAL_AUTH_PIPELINE في settings.py وإضافة دوال مخصصة.

الحماية والأمان في Django مع OAuth2

  • استخدم HTTPS في بيئة الإنتاج لتفادي تسريب Tokens.
  • لا تحفظ Access Tokens في ملفات السجلات أو تعرضها في رسائل الخطأ.
  • احفظ client_secret في متغيرات البيئة وليس في الكود مباشرة.
  • استخدم Middlewares للتحقق من حالة المستخدم أو تطبيق قيود دخول إضافية. لمزيد من التفاصيل يمكنك الاطلاع على: شرح طريقة استخدام Middleware في Django مع أمثلة.

تنفيذ OAuth2 يدويًا في FastAPI مع Google و GitHub

في FastAPI يمكننا استخدام مكتبة httpx أو requests للتعامل مع مزودات OAuth2 يدويًا، أو الاعتماد على مكتبات طرف ثالث. هنا سنشرح الفكرة الأساسية يدويًا مع Google و GitHub.

تثبيت المتطلبات

pip install fastapi uvicorn[standard] httpx python-multipart

هيكل أساسي لمشروع FastAPI

# main.py
from fastapi import FastAPI, Request
from fastapi.responses import RedirectResponse, HTMLResponse
import httpx
import os

app = FastAPI()

GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
GOOGLE_CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
GOOGLE_REDIRECT_URI = "http://localhost:8000/auth/google/callback"

GITHUB_CLIENT_ID = os.getenv("GITHUB_CLIENT_ID")
GITHUB_CLIENT_SECRET = os.getenv("GITHUB_CLIENT_SECRET")
GITHUB_REDIRECT_URI = "http://localhost:8000/auth/github/callback"

إنشاء مسار بدء تسجيل الدخول مع Google في FastAPI

@app.get("/auth/google/login")
async def google_login():
    google_auth_endpoint = "https://accounts.google.com/o/oauth2/v2/auth"
    scope = "openid email profile"
    redirect_uri = (
        f"{google_auth_endpoint}"
        f"?client_id={GOOGLE_CLIENT_ID}"
        f"&response_type=code"
        f"&redirect_uri={GOOGLE_REDIRECT_URI}"
        f"&scope={scope}"
        f"&access_type=offline"
        f"&prompt=consent"
    )
    return RedirectResponse(redirect_uri)

سيتم إعادة توجيه المستخدم إلى شاشة الدخول في Google، وبعد الموافقة سيتم إرجاعه إلى /auth/google/callback مع ?code=....

مسار Callback لاستبدال الكود بـ Access Token

@app.get("/auth/google/callback")
async def google_callback(request: Request):
    code = request.query_params.get("code")
    if not code:
        return HTMLResponse("لم يتم توفير كود التفويض", status_code=400)

    token_endpoint = "https://oauth2.googleapis.com/token"
    async with httpx.AsyncClient() as client:
        token_response = await client.post(
            token_endpoint,
            data={
                "code": code,
                "client_id": GOOGLE_CLIENT_ID,
                "client_secret": GOOGLE_CLIENT_SECRET,
                "redirect_uri": GOOGLE_REDIRECT_URI,
                "grant_type": "authorization_code",
            },
            headers={"Content-Type": "application/x-www-form-urlencoded"},
        )

    token_data = token_response.json()
    access_token = token_data.get("access_token")
    if not access_token:
        return HTMLResponse("فشل في الحصول على Access Token", status_code=400)

    # طلب بيانات المستخدم
    userinfo_endpoint = "https://www.googleapis.com/oauth2/v2/userinfo"
    async with httpx.AsyncClient() as client:
        userinfo_response = await client.get(
            userinfo_endpoint,
            headers={"Authorization": f"Bearer {access_token}"},
        )

    userinfo = userinfo_response.json()
    email = userinfo.get("email")
    name = userinfo.get("name")

    # في هذه النقطة يمكنك:
    # 1- البحث عن المستخدم في قاعدة البيانات
    # 2- إنشاء مستخدم جديد إن لم يكن موجودًا
    # 3- إصدار JWT أو إنشاء جلسة (حسب تصميم تطبيقك)

    content = f"مرحبًا {name} - بريدك: {email}"
    return HTMLResponse(content)

تسجيل الدخول باستخدام GitHub في FastAPI

نكرر الفكرة نفسها مع GitHub مع اختلاف الروابط ونوع البيانات.

@app.get("/auth/github/login")
async def github_login():
    github_auth_endpoint = "https://github.com/login/oauth/authorize"
    redirect_uri = (
        f"{github_auth_endpoint}"
        f"?client_id={GITHUB_CLIENT_ID}"
        f"&redirect_uri={GITHUB_REDIRECT_URI}"
        f"&scope=user:email"
    )
    return RedirectResponse(redirect_uri)

مسار Callback لـ GitHub

@app.get("/auth/github/callback")
async def github_callback(request: Request):
    code = request.query_params.get("code")
    if not code:
        return HTMLResponse("لم يتم توفير كود التفويض", status_code=400)

    token_endpoint = "https://github.com/login/oauth/access_token"
    async with httpx.AsyncClient() as client:
        token_response = await client.post(
            token_endpoint,
            data={
                "client_id": GITHUB_CLIENT_ID,
                "client_secret": GITHUB_CLIENT_SECRET,
                "code": code,
                "redirect_uri": GITHUB_REDIRECT_URI,
            },
            headers={"Accept": "application/json"},
        )

    token_data = token_response.json()
    access_token = token_data.get("access_token")
    if not access_token:
        return HTMLResponse("فشل في الحصول على Access Token من GitHub", status_code=400)

    # طلب بيانات المستخدم
    user_endpoint = "https://api.github.com/user"
    async with httpx.AsyncClient() as client:
        user_response = await client.get(
            user_endpoint,
            headers={"Authorization": f"Bearer {access_token}"},
        )

    userinfo = user_response.json()
    username = userinfo.get("login")
    email = userinfo.get("email")

    content = f"مرحبًا {username} - البريد: {email}"
    return HTMLResponse(content)

يمكنك في هذه المرحلة ربط عملية تسجيل الدخول بنظام JWT أو نظام جلسات خاص بك. إذا كنت تبني API فقط، قد تفضّل إصدار JWT Token للمستخدم بعد إتمام OAuth2 واستخدامه في طلبات API اللاحقة.

دمج OAuth2 مع أنظمة المصادقة في Django و FastAPI

في Django

  • بعد عودة المستخدم من Google أو GitHub، تنشئ المكتبة مستخدم Django عادي في جدول auth_user.
  • يمكنك استخدام نظام الصلاحيات (Permissions & Groups) المدمج في Django بعد تسجيل الدخول.
  • تعمل الجلسات Session بشكل عادي، وكل ما عليك هو حماية المسارات باستخدام @login_required أو LoginRequiredMixin.

في FastAPI

  • FastAPI لا يحتوي على نظام مستخدمين مدمج مثل Django، لذا ستحتاج إلى تصميم جدول مستخدمين بنفسك.
  • بعد اكتمال تسجيل الدخول الخارجي، يمكنك:
    • إرجاع JWT يحتوي على معرف المستخدم.
    • أو إنشاء Cookie Session وربطه بمستخدم في قاعدة البيانات.
  • FastAPI مصمم ليعمل بكفاءة عالية مع البرمجة غير المتزامنة async/await، ويمكنك الاستفادة من ذلك في طلبات HTTP إلى Google و GitHub. للتعمق أكثر يمكنك قراءة: البرمجة غير المتزامنة في بايثون: تحسين الأداء باستخدام async و await.

أفضل الممارسات الأمنية مع OAuth2 Django FastAPI

بما أن موضوع OAuth2 حساس ويتعامل مع بيانات المستخدمين، فهناك نقاط ينبغي الانتباه لها:

  • استخدام HTTPS دائمًا في بيئة الإنتاج حتى لا يتم اعتراض Access Token أو Authorization Code.
  • تخزين المفاتيح الحساسة مثل client_secret في متغيرات البيئة أو أنظمة إدارة الأسرار (Secrets Manager)، وعدم رفعها في Git.
  • تحديد Redirect URIs بدقة في لوحات Google/GitHub لتجنب إعادة التوجيه إلى مواقع خبيثة.
  • تقييد scopes إلى أقل ما تحتاجه من بيانات (لا تطلب صلاحيات لا تستخدمها).
  • تحديث المكتبات الأمنية لديك باستمرار (Django, FastAPI, httpx, social-auth-app-django).
  • التأكد من التحقق من حالة المستخدم بعد تسجيل الدخول (هل هو مفعّل؟ هل لديه صلاحية الوصول لهذا الجزء من النظام؟).

متى أستخدم Django ومتى أستخدم FastAPI مع OAuth2؟

إذا كنت تركز على:

  • تطبيقات ويب تقليدية مع صفحات HTML وجلسات Session وتكامل جاهز مع نظام مستخدمين: فـ Django مع social-auth-app-django خيار ممتاز وسريع التهيئة.
  • APIs عالية الأداء أو خدمات مايكروسيرفس، وتعتمد على JWT وتتعامل غالبًا مع JSON: فـ FastAPI مع تنفيذ يدوي أو مكتبات OAuth2 طرف ثالث سيكون أكثر مرونة.

إذا كان مشروعك يعتمد على Django REST Framework وتريد إضافة OAuth2 فوقه، فاقرأ: دليل شامل لإطار Django REST مع أمثلة، ثم اربط بين المصادقة في DRF ونتائج تسجيل الدخول عبر Google/GitHub.

خلاصة

تنفيذ OAuth2 Django FastAPI لم يعد أمرًا معقدًا كما كان في السابق، بفضل المكتبات الجاهزة ودعم البروتوكول في أغلب مزودات الهوية. في هذا الدليل:

  • فهمنا آلية عمل OAuth2 والمفاهيم الأساسية مثل Access Token و Authorization Code.
  • قمنا بإعداد تسجيل الدخول عبر Google و GitHub في Django باستخدام social-auth-app-django مع خطوات عملية.
  • طبّقنا تسجيل الدخول الخارجي في FastAPI يدويًا باستخدام httpx ومسارات login/callback.
  • تطرقنا لأهم الجوانب الأمنية وأفضل الممارسات في إدارة المفاتيح والـ Redirect URIs و scopes.

يمكنك الآن دمج OAuth2 في مشاريعك سواء كانت مبنية على Django أو FastAPI، وتوفير تسجيل دخول سلس وآمن للمستخدمين عبر حساباتهم في Google أو GitHub مع الحفاظ على التحكم الكامل في بياناتهم داخل تطبيقك.

حول المحتوى:

شرح آلية OAuth2 وكيفية تنفيذ تسجيل الدخول باستخدام Google و GitHub داخل Django وFastAPI مع أمثلة عملية وجوانب الأمان.

هل كان هذا مفيدًا لك؟

أضف تعليقك