TutuId

دليل الدمج

أضِف «الدخول عبر Tutu» إلى منتجك في أربع خطوات باستخدام OAuth2 + PKCE.

أربع خطوات للدمج

1

الواجهة الأمامية تبدأ الدخول

أنشئ نسخة عبر createTutuId({ baseUrl, clientId }) من @tutu/id-sdk واستدعِ idSdk.startLogin() عند النقر. تُولّد SDK زوج PKCE وتحوّل إلى صفحة تفويض TutuId.

2

المستخدم يُصرّح في TutuId

في شاشة الموافقة /oauth/authorize ينقر المستخدم «سماح» (تُتخطّى تلقائيًا إن سبق التفويض). بعد تحقّق TutuId من client_id وقائمة redirect_uri المسموح بها، يعيد التوجيه إلى /auth/callback لديك مع رمز تفويض.

3

صفحة الاستدعاء تجلب الـ code

في /auth/callback استدعِ idSdk.completeCallback() للحصول على { code, codeVerifier }. سلّم هاتين القيمتين إلى خادمك الخلفي، ولا تبادل الـ token مباشرة في المتصفح.

4

الخادم الخلفي يبادل الـ token ← ينشئ الحساب

يبادل خادمك code + codeVerifier بـ access_token، ويستدعي /userinfo للحصول على { sub, email, emailVerified, ... }، ثم يربط أو ينشئ حسابًا محليًا حسب sub، ويُصدر جلستك الخاصة.

★ الحماية من الاستيلاء على الحساب (إلزامي)

خادمك الخلفي لا يجوز له ربط أو إنشاء حساب محلي إلا حسب sub (المعرّف الفريد للحساب). وفقط عندما emailVerified === true يمكنك ربط حساب محلي قائم بالبريد؛ وإلا فأنشئ حسابًا مستقلًا واترك البريد فارغًا.

السبب: قد يسجّل مهاجم حساب Tutu مسبقًا ببريد الضحية؛ والدمج ببريد غير مُتحقَّق يسلّم حساب الضحية المحلي. هذا خط أحمر بمستوى CVE.

مرجع واجهة SDK

@tutu/id-sdk · من جانب العميل بالكامل، بلا اعتماديات (يتطلب Web Crypto + sessionStorage).

createTutuId({ baseUrl, clientId, redirectUri? })

ينشئ نسخة SDK. القيمة الافتراضية لـ redirectUri هي ${location.origin}/auth/callback وتتكيّف مع dev/prod.

startLogin(scope?)

يُولّد زوج PKCE ويحوّل إلى صفحة التفويض، بادئًا «الدخول عبر Tutu».

trySilentLogin(scope?)

الدخول الصامت: إن وُجدت جلسة مركزية وتفويض سابق يبادل الرمز بسلاسة؛ وإلا يعود فورًا إلى حالة الخروج (دون مقاطعة المستخدم).

completeCallback()

يحلّل النتيجة في صفحة الاستدعاء: عند النجاح يعيد { code, codeVerifier }، وعند الفشل/الإلغاء يعيد { error }.

logoutEverywhere(returnTo?)

يُنهي الجلسة المركزية ويسجّل الخروج بالتتابع من كل منتجات الحزمة، ثم يعود إلى returnTo.

تريد رؤيته عمليًا أولًا؟