אימות

כל בקשה (למעט נקודת הקצה של וידג'ט ההטמעה עצמו) עוברת אימות באחת משתי דרכים: JWT של צד ראשון (המשתמשים שלכם מתחברים דרך הממשק שלכם) או מפתח API (אינטגרציות בין שרתים). שתיהן מסתכמות באותו RequestContext — מזהה דייר, ואחריו חברות (membership) או תת-קבוצת ההרשאות של מפתח API — ולכן כל נקודת קצה מתנהגת בדיוק אותו הדבר, בלי קשר למי שקרא לה.

תהליך JWT לצד ראשון

Institflow היא מערכת רב-דיירית: חשבון משתמש אחד יכול להחזיק חברות בכמה מכונים, ולכן ההתחברות היא לחיצת יד בת שלושה שלבים ולא קריאה בודדת.

  1. POST /api/v1/auth/login עם email, password ו-client ("web" או "mobile"). התשובה נושאת accessToken קצר-מועד, את ה-user המחובר, וכל membership פעילה שהחשבון מחזיק. אם יש בדיוק אחת, activeTenantId כבר מוגדר ואפשר לדלג על שלב 2.
  2. POST /api/v1/auth/select-tenant עם ה-membershipId שנבחר — מחזיר accessToken חדש התחום לאותו דייר. הקשר הדייר של כל בקשה עוקבת מגיע מהטוקן הזה, לעולם לא מכותרת או מפרמטר שאילתה שאתם קובעים בעצמכם.
  3. POST /api/v1/auth/refresh מחליף את ה-refresh token ומנפיק accessToken חדש לפני שהישן פג. refresh token שמוצג פעמיים (שימוש חוזר — סימן חזק לכך שנגנב) מבטל מיד את כל משפחת הסשן (SESSION_REVOKED); התחברו מחדש.

שלחו את טוקן הגישה ככותרת Authorization: Bearer <accessToken> בכל בקשה. GET /api/v1/auth/me מחזיר את המשתמש הנוכחי, את החברויות שלו ואת רשימת ההרשאות האפקטיבית שלו בדייר הפעיל — שימושי לבניית ממשק הסשן שלכם בלי לגזור מחדש הרשאות בצד הלקוח.

מפתחות API סודיים

לאינטגרציות בין שרתים (סקריפט, משימת רקע, מערכת שותפה) צרו במקום זאת מפתח סודי: POST /api/v1/api-keys (דורש integration.manage) עם name ומערך permissions — תת-קבוצה ממש של ההרשאות האפקטיביות שלכם; לעולם אי אפשר להעניק למפתח יותר ממה שאתם עצמכם מחזיקים. המפתח המלא (ifl_live_<32 בתים אקראיים, base64url>) מוחזר פעם אחת בדיוק, ברגע היצירה — נשמרים ממנו רק הקידומת וגיבוב (hash), כך שאם איבדתם אותו, בטלו אותו וצרו חדש.

header
Authorization: Bearer ifl_live_9c1f2e7a4b8d3c6f0a5e9b2d7c4f1a8e

מפתח קשור לדייר של החברות שיצרה אותו ונושא רק את ה-permissions שלו עצמו — לא את מלוא ההרשאות שלכם — בכל בקשה שהוא מאמת. ביטול מפתח (DELETE /api/v1/api-keys/:id) נכנס לתוקף מיד, לא אחרי שמטמון כלשהו יפוג.

מפתחות לפרסום

סוג מפתח שני, ifl_pub_<…>, בטוח להטמעה בקוד צד לקוח — הוא יכול רק לפנות למשטח /api/v1/public/*: POST /public/registrations (נקודת הקצה שבה משתמש וידג'ט ההרשמה), POST /public/leads (פניות מטופס יצירת קשר), וקטלוג הקורסים לקריאה בלבד (GET /public/courses, /public/courses/:id, /public/branches — אלה מחזירים 404 עד שהמכון מפעיל את הקטלוג הציבורי). הוא נשלח בכותרת X-Api-Key, מוגבל לרשימת מקורות מאושרים שאתם מגדירים לכל מפתח, לא נושא שום הרשאת צוות, ומוגבל בקצב לפי כתובת IP ולפי מפתח. אל תשתמשו במפתח לפרסום לשום דבר אחר — הוא במכוון לא מסוגל לשום דבר אחר.

הכותרת Idempotency-Key

כל POST/PUT/PATCH/DELETE שנשלח עם מפתח API חייב לשאת כותרת Idempotency-Key (כל מחרוזת ייחודית — בדרך כלל UUID). קוראים עם JWT של צד ראשון יכולים לשלוח אותה גם, אבל אצלם היא רשות.

  • חסרה בפעולת שינוי שאומתה במפתח ← 428 IDEMPOTENCY_KEY_REQUIRED.
  • אותו מפתח, עם אותם method/נתיב/גוף כמו קודם ← התשובה המקורית מושמעת שוב בית-בבית (בלי ביצוע חוזר — בטוח לנסות שוב בקשה שפג לה הזמן בלי לדעת מה קרה).
  • אותו מפתח, עם method/נתיב/גוף שונים ← 409 IDEMPOTENCY_KEY_REUSED — השתמשתם שוב במפתח עבור בקשה אחרת.
  • אותו מפתח, שמגיע בזמן שהבקשה הראשונה עדיין רצה ← 409 IDEMPOTENCY_KEY_REUSED. הפעולה מתבצעת פעם אחת בלבד; נסו שוב אחרי שהקריאה הראשונה חוזרת.
  • גם 4xx נזכר — כישלון דטרמיניסטי מושמע שוב במקום להתבצע מחדש. לעומת זאת 5xx לעולם אינו נשמר במטמון: ניסיון חוזר באמת מבצע את הבקשה מחדש, וזה בדיוק מה שרוצים אחרי כשל בצד השרת.

המפתחות נזכרים למשך 24 שעות.

טביעת האצבע כוללת את מחרוזת השאילתה

שתי בקשות נחשבות “אותה בקשה” רק כאשר ה-method שלהן, הכתובת המלאה כולל מחרוזת השאילתה, והגוף זהים בית-בבית — טביעת האצבע היא sha256(METHOD + "\n" + url + "\n" + body). ניסיון חוזר שמסדר מחדש את פרמטרי השאילתה הוא בקשה אחרת עם אותו מפתח, ויקבל 409 במקום ההשמעה החוזרת שציפיתם לה. סדרו את הפרמטרים באותו אופן בכל פעם.

מגבלות קצב

כל בקשה מוגבלת בקצב על ידי חלון קבוע ב-Redis — לפי כתובת IP כשאין אימות, לפי משתמש לקוראים עם JWT, ולפי מפתח לקוראים עם מפתח API (600 בקשות לדקה כברירת מחדל). אפשר לתת למפתח תקציב צר משלו באמצעות rateLimitPerMin ב-POST /api/v1/api-keys או PATCH /api/v1/api-keys/{id} — שימושי למפתח שאתם מוסרים לצד שלישי. הוא לעולם לא יכול להיות רחב מתקרת התוכנית שלכם; בקשה ליותר מכך מחזירה 422. כל תשובה נושאת את מצב החלון הנוכחי, ו-RateLimit-Limit הוא תמיד התקציב האפקטיבי של אותו מפתח:

response headers
RateLimit-Limit: 600
RateLimit-Remaining: 587
RateLimit-Reset: 42

חריגה מהמגבלה מחזירה 429 עם קוד השגיאה RATE_LIMITED — המתינו עד שיחלפו מספר השניות שמופיע ב-RateLimit-Reset.

לעולם אל תשתפו מפתח סודי

מפתח סודי (ifl_live_…) מבצע אימות עם כל ההרשאות שהוענקו לו — התייחסו אליו כאל סיסמה. רק מפתח לפרסום (ifl_pub_…) בטוח בקוד שגלוי לדפדפן.