# تسجيل الدخول باستخدام URnetwork

دع الناس يقبلون تطبيقك أو وكيلك البرمجي في شبكتهم الخاصة، فيستطيع التنسيق مع
كل ما يشغّلونه هناك.

![زر تسجيل الدخول باستخدام URnetwork بنوعيه: الزر الداكن على سطح فاتح، والزر الفاتح على سطح داكن](/docs-assets/sign-in-with-urnetwork-buttons.svg)

[نزّل حزمة الزر](/docs-assets/sign-in-with-urnetwork-kit.zip) — الترميز،
والأنماط، والنوعان كلٌّ منهما في ملف مستقل، والعلامة وحدها. والحزمة نهائية
وتستطيع اعتمادها اليوم، قبل كل ما سواها في هذه الصفحة.

> **قيد التطوير — لم يُطلق بعد.** تصف هذه الصفحة تصميماً قيد البناء. فخادم
> التفويض مكتوب لكنه غير منشور: فاسم المضيف `auth.bringyour.com` لا يُحَلّ
> بعد، ولا يمكن اليوم استدعاء أيٍّ من نقاط النهاية أدناه. وتُصدَر معرّفات
> `client_id` عند الطلب لا بالخدمة الذاتية. وكل ما وُسم بأنه *مخطَّط له* غير
> مبنيّ. أما حزمة الزر فحقيقية ونهائية، وتستطيع اعتمادها الآن.

## ما هو

تسجيل الدخول هو خطوة القبول. وما يمنحه هو *مكانٌ على شبكة* — قابلٌ للعنونة،
ويمكن لبرمجيات ذلك الشخص الأخرى اكتشافه، ويستطيع هو إلغاءه في أي وقت.

وهذا يجعله شيئاً مختلفاً عن أزرار تسجيل الدخول التي يشبهها. فأنت لا تعرف، في
المقام الأول، مَن يكون شخصٌ ما. بل يُسمح لك بالدخول إلى مكانٍ ما.

وللمنحة نصفان، يُوافَق على كلٍّ منهما ويُلغى على حدة:

- **الهوية** — أن حساباً في URnetwork وافق عليك، ولا شيء أكثر.
- **الشبكة** — أيُّ شبكة يجوز لك أن تعمل عليها، مسمّاةً على شاشة الموافقة
  ومربوطةً في الرمز عند إصداره.

ويجوز لتطبيق أن يطلب الهوية وحدها. وتسجيل الدخول لا يعني ضمناً الوصول إلى
الشبكة أبداً.

## الزر

استخدم الزر الداكن على الأسطح الفاتحة ومتوسطة الدرجة، والزر الفاتح على
الأسطح الداكنة. ولا يوجد سكربت مستضاف: فالحزمة HTML وCSS ورسم SVG واحد
مضمَّن، فوضعُ الزر في صفحتك لا يُرسل شيئاً حتى ينقر عليه أحد.

وفضّل الترميز أدناه على ملفات SVG حيثما استطعت — فعندها يُعرض نص الزر بخط
صفحتك نفسها، ويبقى قابلاً للتحديد والبحث والترجمة. أما ملفات SVG المستقلة في
[الحزمة](/docs-assets/sign-in-with-urnetwork-kit.zip) فهي للمواضع التي لا
تقبل إلا صورة.

ويحمل الزر عبارة **تسجيل الدخول باستخدام** (Sign in with) يليها الشعار
المركّب (lockup) الخاص بـURnetwork. والجزء الأول وحده نصّ، وهذا ما يجعل
ترجمة الزر نظيفة: اعرض «Sign in with» بلغة واجهتك واترك الشعار المركّب على
حاله، فتُقرأ العلامة التجارية بالشكل نفسه في كل مكان.

```html
<a class="ur-signin" href="YOUR_AUTHORIZE_URL" aria-label="Sign in with URnetwork">
  <span>Sign in with</span>
  <svg class="ur-signin-lockup" viewBox="0 0 980 139" fill="currentColor" aria-hidden="true">
    <!-- the lockup, 10 paths — copy them from the kit -->
  </svg>
</a>
```

