חיבור מאובטח של נתונים באמצעות הרשאה ואימות

Firebase Data Connect מספק אבטחה איתנה בצד הלקוח באמצעות:

  • הרשאה ללקוחות בנייד ובאינטרנט
  • אמצעי בקרה ספציפיים להרשאות ברמת השאילתה וברמת המוטציה
  • אימות אפליקציות באמצעות Firebase App Check.

Data Connect מרחיב את האבטחה הזו באמצעות:

  • הרשאה בצד השרת
  • אבטחת משתמשים בפרויקט Firebase וב-Cloud SQL באמצעות IAM.

מתן הרשאה לשאילתות ולמוטציות של לקוחות

Data Connect משולב באופן מלא עם Firebase Authentication, כך שתוכלו להשתמש בנתונים עשירים על משתמשים שמקבלים גישה לנתונים שלכם (אימות) בתכנון של הנתונים שהמשתמשים האלה יכולים לגשת אליהם (הרשאה).

Data Connect מספק הוראה @auth לשאילתות ולמוטציות שמאפשרת להגדיר את רמת האימות הנדרשת כדי לאשר את הפעולה. במדריך הזה נתאר את ההנחיה @auth עם דוגמאות.

בנוסף, Data Connect תומך בהרצה של שאילתות שמוטמעות במוטציות, כך שתוכלו לאחזר קריטריונים נוספים להרשאה ששמרתם במסד הנתונים, ולהשתמש בקריטריונים האלה בהוראות @check כדי להחליט אם מוטציות מצורפות מורשות. במקרה הזה של הרשאה, ההנחיה @redact מאפשרת לכם לקבוע אם תוצאות השאילתה יחזרו ללקוחות בפרוטוקול הנתונים, והשאילתה המוטמעת תושמט בערכות ה-SDK שנוצרו. מבוא להנחיות האלה, עם דוגמאות

הסבר על ההנחיה @auth

אפשר להגדיר פרמטרים להנחיה @auth כך שתתאים לאחת מכמה רמות גישה מוגדרות מראש, שמכסות תרחישים גישה נפוצים רבים. הרמות האלה נעות בין PUBLIC (שמאפשרת שאילתות ומוטציות מכל הלקוחות ללא אימות מכל סוג שהוא) לבין NO_ACCESS (שמגבילה שאילתות ומוטציות מחוץ לסביבות שרת בעלות הרשאות באמצעות Firebase Admin SDK). כל אחת מהרמות האלה קשורה לתהליכי האימות ש-Firebase Authentication מספק.

רמה הגדרה
PUBLIC כל אחד יכול לבצע את הפעולה, עם או בלי אימות.
PUBLIC כל אחד יכול לבצע את הפעולה, עם או בלי אימות.
USER_ANON כל משתמש מזוהה, כולל משתמשים שנכנסו לחשבון באופן אנונימי באמצעות Firebase Authentication, מורשה לבצע את השאילתה או את המוטציה.
USER כל משתמש שנכנס באמצעות Firebase Authentication מורשה לבצע את השאילתה או את המוטציה, מלבד משתמשים לא רשומים שנכנסו לחשבון.
USER_EMAIL_VERIFIED כל משתמש שנכנס לחשבון באמצעות Firebase Authentication עם כתובת אימייל מאומתת מורשה לבצע את השאילתה או את המוטציה.
NO_ACCESS לא ניתן לבצע את הפעולה הזו מחוץ להקשר של Admin SDK.

רמות הגישה המוגדרות מראש האלה משמשות כנקודת מוצא להגדרת בדיקות הרשאה מורכבות וחזקות בהוראה @auth באמצעות מסנני where וביטויים של Common Expression Language‏ (CEL) שמתבצעת להם הערכה בשרת.

שימוש בהנחיה @auth להטמעת תרחישים נפוצים של הרשאות

רמות הגישה המוגדרות מראש הן נקודת ההתחלה של ההרשאה.

רמת הגישה USER היא הרמה הבסיסית הכי שימושית להתחלה.

גישה מאובטחת לחלוטין תתבסס על רמה USER, בתוספת מסננים וביטויים שבודקים מאפייני משתמשים, מאפייני משאבים, תפקידים ובדיקות אחרות. הרמות USER_ANON ו-USER_EMAIL_VERIFIED הן וריאציות של המקרה USER.

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

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

