# אימות

> בחירת דרך האימות של ה-SDK של Nodaro עם StaticTokenAuth, CallbackAuth או supabaseAuth, ושיתוף התחברות אחת בדפדפן בין תת־הדומיינים שלכם.

Source: https://nodaro.ai/he/docs/developers/sdk/auth

**ספק אימות** אומר ל-SDK של Nodaro איזה אסימון לשלוח. לפני כל בקשה, הלקוח קורא ל-`getToken()` של הספק ושולח את התוצאה כ-`Authorization: Bearer <token>`. כשהספק מחזיר `null`, הבקשה נשלחת בלי הכותרת, כבקשה אנונימית.

## בחירת ספק אימות
| ספק האימות | למה הוא מתאים | מאיפה האסימון מגיע |
| --- | --- | --- |
| [`StaticTokenAuth`](#statictokenauth) | קוד בשרת, סקריפטים, משימות מתוזמנות | אסימון קבוע: אסימון API או אסימון גישה של OAuth |
| [`CallbackAuth`](#callbackauth) | אסימונים עם תוקף מוגבל שצריך לרענן, מאגרי סשנים משלכם | הפונקציה שלכם, שנקראת לפני כל בקשה |
| [`supabaseAuth`](#supabaseauthsupabase) | אפליקציית דפדפן שהמשתמשים שלה מתחברים לאותו מופע Nodaro | הסשן הפעיל של המשתמש, שמתרענן אוטומטית |
| אובייקט משלכם | כל דבר אחר | כל אובייקט עם מתודה `getToken()` |

## האסימונים שאפשר להשתמש בהם
| אסימון | איך הוא נראה | בשם מי הוא פועל | מאיפה מקבלים אותו |
| --- | --- | --- | --- |
| אסימון API | `ndr_` ואחריו 64 תווים הקסדצימליים | בשמכם | **הגדרות › מפתחות API** ב-Nodaro |
| אסימון גישה של OAuth | `ndr_app_` ואחריו 64 תווים הקסדצימליים | בשם משתמש שאישר את האפליקציה שלכם | החלפת הקוד של OAuth באסימון, [`client.oauth.exchangeCode()`](https://nodaro.ai/docs/developers/sdk/developer-apps) |
| אסימון סשן | סשן מחובר | בשם המשתמש המחובר | ההתחברות ל-Nodaro, דרך `supabaseAuth` |

- **אסימון API** משמש כפרטי גישה קבועים, בלי תקרת הוצאה. האסימון עובד עד שתשביתו או תמחקו אותו, ולכן שמרו אותו בשרת. המגבלות והגדרות מגבלת הקצב מפורטות בדף ה-API [אימות](https://nodaro.ai/docs/developers/api/authentication).
- **אסימון גישה של OAuth** כולל רק את ההרשאות שהמשתמש אישר. השתמשו בו כשהאפליקציה שלכם פועלת בשם אנשים אחרים. ראו את הדף [אפליקציות OAuth](https://nodaro.ai/docs/developers/oauth).
- **בהתקנה של מהדורת Community באירוח עצמי**, האפליקציה לא מציעה אסימוני API. התחברו והשתמשו במקומם באסימון הסשן שלכם, עם `supabaseAuth` או עם `CallbackAuth`.

## הממשק Auth
```ts
interface Auth {
getToken(): Promise<string | null>
}
```

כל אובייקט במבנה הזה יכול לשמש כאפשרות `auth` של `createClient`. שלושת הספקים שבהמשך מממשים את הממשק הזה.

## StaticTokenAuth
```ts
new StaticTokenAuth(token: string)
```

עוטף אסימון קבוע אחד. השתמשו בו כשהאסימון לא משתנה כל זמן שהקוד שלכם רץ: אסימון API, או אסימון גישה של OAuth שהשרת שלכם קיבל בזרימת ה-authorization code.

<TypeTable
type={{
token: {
type: 'string',
required: true,
description: "האסימון שנשלח עם כל בקשה: אסימון API שמתחיל ב-ndr_‎ או אסימון גישה של OAuth שמתחיל ב-ndr_app_‎.",
},
}}
/>

```ts

const client = createClient({
baseUrl: "https://app.nodaro.ai",
auth: new StaticTokenAuth(process.env.NODARO_TOKEN!),
})
```

## CallbackAuth
```ts
new CallbackAuth(fn: () => string | null | Promise<string | null>)
```

קורא לפונקציה שלכם לפני כל בקשה ושולח את האסימון שהיא מחזירה. הפונקציה יכולה להיות סינכרונית או אסינכרונית. החזירו `null` כדי לשלוח את הבקשה בלי אסימון.

<TypeTable
type={{
fn: {
type: '() => string | null | Promise<string | null>',
required: true,
description: "מחזירה את האסימון לבקשה הבאה, או null לבקשה אנונימית.",
},
}}
/>

השתמשו בו כדי לרענן אסימונים, לקרוא ממאגר סשנים משלכם או להחליף פרטי גישה מעת לעת:

```ts

const client = createClient({
baseUrl: "https://app.nodaro.ai",
auth: new CallbackAuth(async () => {
const session = await sessionStore.read()
if (!session) return null
if (Date.now() > session.expiresAt - 60_000) {
await refresh(session)
}
return session.accessToken
}),
})
```

## supabaseAuth(supabase)‎
```ts
supabaseAuth(supabase: SupabaseLikeClient): Auth
```

לוקחת את האסימון של המשתמש המחובר מלקוח Supabase v2 לפני כל בקשה. השתמשו בה באפליקציית דפדפן שהמשתמשים שלה מתחברים לאותו מופע Nodaro, למשל פרונטאנד משלכם להתקנה באירוח עצמי. העורך של Nodaro משתמש באותו ספק. האסימון נקרא בזמן אמת, ולכן סשן שהתרענן נקלט אוטומטית.

<TypeTable
type={{
supabase: {
type: 'SupabaseLikeClient',
required: true,
description: "לקוח Supabase v2, או כל אובייקט שהמתודה auth.getSession()‎ שלו מחזירה את הסשן הנוכחי. שום מתודה אחרת לא נקראת.",
},
}}
/>

```ts

const supabase = createSupabase(
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_ANON_KEY,
)

const client = createClient({
baseUrl: import.meta.env.VITE_API_URL ?? "",
auth: supabaseAuth(supabase),
})
```

כשאף אחד לא מחובר, הבקשה נשלחת בלי אסימון.

## createSharedSupabaseClient(options)‎
```ts

createSharedSupabaseClient<Db = any>(options: {
url: string
anonKey: string
cookieDomain?: string
}): SupabaseClient<Db>
```

יוצרת לקוח Supabase לדפדפן ששומר את הסשן ב-**cookies** במקום ב-local storage. עם `cookieDomain`, כמה אפליקציות בתת־דומיינים של אותו דומיין חולקות התחברות אחת: משתמש שמתחבר באחת מהן מחובר בכולן, והתנתקות באחת מהן מנתקת את המשתמש מכולן.

<TypeTable
type={{
url: {
type: 'string',
required: true,
description: "כתובת ה-URL של פרויקט ה-Supabase של מופע Nodaro.",
},
anonKey: {
type: 'string',
required: true,
description: "המפתח הציבורי האנונימי של הפרויקט הזה.",
},
cookieDomain: {
type: 'string',
description: "דומיין אב, למשל ‎.example.com, כדי לשתף את הסשן בין כל תת־הדומיינים שלו. השמיטו אותו כדי שה-cookies יישארו בדומיין הנוכחי בלבד.",
},
}}
/>

```ts

const supabase = createSharedSupabaseClient({
url: SUPABASE_URL,
anonKey: SUPABASE_ANON_KEY,
cookieDomain: ".example.com",
})

const client = createClient({ baseUrl: "", auth: supabaseAuth(supabase) })
```

- האפשרות `cookieDomain` חלה רק כשהדומיין של הדף הוא הדומיין הזה או אחד מתת־הדומיינים שלו. בכל דומיין אחר, כמו `localhost` או כתובת של סביבת תצוגה מקדימה, ה-cookies נשארים בדומיין הנוכחי, ולכן בפיתוח מקומי יש לכל origin סשן נפרד.
- בטעינה הראשונה, סשן קיים ב-local storage עובר ל-cookie, והרשומה הישנה נמחקת. משתמש שכבר היה מחובר נשאר מחובר. סשנים שפג תוקפם נמחקים.
- הייצוא הזה נמצא בנתיב נפרד, `@nodaro/sdk/supabase`, ולכן החבילה הראשית לא תלויה ב-Supabase. כדי להשתמש בו, התקינו את `@supabase/supabase-js` ואת `@supabase/ssr`.

## הרשאות (scopes) והרשאות חסרות
אסימון גישה של OAuth כולל את ההרשאות שהמשתמש אישר, כמו `workflows:read` או `workflows:execute`. בדף של כל משאב מצוינת ההרשאה שהמתודות שלו צריכות. כשלאסימון חסרה הרשאה, המתודה זורקת `ForbiddenError`, והשדה `missingScope` של השגיאה מציין את ההרשאה החסרה:

```ts

try {
await client.workflows.run(workflowId)
} catch (err) {
if (err instanceof ForbiddenError && err.missingScope) {
requestConsentFor([err.missingScope]) // send the user through OAuth again
} else {
throw err
}
}
```

אסימוני API ואסימוני סשן לא מוגבלים בהרשאות. כל מחלקות השגיאה מפורטות בדף [שגיאות](https://nodaro.ai/docs/developers/sdk/errors).

## כללים לדפדפן
- **לעולם אל תשימו אסימון API בקוד של דפדפן.** כל אחד יכול לקרוא אותו מהדף, והוא פועל בשמכם.
- **אסימוני OAuth בדפדפן** עובדים רק מ-origin שמופיע ב-`allowedOrigins` של יישום המפתחים שלכם. ראו את הדף [OAuth ויישומי מפתחים](https://nodaro.ai/docs/developers/sdk/developer-apps).
- **אסימוני סשן מ-`supabaseAuth`** לא נבדקים מול הרשימה הזו.
- **שמרו סודות בשרת.** החלפת הקוד של OAuth באסימון דורשת את סוד הלקוח שלכם, ולכן הריצו את [`client.oauth.exchangeCode()`](https://nodaro.ai/docs/developers/sdk/developer-apps) רק בקוד שרץ בשרת.

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

### באיזה ספק אימות כדאי להשתמש בשרת?

בשרת, השתמשו בספק האימות StaticTokenAuth. העבירו לו אסימון API שמתחיל ב-ndr_‎ או אסימון גישה של OAuth שמתחיל ב-ndr_app_‎, וטענו את האסימון ממשתנה סביבה.

### איך מרעננים ב-SDK אסימון שתוקפו עומד לפוג?

כדי לרענן אסימון שתוקפו עומד לפוג, השתמשו בספק האימות CallbackAuth. ה-SDK קורא לפונקציה שלכם לפני כל בקשה, כך שהפונקציה יכולה לבדוק את מועד התפוגה, לרענן את האסימון ולהחזיר את האסימון החדש.

### האם אפשר לשים אסימון API באפליקציית דפדפן?

לא. אסימון API פועל בשמכם, בלי תקרת הוצאה, וכל אחד יכול לקרוא אותו מהקוד שרץ בדפדפן. בדפדפן, תנו למשתמשים להתחבר והשתמשו בסשן שלהם, או השתמשו ב-OAuth.

### מה המשמעות של ForbiddenError.missingScope?

השדה ForbiddenError.missingScope אומר שאסימון ה-OAuth תקף, אבל לא קיבל את ההרשאה שנקודת הקצה צריכה, למשל workflows:execute. בקשו מהמשתמש לאשר את ההרשאה הזו, ואז נסו שוב עם האסימון החדש.