ويُرسم الشعار المركّب بـ`currentColor`، فيرث لون نص الزر، وتكفي نسخة واحدة
منه للنوعين. ويحمل `aria-label` الاسم كاملاً، لأن نصفه رسمٌ.

```css
.ur-signin {
  display: inline-flex; align-items: center; gap: 10px;
  height: 44px; padding: 0 20px; min-width: 200px;
  border-radius: 12px; border: 1px solid transparent;
  background: #101010; color: #f8f8f8;
  font: 500 15px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  text-decoration: none; white-space: nowrap; cursor: pointer;
}
.ur-signin:hover         { background: #1c1c1c; }
.ur-signin:focus-visible { outline: 2px solid #0099ff; outline-offset: 2px; }
.ur-signin-lockup        { height: 16px; width: auto; flex: none; }

/* Light variant, for dark surfaces */
.ur-signin.is-light       { background: #f8f8f8; color: #101010; border-color: rgba(16,16,16,.14); }
.ur-signin.is-light:hover { background: #ececec; }
```

ويستخدم نص الزر مجموعةَ خطوط النظام بدل خط علامة URnetwork التجارية، الذي لا
يجوز إعادة توزيعه على مواقع أخرى — كما أن اشتراط خط ويب كان سيعيد طلبَ شبكة
إلى الزر.

### قواعد العلامة التجارية

| | |
| --- | --- |
| الارتفاع | 44px حداً أدنى — وهو أيضاً هدف اللمس. لا أصغر أبداً. |
| نصف قطر الزاوية | 12px |
| الشعار المركّب | ارتفاعه 16px، و`currentColor`، ولا يُعاد رسمه أو تلوينه أو بناؤه من أجزاء أبداً |
| المساحة الخالية | 8px على الجوانب الأربعة كلها |
| حلقة التركيز | 2px بلون `#0099ff`، بإزاحة 2px |
| الخلفية | `#101010` للداكن، و`#f8f8f8` للفاتح. لا لون آخر، ولا تدرّج. |

لا تكتب كلمة URnetwork بخطّك أنت بدل الشعار المركّب، ولا تستخدم العلامة من
دون نص الزر، ولا تُحرّك الزر ولا تُمِله. وترجم «Sign in with» إلى لغة
واجهتك؛ أما الشعار المركّب فلا يتغير أبداً.

## كيف يعمل

*مخطَّط له. نقاط النهاية أدناه غير منشورة.*

تدفق رمز التفويض (authorization code) مع PKCE، ولا شيء غيره — فهذا
OAuth 2.1، فلا توجد منحة ضمنية (implicit grant)، و`S256` إلزامي.

| | |
| --- | --- |
| الجهة المُصدِرة | `https://auth.bringyour.com` |
| الاكتشاف | `/.well-known/openid-configuration` و`/.well-known/oauth-authorization-server` |
| المفاتيح | `/.well-known/jwks.json` |
| التفويض | `https://ur.io/authorize` |
| الرمز | `POST /oauth/token` |
| معلومات المستخدم (UserInfo) | `GET /oauth/userinfo` |
| الإلغاء | `POST /oauth/revoke` |

وتقع نقطة نهاية التفويض على أصلٍ (origin) غير أصل الجهة المُصدِرة، عن قصد.
فشاشة الموافقة يجب أن تعيد استخدام الجلسة التي سُجّل الدخول فيها، وتخزين
المتصفح مقيّد بالأصل، فتسكن الصفحة حيث توجد الجلسة بالفعل. ويجيز RFC 8414
ذلك، ولا تتأثر هوية الجهة المُصدِرة — فواصل التحقق من `iss` مقابل
`auth.bringyour.com`.

وتُعرض شاشة الموافقة دائماً في أول مرة تطلب فيها مجموعة معيّنة من النطاقات
(scopes)، حتى لو كان الشخص قد سجّل دخوله بالفعل. فالموافقة على طرف ثالث بصمت
كانت ستجعل الصفحة «نائباً مرتبكاً» (confused deputy).

## الانضمام إلى الشبكة

*مخطَّط له.*

رمز الوصول في OAuth يفوّض؛ لكنه لا يتكلم بروتوكول `connect`. ولكي تشغل
المكان الذي مُنحته على الشبكة، استبدله:

```
POST /network/auth-client
Authorization: Bearer <access token>
```