במדריך הזה מפורטות עכשיו דוגמאות לפיתוח ב-USER וב-PUBLIC.

דוגמה מעוררת השראה

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

סביר להניח שפלטפורמה כזו תיצור מודלים של Users ושל Posts.

type User @table(key: "uid") {
  uid: String!
  name: String
  birthday: Date
  createdAt: Timestamp! @default(expr: "request.time")
}

type Post @table {
  author: User!
  text: String!
  # "one of 'draft', 'public', or 'pro'"
  visibility: String! @default(value: "draft")
  # "the time at which the post should be considered published. defaults to
  # immediately"
  publishedAt: Timestamp! @default(expr: "request.time")
  createdAt: Timestamp! @default(expr: "request.time")
  updatedAt: Timestamp! @default(expr: "request.time")
}

משאבים בבעלות המשתמש

מומלץ לכתוב ב-Firebase מסננים וביטויים שבודקים את הבעלות של משתמש על משאב, במקרים הבאים, הבעלות על Posts.

בדוגמאות הבאות, נתונים מאסימוני אימות נקרא ומשווים באמצעות ביטויים. התבנית הרגילה היא להשתמש בביטויים כמו where: {authorUid: {eq_expr: "auth.uid"}} כדי להשוות בין authorUid ששמור לבין auth.uid (מזהה המשתמש) שהוענק באסימון האימות.

יצירה

כדי להשתמש בשיטת האימות הזו, צריך להוסיף את הערך auth.uid מאסימון האימות לכל Post חדש בתור שדה authorUid, כדי לאפשר השוואה בבדיקות האימות הבאות.

# Create a new post as the current user
mutation CreatePost($text: String!, $visibility: String) @auth(level: USER) {
  post_insert(data: {
    # set the author's uid to the current user uid
    authorUid_expr: "auth.uid"
    text: $text
    visibility: $visibility
  })
}
עדכון

כשלקוח מנסה לעדכן Post, אפשר לבדוק את auth.uid שהועברו מול authorUid השמור.

# Update one of the current user's posts
mutation UpdatePost($id: UUID!, $text: String, $visibility: String) @auth(level:USER) {
  post_update(
    # only update posts whose author is the current user
    first: { where: {
      id: {eq: $id}
      authorUid: {eq_expr: "auth.uid"}
    }}
    data: {
      text: $text
      visibility: $visibility
      # insert the current server time for updatedAt
      updatedAt_expr: "request.time"
    }
  )
}
מחיקה

אותה טכניקה משמשת לאישור פעולות מחיקה.

# Delete one of the current user's posts
mutation DeletePost($id: UUID!) @auth(level: USER) {
  post_delete(
    # only delete posts whose author is the current user
    first: { where: {
      id: {eq: $id}
      authorUid: {eq_expr: "auth.uid"}
    }}
  )
}
# Common display information for a post
fragment DisplayPost on Post {
  id, text, createdAt, updatedAt
  author { uid, name }
}
רשימה
# List all posts belonging to the current user
query ListMyPosts @auth(level: USER) {
  posts(where: {
    userUid: {eq_expr: "auth.uid"}
  }) {
    # See the fragment above
    ...DisplayPost
    # also show visibility since it is user-controlled
    visibility
  }
}
קבל
# Get a post only if it belongs to the current user
query GetMyPost($id: UUID!) @auth(level: USER) {
  post(key: {id: $id},
    first: {where: {
      id: {eq: $id}
      authorUid: {eq_expr: "auth.uid"}}
      }}, {
      # See the fragment above
      ...DisplayPost
      # also show visibility since it is user-controlled
      visibility
  }
}

סינון הנתונים

מערכת ההרשאות של Data Connect מאפשרת לכתוב מסננים מתוחכמים בשילוב עם רמות גישה מוגדרות מראש כמו PUBLIC, וגם באמצעות נתונים מאסימוני אימות.

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

סינון לפי מאפייני המשאבים

כאן, ההרשאה לא מבוססת על אסימוני אימות כי רמת האבטחה הבסיסית מוגדרת ל-PUBLIC. עם זאת, אנחנו יכולים להגדיר באופן מפורש רשומות במסד הנתונים שלנו כמתאימות לגישה ציבורית. נניח שיש לנו Post רשומות במסד הנתונים עם הערך visibility שמוגדר כ'ציבורי'.

