# כניסה מאוחדת (SSO)

> התחברות ל-Nodaro באירוח עצמי דרך ספק זהות מהימן: הגדירו את EXTERNAL_SSO_PROVIDERS, בחרו כללי קישור חשבונות ואפשרו התחברות רק דרך SSO.

Source: https://nodaro.ai/he/docs/self-hosting/sso

**כניסה מאוחדת (SSO)** מאפשרת למשתמשים להתחבר ל-Nodaro שלכם באירוח עצמי דרך ספק זהות חיצוני ומהימן. מתארים כל ספק במשתנה `EXTERNAL_SSO_PROVIDERS`, ודף ההתחברות מציג כפתור SSO. החשבון שהמשתמש מגיע אליו הוא משתמש רגיל בהתקנה שלכם: SSO הוא רק דרך להתחיל סשן. הדף הזה עוסק בצד של מנהל השרת; הפרוטוקול מתואר בדף [התחברות חיצונית (SSO)](https://nodaro.ai/docs/developers/sso) למפתחים.

## איזה סוג ספק לבחור
| סוג | מתי להשתמש בו | סטטוס |
| --- | --- | --- |
| `assertion` | המנפיק שלכם לא מדבר OpenID Connect או SAML, אבל יכול לחתום על JWT קצר מועד, למשל אפליקציה שמטמיעה את Nodaro. Nodaro מאמת את האסימון ומחליף אותו בסשן רגיל. | עובד מקצה לקצה. |
| `oidc` | ספק הזהות שלכם מדבר OpenID Connect ומוגדר ב-Supabase Auth של ההתקנה שלכם. הדפדפן עובר הפניית התחברות רגילה של Supabase, ו-Supabase מאמת את התוצאה. | השדה `id` של הספק חייב להיות השם של ספק ה-OAuth ב-Supabase Auth שלכם, כמו `keycloak` או `azure`. |
| `saml` | — | הרשומות עוברות את בדיקת התקינות, אבל דף ההתחברות לא יכול לשלוח דומיין SAML, ולכן אף אחד לא יכול להתחבר דרכן. השתמשו ב-`assertion` או ב-`oidc`. |

SSO כבוי כברירת מחדל. כשהמשתנה `EXTERNAL_SSO_PROVIDERS` לא מוגדר, דף ההתחברות לא מציג כפתור SSO, הנתיב `GET /v1/sso/providers` מחזיר רשימה ריקה, וכל נתיב של ספק מחזיר `404 unknown_provider`.

## הגדרת הספקים
הגדירו את `EXTERNAL_SSO_PROVIDERS` כמערך JSON, ישירות בערך המשתנה, או כ-`@/path/to/providers.json` כדי לקרוא אותו מקובץ.

```json
[
{
"id": "librechat",
"label": "LibreChat",
"kind": "assertion",
"secret": "a-dedicated-32+char-hmac-key-not-the-idp-session-secret",
"audience": "nodaro",
"claimMap": { "email": "email", "emailVerified": "email_verified", "subject": "sub" },
"initiateUrl": "https://chat.example.com/oauth/nodaro",
"maxLifetimeSeconds": 300
},
{
"id": "keycloak",
"label": "Acme (Keycloak)",
"kind": "oidc",
"supabaseProvider": "keycloak"
}
]
```

ערך פגום **עוצר את האתחול**. שגיאת הקלדה ברשומה שיש בה סוד לעולם לא אמורה להשמיט ספק בשקט, מה שהיה נראה כמו כשל של SSO עם `404`, או להשאיר את ההתחברות מוגדרת למחצה.

במערך השירותים של Compose, הוסיפו את `EXTERNAL_SSO_PROVIDERS` ואת `EXTERNAL_SSO_LINK_EXISTING` תחת `environment:` של השירות `nodaro`, כי קובץ ה-Compose לא מעביר אותם מהקובץ `.env`. כדי להשתמש בקובץ, מפו אותו לתוך הקונטיינר וציינו את הנתיב שלו בתוך הקונטיינר.

### השדות של ספק
| שדה | חל על | מה הוא עושה |
| --- | --- | --- |
| `id` | כל הסוגים | שם שבטוח לשימוש ב-URL: אות לטינית קטנה או ספרה בהתחלה, ואחריה אותיות קטנות, ספרות, `_` או `-`, בין 1 ל-63 תווים בסך הכול, בלי נקודות. השם מופיע בנתיב `/v1/sso/<id>` ונשמר ברשומת המשתמש. חייב להיות ייחודי. |
| `label` | כל הסוגים | שם הספק. הוא מופיע על כפתור ההתחברות רק כשיש שני ספקים או יותר, ופרופיל הממשק לא מגדיר `auth.ssoLabel`. |
| `kind` | כל הסוגים | `assertion`, `oidc` או `saml`. |
| `secret` | `assertion`, נדרש | מפתח ה-HS256 שמאמת את החתימה של ה-assertion, באורך של 16 תווים לפחות. השתמשו במפתח ייעודי, ואף פעם לא בסוד הסשנים של ספק הזהות עצמו, כדי שדליפה בצד של Nodaro לא תאפשר לזייף את הסשנים של הספק. |
| `audience` | `assertion`, נדרש | הערך שה-claim `aud` של ה-assertion חייב להיות שווה לו. |
| `claimMap` | `assertion` | אילו claims מעבירים את האימייל, את דגל האימות ואת ה-subject. ברירת המחדל היא `{ "email": "email", "emailVerified": "email_verified", "subject": "sub" }`. |
| `initiateUrl` | `assertion` | כתובת URL מלאה. כשמשתמש לוחץ על כפתור ההתחברות, Nodaro מפנה אותו לשם. המשתמש מתחבר אצל הספק, והספק חוזר עם ה-assertion. בלי השדה הזה, לחיצה על הכפתור נדחית עם `400 no_assertion`. |
| `initiateUrlByHost` | `assertion` | מיפוי של שם מארח, באותיות קטנות ובלי פורט, לכתובת URL מלאה. לחיצה שמגיעה למארח הזה מופנית לכתובת שלו במקום ל-`initiateUrl`. מיועד להתקנות שעונות בכמה שמות מארח. מפתח שגוי עוצר את האתחול, והמיפוי אף פעם לא מתפרסם. |
| `maxLifetimeSeconds` | `assertion` | אורך החיים המרבי של assertion, `exp` פחות `iat`. ברירת המחדל היא `300`, כלומר 5 דקות, והמקסימום הוא `3600`. |
| `supabaseProvider` | `oidc` | עובר בדיקת תקינות, אבל לא בשימוש: דף ההתחברות מעביר ל-Supabase את ה-`id` של הספק. |
| `domain` | `oidc`, `saml` | עובר בדיקת תקינות, אבל לא בשימוש. רשומה מסוג `oidc` או `saml` צריכה `domain` או `supabaseProvider`. |

רק השדות `id`, `label` ו-`kind` מתפרסמים, דרך `GET /v1/sso/providers`. הסוד אף פעם לא יוצא מהשרת.

## מה Nodaro בודק ב-assertion
שרת הזהות של ספק מסוג `assertion` חותם על JWT ושולח את הדפדפן אל `GET /v1/sso/<id>?assertion=<jwt>`. Nodaro מקבל את ה-assertion רק כשכל התנאים האלה מתקיימים:

- ה-assertion חתום ב-**HS256** ובערך `secret` של הספק.
- **`aud`** שווה לערך `audience` של הספק.
- **`exp`** קיים ועדיין לא עבר, עם סובלנות של 5 שניות, ואורך החיים לא חורג מ-`maxLifetimeSeconds`.
- **`jti`** קיים ואף פעם לא נעשה בו שימוש. כל assertion עובד פעם אחת.
- ה-claim של **האימייל** קיים. ה-claim של **האימות** חייב להיות `true` כדי ליצור חשבון או לקשר חשבון.

assertion שנכשל באחת הבדיקות נדחה עם `401`. הנתיב מקבל עד 20 בקשות ב-60 שניות מכתובת IP אחת, ובקשות נוספות נדחות עם `429`. החוזה המלא, קודי השגיאה וזרימת ההפניות מתוארים בדף [התחברות חיצונית (SSO)](https://nodaro.ai/docs/developers/sso) למפתחים.

## איך חשבונות מקושרים
כש-assertion עובר את הבדיקות, Nodaro מוצא את המשתמש המתאים או יוצר אותו. הכללים מבטיחים ש-assertion לעולם לא יוכל להשתלט על חשבון קיים רק כי יש לו אותה כתובת אימייל.

| מצב | תוצאה |
| --- | --- |
| אין חשבון עם האימייל הזה, והאימייל מאומת | נוצר משתמש חדש, והוא מקושר לספק. |
| אין חשבון עם האימייל הזה, והאימייל לא מאומת | הבקשה נדחית: `403 email_unverified`. |
| החשבון כבר מקושר לספק הזה | המשתמש מתחבר. |
| החשבון מקושר לספק אחר | הבקשה נדחית: `403 account_linked_other_provider`, גם עם `EXTERNAL_SSO_LINK_EXISTING=true`. |
| קיים חשבון מקומי בלי SSO, `EXTERNAL_SSO_LINK_EXISTING=true`, והאימייל מאומת | החשבון מקושר לספק, והמשתמש מתחבר. |
| קיים חשבון מקומי בלי SSO, והדגל הוא `false` או שהאימייל לא מאומת | הבקשה נדחית: `403 account_exists`. |

ברירת המחדל של `EXTERNAL_SSO_LINK_EXISTING` היא `false`, וזו ההגדרה שמגינה מפני השתלטות. רק הערכים `true` או `1`, באותיות גדולות או קטנות, מפעילים אותו. הפעילו אותו רק כשאתם סומכים על ה-claims של אימייל מאומת שהספקים שלכם שולחים, עד כדי כך שאתם מוכנים לקשר אותם לחשבונות קיימים.

הקודים `account_exists` ו-`email_unverified` מנוסחים אותו דבר לכל חשבון, ולכן אי אפשר להשתמש בטופס ההתחברות כדי לגלות אילו כתובות קיימות. יומן השרת רושם לאיזה חשבון כוונה הבקשה.

## כפתור ההתחברות
דף ההתחברות מציג כפתור SSO כשמוגדר לפחות ספק אחד.

- **ספק אחד:** על הכפתור כתוב **כניסה מאוחדת (SSO)**, או הערך של `auth.ssoLabel` בפרופיל הממשק.
- **כמה ספקים:** כל כפתור מציג את ה-`label` של הספק שלו, אלא אם פרופיל הממשק מגדיר `auth.ssoLabel`.

אחרי ההתחברות, המשתמש מגיע ל-`/projects`. הספק יכול לשלוח אותו לדף אחר אם הוא מוסיף `&next=<path>` כשהוא מפנה בחזרה. Nodaro מכבד את הפרמטר הזה רק כשהוא נתיב יחסי באותו origin, שמתחיל ב-`/` אבל לא ב-`//`. כל ערך אחר מוביל ל-`/projects`. במארחים שמופיעים ב-`CORS_ORIGIN`, ההפניה לדף הנחיתה נשארת יחסית, במארח שהמשתמש הגיע אליו.

## התחברות דרך SSO בלבד
במהדורת Business, פרופיל הממשק יכול להסיר את שאר שיטות ההתחברות:

```bash
NODARO_SURFACE_PROFILE={"auth":{"methods":["sso"],"ssoLabel":"Sign in with Acme"}}
```

רשימה שכוללת רק את `sso` מפעילה גם כלל בצד השרת: כל חשבון מחובר צריך להיות חשבון שנוצר או קושר דרך SSO. כל סשן אחר נדחה בקריאה הראשונה שלו ל-API עם `403 sso_required`, כך שחשבון שנרשם ישירות בשירות ההתחברות לא יכול להשתמש בהתקנה. הוספת `email` לרשימה משביתה את הכלל. ראו את הסעיף [שיטות התחברות](https://nodaro.ai/docs/self-hosting/editions-and-profiles#sign-in-methods) בדף מהדורות ופרופילי ממשק.

## סיכום האבטחה
- **סוד ייעודי** לכל ספק, שונה מסוד הסשנים של הספק עצמו.
- **assertions לשימוש חד־פעמי**, עם אורך חיים קצר שהשרת אוכף.
- **הפניות רק לאותו origin** אחרי ההתחברות.
- **אף סוד לא יוצא מהשרת.** ה-assertions והאסימונים החד־פעמיים מוסרים מיומני הבקשות.
- **מגבלות קצב** על נתיב ההחלפה, לכל כתובת IP.
- **סשנים רגילים.** אחרי ההחלפה, המשתמש הוא משתמש רגיל, בלי פרטי גישה מיוחדים.

## שאלות נפוצות

### האם הכניסה המאוחדת (SSO) מופעלת כברירת מחדל?

לא. כשהמשתנה EXTERNAL_SSO_PROVIDERS לא מוגדר, דף ההתחברות לא מציג כפתור SSO, וכל נתיב SSO מחזיר 404. כדי להפעיל SSO, הגדירו לפחות ספק אחד.

### איזה סוג ספק כדאי להגדיר?

השתמשו בסוג assertion למנפיק שיכול להנפיק אסימון חתום קצר מועד, כמו אפליקציה שמטמיעה את Nodaro. השתמשו בסוג oidc לספק זהות שהגדרתם ב-Supabase Auth של ההתקנה שלכם. רשומות מהסוג saml עוברות את בדיקת התקינות, אבל אף אחד לא יכול להתחבר דרכן.

### האם SSO יכול להשתלט על חשבון קיים עם אותו אימייל?

לא כברירת מחדל. הערך של EXTERNAL_SSO_LINK_EXISTING הוא false, ולכן assertion לאימייל שכבר יש לו חשבון מקומי נדחה עם account_exists. הגדירו אותו ל-true רק כשאתם סומכים על ה-claims של אימייל מאומת שהספקים שלכם שולחים.

### איך מאפשרים התחברות רק דרך SSO?

במהדורת Business, הגדירו בפרופיל הממשק את auth.methods ל-‎["sso"]‎, יחד עם auth.ssoLabel. כל חשבון חייב אז לעבור דרך SSO, וכל סשן אחר נדחה עם 403 sso_required.

### מה קורה אם יש טעות ב-EXTERNAL_SSO_PROVIDERS?

ההתקנה מסרבת לעלות. ערך פגום אף פעם לא משמיט ספק בשקט ולא משאיר את ההתחברות מוגדרת למחצה.