فتتسلّم رمز JWT للمنصة يحمل `clientId` — وهو العنوان الذي يعرفك به
البروتوكول. ومن هناك أنت عضو عادي في تلك الشبكة، و`/network/peers` هو طريقتك
لاكتشاف ما قُبل فيها غيرك.

وثلاثة أمور تفاجئ الناس:

1. هذا هو العبور الوحيد بين نظامَي الاعتمادات، وهو قرار تفويض يُتخذ على
   الخادم، لا رمزٌ واحد يُتحقَّق منه في موضعين. وتبقى مجموعتا مفاتيح التوقيع
   لا تتقاطعان.
2. تجهيز عميل **يُحمَّل على فاتورة الشبكة**. وشاشة الموافقة تقول ذلك؛ ونكرره
   هنا كي لا يعرفه أحد من فاتورة.
3. الاعتماد مقصور على الشبكة الواحدة التي رُبط بها عند التفويض. ولا يتبع
   أحداً إلى شبكة أخرى أبداً.

وحين يحتاج تطبيقان إلى معرفة أنهما يخدمان الشخص نفسه، فالمرجع المشترك بينهما
هو الشبكة التي قُبل فيها كلاهما — لا الشخص. وهذا مقصود: فالشبكة موافَقٌ
عليها، ومسمّاة على الشاشة، وقابلة للإلغاء، بينما لن يكون معرّفُ مستخدمٍ دائم
أياً من ذلك.

## الرموز

*مخطَّط له.*

| الرمز | العمر | ملاحظات |
| --- | --- | --- |
| رمز الوصول | ساعة واحدة | جمهوره (audience) مورد واحد |
| رمز التحديث | 90 يوماً، بمدة منزلقة | مبهم ومتناوب — كل استخدام يُبطل سابقه |
| رمز الهوية (ID token) | ساعة واحدة | موجَّه إلى `client_id` الخاص بك؛ لا ترسله أبداً إلى خادم موارد |

وتتناوب رموز التحديث، وإعادة استخدام رمز مُبطَل تُعامَل بوصفها سرقة: فتُلغى
عائلة الرموز كلها. فاحفظ أحدث رمز أُعطيته.

## متطلبات تطبيقك

- **PKCE مع `S256`.** ليس اختيارياً.
- **عناوين URI لإعادة التوجيه بمطابقة تامة**، مسجّلة مسبقاً، وبـHTTPS وحده.
  ويجوز للتطبيقات الأصلية استخدام `http://127.0.0.1:<port>/…`.
- **تحقّق من `iss`** في استجابة التفويض — فهذا ما يحمي من هجمات الخلط
  (mix-up).
- **تحقّق من أن `aud`** في رمز الهوية يساوي `client_id` الخاص بك.
- **لا تقدّم رمزاً من تسجيل الدخول باستخدام URnetwork إلى API المشغّل.**
  فمجموعتا المفاتيح لا تتقاطعان؛ ولا يمكن أن ينجح ذلك، وهذا هو التصميم لا
  ثغرةٌ فيه.

## الحصول على `client_id`

الإصدار يخضع للمراجعة، وليس مفتوحاً. فالتطبيق المقبول يستطيع مخاطبة أجهزة
شخصٍ ما، ولذلك يُفحص العملاء قبل أن يستطيعوا طلب الهوية أو الوصول إلى
الشبكة. ويبقى التسجيل الديناميكي متاحاً للنطاقات الخاصة بـMCP وحدها، حيث لا
يتجاوز المدى البحثَ عن مزوّد.

ولا توجد بعد لوحة تحكم للخدمة الذاتية. اطلب `client_id` وسنُصدر لك واحداً.

## الإلغاء

*مخطَّط له.*

يدير الناس المنح من شاشة التطبيقات المتصلة (connected apps) في تطبيق
URnetwork، وإلغاء إحداها يزيل عملاء الشبكة الذين جهّزتهم. وما يلاحظه تطبيقك
هو سقوط جلسة الاتصال وكفّ `clientId` عن أن يُحَلّ — وهذا إلغاء، لا عطل.

وتعيش رموز الوصول ساعة، ولذلك يسري الإلغاء في غضون الساعة لا فوراً.