# List all posts marked as 'public' visibility
query ListPublicPosts @auth(level: PUBLIC) {
  posts(where: {
    # Test that visibility is "public"
    visibility: {eq: "public"}
    # Only display articles that are already published
    publishedAt: {lt_expr: "request.time"}
  }) {
    # see the fragment above
    ...DisplayPost
  }
}
סינון לפי הצהרות של משתמשים

כאן נניח שהגדרתם הצהרות משתמש בהתאמה אישית שמעבירות אסימוני אימות כדי לזהות משתמשים בתוכנית 'פרו' באפליקציה, שמסומנים בשדה auth.token.plan באסימון האימות. הביטויים שלכם יכולים לבדוק את השדה הזה.

# List all public or pro posts, only permitted if user has "pro" plan claim
query ProListPosts @auth(expr: "auth.token.plan == 'pro'") {
  posts(where: {
    # display both public posts and "pro" posts
    visibility: {in: ['public', 'pro']},
    # only display articles that are already published
    publishedAt: {lt_expr: "request.time"},
  }) {
    # see the fragment above
    ...DisplayPost
    # show visibility so pro users can see which posts are pro\
    visibility
  }
}
סינון לפי סדר + מגבלה

לחלופין, יכול להיות שהגדרתם את השדה visibility ברשאות Post כדי לזהות שהן מכילות תוכן שזמין למשתמשים 'פרו', אבל כדי להציג תצוגה מקדימה או טיזר של הנתונים, תוכלו להגביל עוד יותר את מספר הרשומות שיוחזרו.

# Show 2 oldest Pro post as a preview
query ProTeaser @auth(level: USER) {
  posts(
    where: {
      # show only pro posts
      visibility: {eq: "pro"}
      # that have already been published more than 30 days ago
      publishedAt: {lt_time: {now: true, sub: {days: 30}}}
    },
    # order by publish time
    orderBy: [{publishedAt: DESC}],
    # only return two posts
    limit: 2
  ) {
    # See the fragment above
    ...DisplayPost
  }
}
סינון לפי תפקיד

אם הצהרת ה-Claim בהתאמה אישית מגדירה תפקיד admin, תוכלו לבדוק ולתת הרשאה לפעולות בהתאם.

# List all posts unconditionally iff the current user has an admin claim
query AdminListPosts @auth(expr: "auth.token.admin == true") {
  posts { ...DisplayPost }
}

הסבר על ההנחיות @check ו-@redact

ההנחיה @check מאמתת ששדות מסוימים נמצאים בתוצאות של השאילתה. ביטוי של Common Expression Language ‏ (CEL) משמש לבדיקה של ערכי השדות. ברירת המחדל של ההנחיה היא לבדוק אם יש צמתים עם ערך null ולדחות אותם.

ההנחיה @redact מסננת חלק מהתגובה מהלקוח. עדיין מתבצעת הערכה של שדות שחוסמים את המידע כדי לזהות תופעות לוואי (כולל שינויים בנתונים ו-@check), והתוצאות עדיין זמינות לשלבים מאוחרים יותר בביטויים של CEL.

ב-Data Connect, ההנחיות @check ו-@redact משמשות בדרך כלל בהקשר של בדיקות הרשאה. אפשר לעיין בדיון לגבי חיפוש של נתוני הרשאה.

הוספת ההנחיות @check ו-@redact לחיפוש נתוני הרשאה

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

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

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

# MoviePermission
# Suppose a user has an authorization role with respect to records in the Movie table
type MoviePermission @table(key: ["doc", "userId"]) {
  movie: Movie! # implies another field: movieId: UUID!
  userId: String! # Can also be a reference to a User table, doesn't matter
  role: String!
}

בהטמעה לדוגמה הבאה, המוטציה UpdateMovieTitle כוללת את השדה query כדי לאחזר נתונים מ-MoviePermission, ואת ההוראות הבאות כדי להבטיח שהפעולה מאובטחת וחזקה:

  • הוראה @transaction כדי לוודא שכל השאילתות והבדיקות של ההרשאות יושלמו או יכשלו באופן אטומי.
  • ההנחיה @redact להשמטת תוצאות השאילתה מהתגובה, כלומר בדיקת ההרשאה מתבצעת בשרת Data Connect אבל מידע אישי רגיש לא נחשף ללקוח.
  • שתי הנחיות @check להערכת לוגיקה של הרשאה בתוצאות של שאילתות, למשל בדיקה של תפקיד מתאים של מזהה משתמש נתון לביצוע שינויים.

mutation UpdateMovieTitle($movieId: UUID!, $newTitle: String!) @auth(level: USER) @transaction {
  # Step 1: Query and check
  query @redact {
    moviePermission( # Look up a join table called MoviePermission with a compound key.
      key: {movieId: $movieId, userId_expr: "auth.uid"}
    # Step 1a: Use @check to test if the user has any role associated with the movie
    # Here the `this` binding refers the lookup result, i.e. a MoviePermission object or null
    # The `this != null` expression could be omitted since rejecting on null is default behavior
    ) @check(expr: "this != null", message: "You do not have access to this movie") {
      # Step 1b: Check if the user has the editor role for the movie
      # Next we execute another @check; now `this` refers to the contents of the `role` field
      role @check(expr: "this == 'editor'", message: "You must be an editor of this movie to update title")
    }
  }
  # Step 2: Act
  movie_update(id: $movieId, data: {
    title: $newTitle
  })
}

דפוסים שליליים שכדאי להימנע מהם בנוגע להרשאות

בקטע הקודם מוסבר על דפוסים שכדאי לפעול לפיהם כשמשתמשים בהנחיה @auth.

חשוב גם להכיר דפוסים שליליים חשובים שצריך להימנע מהם.

הימנעות מעברת מזהי מאפייני משתמשים ופרמטרים של אסימוני אימות בארגומנטים של שאילתות ומוטציות

Firebase Authentication הוא כלי רב-עוצמה להצגת תהליכי אימות ולתיעוד מאובטח של נתוני אימות, כמו מזהי משתמשים רשומים ומספר שדות שמאוחסנים באסימוני אימות.

לא מומלץ להעביר מזהי משתמשים ונתוני אסימוני אימות בארגומנטים של שאילתות ושל טרנספורמציות.

# Antipattern!
# This incorrectly allows any user to view any other user's posts
query AllMyPosts($userId: String!) @auth(level: USER) {
  posts(where: {authorUid: {eq: $userId}}) {
    id, text, createdAt
  }
}

הימנעות משימוש ברמת הגישה USER ללא מסננים

כפי שצוין כמה פעמים במדריך, רמות הגישה הבסיסיות כמו USER,‏ USER_ANON ו-USER_EMAIL_VERIFIED הן נקודות התחלה ורמות בסיס לבדיקות ההרשאה, שאפשר לשפר באמצעות מסננים וביטויים. שימוש ברמות האלה ללא מסנן או ביטוי תואם שבודק איזה משתמש מבצע את הבקשה, שווה ערך לשימוש ברמה PUBLIC.

# Antipattern!
# This incorrectly allows any user to view all documents
query ListDocuments @auth(level: USER) {
  documents {
    id
    title
    text
  }
}

אין להשתמש ברמת הגישה PUBLIC או USER ליצירת אב טיפוס

כדי לזרז את הפיתוח, יכול להיות שתתפתו להגדיר את כל הפעולות לרמת הגישה PUBLIC או לרמת הגישה USER בלי שיפורים נוספים כדי לאשר את כל הפעולות ולאפשר לכם לבדוק את הקוד במהירות.

אחרי שתיצרו אב טיפוס ראשוני מאוד בדרך הזו, תוכלו להתחיל לעבור מ-NO_ACCESS לאישור מוכן לייצור באמצעות רמות PUBLIC ו-USER. עם זאת, אל תפרסו אותם בתור PUBLIC או USER בלי להוסיף לוגיקה נוספת, כפי שמתואר במדריך הזה.

# Antipattern!
# This incorrectly allows anyone to delete any post
mutation DeletePost($id: UUID!) @auth(level: PUBLIC) {
  post: post_delete(
    id: $id,
  )
}

שימוש ב-Firebase App Check לאימות אפליקציות

אימות והרשאה הם רכיבים קריטיים באבטחה של Data Connect. אימות והרשאה בשילוב עם אימות אפליקציות יוצרים פתרון אבטחה חזק מאוד.

באמצעות אימות באמצעות Firebase App Check, במכשירים שבהם פועלת האפליקציה שלכם ישתמש ספק אימות של אפליקציה או מכשיר, שמאמת שהפעולות של Data Connect מגיעות מהאפליקציה המקורית, והבקשות מגיעות ממכשיר מקורי שלא בוצע בו שינוי. האימות הזה מצורף לכל בקשה שהאפליקציה שולחת אל Data Connect.

בסקירה הכללית על App Check מוסבר איך מפעילים את App Check ב-Data Connect ומוסיפים את ה-SDK של הלקוח לאפליקציה.

רמות האימות להנחיה @auth(level)

בטבלה הבאה מפורטות כל רמות הגישה הרגילות והמקבילות שלהן ב-CEL. רמות האימות מפורטות מהרחבה לצרה – כל רמה כוללת את כל המשתמשים שתואמים לרמות הבאות.

רמה הגדרה
PUBLIC כל אחד יכול לבצע את הפעולה, עם או בלי אימות.

שיקולים: כל משתמש יכול לקרוא או לשנות את הנתונים. Firebase ממליצה על רמת ההרשאה הזו לנתונים שגלויים לכולם, כמו כרטיסי מוצר או מדיה. דוגמאות ושיטות מומלצות לחלופות

זהה ל-@auth(expr: "true")

לא ניתן להשתמש במסננים ובביטויים של @auth בשילוב עם רמת הגישה הזו. כל ביטוי כזה ייכשל עם שגיאת 400 בקשה שגויה.
USER_ANON כל משתמש מזוהה, כולל משתמשים שנכנסו לחשבון באופן אנונימי באמצעות Firebase Authentication, מורשה לבצע את השאילתה או את המוטציה.

הערה: USER_ANON הוא קבוצה רחבה יותר מ-USER.

הערות: חשוב לזכור שצריך לתכנן בקפידה את השאילתות והמוטציות ברמת ההרשאה הזו. ברמה הזו, המשתמשים יכולים להתחבר אנונימית (כניסה אוטומטית שמקושרת רק למכשיר של המשתמש) באמצעות Authentication, והיא לא מבצעת בעצמה בדיקות נוספות, למשל כדי לבדוק אם הנתונים שייכים למשתמש. דוגמאות ושיטות מומלצות לחלופות

מאחר ש תהליכי התחברות אנונימיים מנפיקים uid, הרמה USER_ANON זהה ל-
@auth(expr: "auth.uid != nil")Authentication
USER כל משתמש שנכנס באמצעות Firebase Authentication מורשה לבצע את השאילתה או את המוטציה, מלבד משתמשים לא רשומים שנכנסו לחשבון.

הערות: חשוב לזכור שצריך לתכנן בקפידה את השאילתות והמוטציות ברמת ההרשאה הזו. ברמה הזו מתבצעת רק בדיקה שהמשתמש מחובר באמצעות Authentication, והיא לא מבצעת בעצמה בדיקות אחרות, למשל אם הנתונים שייכים למשתמש. דוגמאות ושיטות מומלצות לחלופות

שווה ערך ל-@auth(expr: "auth.uid != nil && auth.token.firebase.sign_in_provider != 'anonymous'")"
USER_EMAIL_VERIFIED כל משתמש שנכנס לחשבון באמצעות Firebase Authentication עם כתובת אימייל מאומתת מורשה לבצע את השאילתה או את המוטציה.

הערות: מאחר שביצוע האימות באימייל מתבצע באמצעות Authentication, הוא מבוסס על שיטת Authentication חזקה יותר, ולכן רמה זו מספקת אבטחה נוספת בהשוואה ל-USER או ל-USER_ANON. ברמה הזו מתבצעת רק בדיקה שהמשתמש מחובר ל-Authentication באמצעות כתובת אימייל מאומתת, והיא לא מבצעת בעצמה בדיקות נוספות, למשל אם הנתונים שייכים למשתמש. דוגמאות ושיטות מומלצות לחלופות

שווה ערך ל-@auth(expr: "auth.uid != nil && auth.token.email_verified")"
NO_ACCESS לא ניתן לבצע את הפעולה הזו מחוץ להקשר של Admin SDK.

שווה ערך ל-@auth(expr: "false")

הפניה ל-CEL עבור @auth(expr) ו-@check(expr)

כפי שמוצג בדוגמאות במקומות אחרים במדריך הזה, אפשר להשתמש בביטויים שהוגדרו ב-Common Expression Language ‏ (CEL) כדי לשלוט בהרשאה ל-Data Connect באמצעות ההוראות @auth(expr:) ו-@check.

בקטע הזה נסביר על תחביר CEL שרלוונטי ליצירת ביטויים להנחיות האלה.

מידע מפורט על CEL זמין במפרט של CEL.

בדיקת משתנים שמועברים בשאילתות ובמוטציות

התחביר של @auth(expr) מאפשר לגשת למשתנים משאילתות וממוטציות ולבדוק אותם.

לדוגמה, אפשר לכלול משתנה פעולה, כמו $status, באמצעות vars.status.

mutation Update($id: UUID!, $status: Any) @auth(expr: "has(vars.status)")

נתונים שזמינים לביטויים

גם ביטוי ה-CEL @auth(expr:) וגם ביטוי ה-CEL @check(expr:) יכולים להעריך את האפשרויות הבאות:

  • request.operationName
  • vars (כתובת אימייל חלופית ל-request.variables)
  • auth (כתובת אימייל חלופית ל-request.auth)

בנוסף, ביטויים של @check(expr:) יכולים להעריך:

  • this (הערך של השדה הנוכחי)

האובייקט request.operationName

באובייקט request.operarationName מאוחסן סוג הפעולה, שאילתה או מוטציה.

האובייקט vars

האובייקט vars מאפשר לביטויים לגשת לכל המשתנים שהועברו בשאילתה או במונטיזציה.

אפשר להשתמש ב-vars.<variablename> בביטוי ככינוי ל-request.variables.<variablename> המלא:

# The following are equivalent
mutation StringType($v: String!) @auth(expr: "vars.v == 'hello'")
mutation StringType($v: String!) @auth(expr: "request.variables.v == 'hello'")

האובייקט auth

המאפיין Authentication מזהה משתמשים שמבקשים גישה לנתונים שלכם ומספק את המידע הזה כאובייקט שאפשר להשתמש בו בביטויים שלכם.

במסננים ובביטויים, אפשר להשתמש ב-auth ככינוי ל-request.auth.

אובייקט האימות מכיל את הפרטים הבאים:

  • uid: מזהה משתמש ייחודי שהוקצה למשתמש מבצע הבקשה.
  • token: מפה של ערכים שנאספו על ידי Authentication.

מידע נוסף על התוכן של auth.token זמין במאמר נתונים באסימוני אימות.

הקישור this

הערך של הקישור this הוא השדה שאליו מצורפת ההנחיה @check. במקרה בסיסי, אפשר להעריך תוצאות של שאילתות עם ערך יחיד.

mutation UpdateMovieTitle($movieId: UUID!, $newTitle: String!) @auth(level: USER) @transaction {
  # Step 1: Query and check
  query @redact {
    moviePermission( # Look up a join table called MoviePermission with a compound key.
      key: {movieId: $movieId, userId_expr: "auth.uid"}
    ) {
      # Check if the user has the editor role for the movie. `this` is the string value of `role`.
      # If the parent moviePermission is null, the @check will also fail automatically.
      role @check(expr: "this == 'editor'", message: "You must be an editor of this movie to update title")
    }
  }
  # Step 2: Act
  movie_update(id: $movieId, data: {
    title: $newTitle
  })
}

אם השדה המוחזר מופיע כמה פעמים כי אחד מהאבות הוא רשימה, כל מופע נבדק עם this שמקושר לכל ערך.

בכל נתיב נתון, אם יש אב null או [], לא נגיע לשדה והערכת ה-CEL תידלג על הנתיב הזה. במילים אחרות, ההערכה מתבצעת רק כאשר this הוא null או לא null, אבל אף פעם undefined.

כשהשדה עצמו הוא רשימה או אובייקט, this פועל לפי אותו מבנה (כולל כל הצאצאים שנבחרו במקרה של אובייקטים), כפי שמתואר בדוגמה הבאה.

mutation UpdateMovieTitle2($movieId: UUID!, $newTitle: String!) @auth(level: USER) @transaction {
  # Step 1: Query and check
  query {
    moviePermissions( # Now we query for a list of all matching MoviePermissions.
      where: {movieId: {eq: $movieId}, userId: {eq_expr: "auth.uid"}}
    # This time we execute the @check on the list, so `this` is the list of objects.
    # We can use the `.exists` macro to check if there is at least one matching entry.
    ) @check(expr: "this.exists(p, p.role == 'editor')", message: "You must be an editor of this movie to update title") {
      role
    }
  }
  # Step 2: Act
  movie_update(id: $movieId, data: {
    title: $newTitle
  })
}

תחביר של ביטויים מורכבים

אפשר לכתוב ביטויים מורכבים יותר על ידי שילוב עם האופרטורים && ו-||.

mutation UpsertUser($username: String!) @auth(expr: "(auth != null) && (vars.username == 'joe')")

בקטע הבא מתוארים כל האופרטורים הזמינים.

אופרטורים וקדימות אופרטורים

בטבלה הבאה מפורטים האופרטורים והעדיפות שלהם.

נתונים ביטויים שרירותיים a ו-b, שדה f ומפתח i.

אופרטור תיאור תכונה אסוסיטיבית
a[i] a() a.f גישה לאינדקס, לשיחה ולשדה משמאל לימין
!a -a שלילה חד-ערךית מימין לשמאל
a/b a%b a*b אופרטורים של כפל משמאל לימין
a+b a-b אופרטורים נוספים משמאל לימין
a>b a>=b a<b a<=b אופרטורים יחסיים משמאל לימין
a in b קיומו ברשימה או במפה משמאל לימין
type(a) == t השוואת טיפוסים, כאשר t יכול להיות bool,‏ int,‏ float,‏ מספר, מחרוזת, רשימה, מפה, חותמת זמן או משך זמן משמאל לימין
a==b a!=b אופרטורים להשוואה משמאל לימין
a && b AND מותנה משמאל לימין
a || b או מותנה משמאל לימין
a ? true_value : false_value ביטוי טרינרי משמאל לימין

נתונים בטוקני אימות

האובייקט auth.token יכול להכיל את הערכים הבאים:

שדה תיאור
email כתובת האימייל שמשויכת לחשבון, אם יש כזו.
email_verified true אם המשתמש אימת שיש לו גישה לכתובת email. ספקים מסוימים מאמתים באופן אוטומטי כתובות אימייל שבבעלותם.
phone_number מספר הטלפון שמשויך לחשבון, אם קיים.
name השם המוצג של המשתמש, אם הוא מוגדר.
sub מזהה המשתמש ב-Firebase. המזהה הזה הוא ייחודי בפרויקט.
firebase.identities מילון של כל הזהויות שמשויכות לחשבון של המשתמש הזה. המפתחות של המילון יכולים להיות כל אחד מהערכים הבאים: email, phone, google.com, facebook.com, github.com, twitter.com. הערכים במילון הם מערכי מזהים ייחודיים לכל ספק זהויות שמשויך לחשבון. לדוגמה, השדה auth.token.firebase.identities["google.com"][0] מכיל את מזהה המשתמש הראשון ב-Google שמשויך לחשבון.
firebase.sign_in_provider ספק הכניסה לחשבון ששימש לקבלת האסימון הזה. יכול להיות אחת מהמחרוזות הבאות: custom, ‏ password, ‏ phone, ‏ anonymous, ‏ google.com, ‏ facebook.com, ‏ github.com, ‏ twitter.com.
firebase.tenant מזהה הדייר שמשויך לחשבון, אם הוא קיים. לדוגמה, tenant2-m6tyz

שדות נוספים באסימונים מזהים מסוג JWT

אפשר גם לגשת לשדות auth.token הבאים:

הצהרות מותאמות אישית על אסימונים
alg אלגוריתם "RS256"
iss המנפיק כתובת האימייל של חשבון השירות של הפרויקט
sub נושא כתובת האימייל של חשבון השירות של הפרויקט
aud קהל "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat שעת ההנפקה השעה הנוכחית, בשניות מאז ראשית זמן יוניקס (Unix epoch)
exp מועד תפוגה הזמן, בשניות מאז ראשית זמן יוניקס (Unix epoch), שבו יפוג תוקף האסימון. הוא יכול להיות עד 3,600 שניות מאוחר יותר מ-iat.
הערה: ההגדרה הזו קובעת רק את הזמן שבו יפוג התוקף של האסימון בהתאמה אישית עצמו. עם זאת, אחרי שמשתמש מתחבר באמצעות signInWithCustomToken(), הוא נשאר מחובר למכשיר עד שהסשן שלו יבוטל או עד שהוא יתנתק.
ֶ<claims> (אופציונלי) הצהרות אופציונליות בהתאמה אישית שאפשר לכלול באסימון, וניתן לגשת אליהן דרך auth.token (או request.auth.token) בביטויים. לדוגמה, אם יוצרים תלונה מותאמת אישית adminClaim, אפשר לגשת אליה באמצעות auth.token.adminClaim.

מה השלב הבא?