שימוש ב-OAuth 2.0 לאפליקציות שרת-אל-שרת

מערכת Google OAuth 2.0 תומכת באינטראקציות משרת-אל-שרת, כמו אלה שבין אפליקציית אינטרנט לבין שירות של Google. במקרה הזה צריך חשבון שירות, שהוא חשבון ששייך לאפליקציה ולא למשתמש קצה ספציפי. האפליקציה שולחת קריאות ל-Google APIs בשם חשבון השירות, כך שהמשתמשים לא מעורבים באופן ישיר. התרחיש הזה נקרא לפעמים "OAUTH דו-רגלי" או "2LO". (המונח הקשור 'OAuth תלת-רגלי' מתייחס לתרחישים שבהם האפליקציה שלכם קוראת ל-Google APIs מטעם משתמשי קצה, ולתרחישים שבהם נדרשת הסכמה מהמשתמש).

מידע נוסף זמין במאמר שיטות מומלצות לשימוש בחשבונות שירות.

בדרך כלל, אפליקציה משתמשת בחשבון שירות כשהיא משתמשת בממשקי Google API כדי לעבוד עם הנתונים שלה ולא עם נתוני משתמש. לדוגמה, אפליקציה שמשתמשת ב-Google Cloud Datastore כדי לשמור נתונים באופן קבוע תשתמש בחשבון שירות כדי לאמת את הקריאות שלה ל-Google Cloud Datastore API.

אדמינים בדומיין Google Workspace יכולים גם להעניק לחשבונות שירות הרשאה ברמת הדומיין לגשת לנתוני משתמשים בשם המשתמשים בדומיין.

במסמך הזה מוסבר איך אפליקציה יכולה להשלים את תהליך ההרשאה באמצעות OAuth 2.0 משרת-אל-שרת באמצעות ספריית לקוח של Google APIs (מומלץ) או באמצעות HTTP.

סקירה כללית

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

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

לבסוף, האפליקציה יכולה להשתמש באסימון הגישה כדי להפעיל Google APIs.

יצירה של חשבון שירות

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

אם האפליקציה שלכם פועלת ב-Google App Engine, חשבון שירות מוגדר באופן אוטומטי כשאתם יוצרים את הפרויקט.

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

אם האפליקציה שלכם לא פועלת ב-Google App Engine או ב-Google Compute Engine, אתם צריכים לקבל את פרטי הכניסה האלה ב-Google API Console. כדי ליצור פרטי כניסה לחשבון שירות או כדי לראות את פרטי הכניסה הציבוריים שכבר יצרתם, צריך לבצע את הפעולות הבאות:

קודם יוצרים חשבון שירות:

  1. פותחים את הדף Service accounts.
  2. אם מתבקשים, בוחרים פרויקט או יוצרים פרויקט חדש.
  3. לוחצים על  יצירת חשבון שירות.
  4. בקטע פרטי חשבון שירות, מקלידים שם, מזהה ותיאור לחשבון השירות ולוחצים על יצירה והמשך.
  5. אופציונלי: בקטע Grant this service account access to project, בוחרים את תפקידי ה-IAM שרוצים להקצות לחשבון השירות.
  6. לוחצים על המשך.
  7. אופציונלי: בקטע Grant users access to this service account, מוסיפים את המשתמשים או הקבוצות שמורשים להשתמש בחשבון השירות ולנהל אותו.
  8. לוחצים על סיום.

בשלב הבא, יוצרים מפתח לחשבון השירות:

  1. לוחצים על כתובת האימייל של חשבון השירות שיצרתם.
  2. לוחצים על הכרטיסייה Keys.
  3. בתפריט הנפתח Add key, בוחרים באפשרות Create new key.
  4. לוחצים על יצירה.

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

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

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

האצלת סמכויות ברמת הדומיין לחשבון השירות

אדמין ב-Workspace בארגון יכול להשתמש בחשבון Google Workspace כדי לתת לאפליקציה הרשאה לגשת לנתוני משתמשים ב-Workspace בשם המשתמשים בדומיין Google Workspace. לדוגמה, אפליקציה שמשתמשת ב-Google Calendar API כדי להוסיף אירועים ליומנים של כל המשתמשים בדומיין Google Workspace, תשתמש בחשבון שירות כדי לגשת ל-Google Calendar API בשם המשתמשים. מתן הרשאה לחשבון שירות לגשת לנתונים בשם משתמשים בדומיין נקרא לפעמים "הענקת הרשאות גישה ברמת הדומיין" לחשבון שירות.

כדי להעניק הרשאות גישה ברמת הדומיין לחשבון שירות, סופר-אדמין בדומיין Google Workspace צריך לבצע את השלבים הבאים:

  1. ב מסוף Admin של הדומיין ב-Google Workspace, נכנסים אל התפריט הראשי > אבטחה > שליטה בגישה ובנתונים > אמצעי בקרה לממשקי API.
  2. בחלונית Domain wide delegation (הענקת גישה ברמת הדומיין), בוחרים באפשרות Manage Domain Wide Delegation (ניהול של הענקת גישה ברמת הדומיין).
  3. לוחצים על הוספת חדש.
  4. בשדה Client ID (מזהה לקוח), מזינים את מזהה הלקוח של חשבון השירות. מזהה הלקוח של חשבון השירות מופיע בדף Service accounts.
  5. בשדה היקפי הרשאות OAuth (מופרדים בפסיקים) מזינים את רשימת היקפי ההרשאות שהאפליקציה צריכה לקבל גישה אליהם. לדוגמה, אם לאפליקציה שלכם נדרשת גישה מלאה ל-Google Drive API ול-יומן Google API בכל הדומיין, מזינים: https://www.googleapis.com/auth/drive, https://www.googleapis.com/auth/calendar.
  6. לוחצים על Authorize.

לאפליקציה שלכם יש עכשיו הרשאה לבצע קריאות ל-API בתור משתמשים בדומיין Workspace (כלומר, "להתחזות" למשתמשים). כשמתכוננים לבצע את הקריאות האלה ל-API עם הרשאה, מציינים במפורש את המשתמש שאת הזהות שלו רוצים לגלם.

ביצוע קריאה ל-API עם הרשאת גישה

בסעיפים הבאים מוסבר איך לבצע קריאה מורשית ל-API באמצעות ספריית לקוח של Google APIs או באמצעות אינטראקציה ישירה עם מערכת OAuth 2.0 באמצעות HTTP.

Java

אחרי שמקבלים את כתובת האימייל של הלקוח ואת המפתח הפרטי מ-קונסולה לממשקי API, משתמשים ב-Google Auth Library for Java כדי ליצור אובייקט GoogleCredentials מפרטי הכניסה של חשבון השירות ומאזורי ההרשאות שהאפליקציה צריכה לגשת אליהם. לדוגמה:

import com.google.auth.oauth2.GoogleCredentials;
import com.google.api.services.sqladmin.SQLAdminScopes;

// ...

GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream("ServiceAccountKey.json"))
    .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN));

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

האצלת סמכויות ברמת הדומיין

אם הענקתם לחשבון השירות הרשאות גישה ברמת הדומיין ואתם רוצים להתחזות לחשבון משתמש, צריך לציין את כתובת האימייל של חשבון המשתמש באמצעות ה-method‏ createDelegated של האובייקט GoogleCredentials. לדוגמה:

GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream("ServiceAccountKey.json"))
    .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN))
    .createDelegated("workspace-user@example.com");

אובייקט GoogleCredentials משמש לקריאה לשיטה createDelegated(). הארגומנט של שיטת createDelegated() חייב להיות משתמש ששייך לחשבון Workspace שלכם. הקוד ששולח את הבקשה ישתמש בפרטי הכניסה האלה כדי לקרוא ל-Google APIs באמצעות חשבון השירות שלכם.

Python

אחרי שמקבלים את כתובת האימייל של הלקוח ואת המפתח הפרטי מקונסולה לממשקי API, משתמשים ב-Google APIs Client Library for Python כדי לבצע את השלבים הבאים:

  1. יוצרים אובייקט Credentials מפרטי הכניסה של חשבון השירות וההיקפים שהאפליקציה צריכה לגשת אליהם. לדוגמה:
    from google.oauth2 import service_account
    
    SCOPES = ['https://www.googleapis.com/auth/sqlservice.admin']
    SERVICE_ACCOUNT_FILE = '/path/to/ServiceAccountKey.json'
    
    credentials = service_account.Credentials.from_service_account_file(
            SERVICE_ACCOUNT_FILE, scopes=SCOPES)

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

  2. האצלת סמכויות ברמת הדומיין

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

    delegated_credentials = credentials.with_subject('user@example.org')

משתמשים באובייקט Credentials כדי לשלוח קריאות ל-Google APIs באפליקציה.

HTTP/REST

אחרי שמקבלים את מזהה הלקוח ואת המפתח הפרטי מ-קונסולה לממשקי API, האפליקציה צריכה לבצע את השלבים הבאים:

  1. יוצרים טוקן אינטרנט בפורמט JSON‏ (JWT,‏ 'ג'וט'), שכולל כותרת, קבוצת טענות וחתימה.
  2. שולחים בקשה לאסימון גישה משרת ההרשאות של Google OAuth 2.0.
  3. מטפלים בתגובת ה-JSON שמוחזרת משרת ההרשאות.

בקטעים הבאים מוסבר איך לבצע את השלבים האלה.

אם התגובה כוללת אסימון גישה, אפשר להשתמש באסימון הגישה כדי לקרוא ל-Google API. (אם התגובה לא כוללת אסימון גישה, יכול להיות ש-JWT ובקשת האסימון לא נוצרו בצורה תקינה, או שחשבון השירות לא קיבל הרשאה לגשת להיקפי הגישה המבוקשים).

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

אפליקציית השרת משתמשת ב-JWT כדי לבקש אסימון משרת ההרשאות של Google, ואז משתמשת באסימון כדי לקרוא לנקודת קצה ל-API של Google. לא מעורב משתמש קצה.
איור 1. אפליקציית השרת משתמשת ב-JWT כדי לבקש אסימון משרת ההרשאות של Google, ואז משתמשת באסימון כדי לקרוא לנקודת קצה ל-API של Google. אין מעורבות של משתמשי קצה.

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

יצירת JWT

אסימון JWT מורכב משלושה חלקים: כותרת, קבוצת טענות וחתימה. הכותרת וקבוצת הטענות הם אובייקטים של JSON. אובייקטי ה-JSON האלה עוברים סריאליזציה לבייטים בפורמט UTF-8, ואז מקודדים באמצעות קידוד Base64url. הקידוד הזה מספק עמידות מפני שינויים בקידוד כתוצאה מפעולות קידוד חוזרות. הכותרת, קבוצת התלונות והחתימה מחוברים יחד באמצעות נקודה (.).

מבנה של JWT:

{Base64url encoded header}.{Base64url encoded claim set}.{Base64url encoded signature}

מחרוזת הבסיס לחתימה היא:

{Base64url encoded header}.{Base64url encoded claim set}
יצירת כותרת ה-JWT

הכותרת מורכבת משני שדות חובה: אלגוריתם החתימה והפורמט של הצהרת הזהות, וממזהה מפתח אופציונלי:

  • חובה לציין אלגוריתם, ויש רק ערך אחד שאפשר לציין: "alg": "RS256".
  • הפורמט הוא חובה, ויש לו רק ערך אחד: "typ": "JWT".
  • מזהה המפתח הוא אופציונלי, והוא המזהה של המפתח של חשבון השירות שמשמש לחתימה על ה-JWT. אם מציינים מזהה מפתח שגוי, המערכת תנסה את כל המפתחות שמשויכים לחשבון השירות. אם לא נמצא מפתח תקין, האסימון נדחה. ‫Google שומרת לעצמה את הזכות לדחות טוקנים עם מזהי מפתח שגויים.

חשבונות שירות מסתמכים על אלגוריתם RSA SHA-256 ועל פורמט אסימון JWT. כתוצאה מכך, ייצוג ה-JSON של הכותרת הוא כדלקמן:

{"alg":"RS256","typ":"JWT", "kid":"370ab79b4513eb9bad7c9bd16a95cb76b5b2a56a"}

הייצוג של המחרוזת הזו ב-Base64url הוא:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsICJraWQiOiIzNzBhYjc5YjQ1MTNlYjliYWQ3YzliZDE2YTk1Y2I3NmI1YjJhNTZhIn0=
יצירת קבוצת ההצהרות של ה-JWT

קבוצת הטענות של ה-JWT מכילה מידע על ה-JWT, כולל ההרשאות המבוקשות (היקפים), היעד של האסימון, המנפיק, המועד שבו האסימון הונפק ומשך החיים של האסימון. רוב השדות הם שדות חובה. בדומה לכותרת ה-JWT, קבוצת ההצהרות של ה-JWT היא אובייקט JSON, והיא משמשת לחישוב החתימה.

תלונות שנדרשות

התביעות הנדרשות בקבוצת התביעות של JWT יכולות להופיע בכל סדר בקבוצת התביעות.

שם תיאור
iss כתובת האימייל של חשבון השירות.
scope רשימה של ההרשאות שהאפליקציה מבקשת, מופרדות ברווחים.
aud תיאור של היעד המיועד של הטענה. כשמבצעים בקשה לטוקן גישה, הערך הזה תמיד יהיה https://oauth2.googleapis.com/token.
exp זמן התפוגה של הטענה, שצוין בשניות מאז 00:00:00 UTC,‏ 1 בינואר 1970. הערך הזה יכול להיות עד שעה אחרי שעת ההנפקה.
iat השעה שבה הונפקה ההצהרה, שצוינה בשניות מאז 00:00:00 UTC,‏ 1 בינואר 1970.

זו דוגמה לייצוג JSON של השדות הנדרשים בקבוצת הצהרות של JWT:

{
  "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
  "scope": "https://www.googleapis.com/auth/devstorage.read_only",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
תלונות נוספות

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

כדי לקבל אסימון גישה שמעניק לאפליקציה גישה מוגבלת למשאב, צריך לכלול את כתובת האימייל של המשתמש במערך ההצהרות של ה-JWT כערך של השדה sub.

שם תיאור
sub כתובת האימייל של המשתמש שהאפליקציה מבקשת עבורו גישה מוקצית.

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

זו דוגמה לקבוצת הצהרות של JWT שכוללת את השדה sub:

{
  "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
  "sub": "some.user@example.com",
  "scope": "https://www.googleapis.com/auth/prediction",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
קידוד קבוצת ההצהרות של JWT

בדומה לכותרת ה-JWT, קבוצת ההצהרות של ה-JWT צריכה לעבור סריאליזציה ל-UTF-8 וקידוד בטוח ל-Base64url. זוהי דוגמה לייצוג JSON של קבוצת הצהרות של JWT:

{
  "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
  "scope": "https://www.googleapis.com/auth/prediction",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
חישוב החתימה

חתימת רשת מבוססת JSON (JWS) היא המפרט שמנחה את המנגנונים של יצירת החתימה עבור ה-JWT. הקלט לחתימה הוא מערך הבייטים של התוכן הבא:

{Base64url encoded header}.{Base64url encoded claim set}

כשמחשבים את החתימה, צריך להשתמש באלגוריתם החתימה בכותרת ה-JWT. אלגוריתם החתימה היחיד שנתמך על ידי שרת ההרשאות של Google OAuth 2.0 הוא RSA באמצעות אלגוריתם הגיבוב SHA-256. הערך הזה מופיע כ-RS256 בשדה alg בכותרת של ה-JWT.

חתימה על ייצוג UTF-8 של הקלט באמצעות SHA256withRSA (נקרא גם RSASSA-PKCS1-V1_5-SIGN עם פונקציית הגיבוב SHA-256) עם המפתח הפרטי שהתקבל מ-Google API Console. הפלט יהיה מערך בייטים.

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

{Base64url encoded header}.
{Base64url encoded claim set}.
{Base64url encoded signature}

זוהי דוגמה ל-JWT לפני קידוד Base64url:

{"alg":"RS256","typ":"JWT"}.
{
"iss":"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
"scope":"https://www.googleapis.com/auth/prediction",
"aud":"https://oauth2.googleapis.com/token",
"exp":1328554385,
"iat":1328550785
}.
[signature bytes]

זו דוגמה ל-JWT שנחתם ומוכן להעברה:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL29hdXRoMi92NC90b2tlbiIsImV4cCI6MTMyODU1NDM4NSwiaWF0IjoxMzI4NTUwNzg1fQ.UFUt59SUM2_AW4cRU8Y0BYVQsNTo4n7AFsNrqOpYiICDu37vVt-tw38UKzjmUKtcRsLLjrR3gFW3dNDMx_pL9DVjgVHDdYirtrCekUHOYoa1CMR66nxep5q5cBQ4y4u2kIgSvChCTc9pmLLNoIem-ruCecAJYgI9Ks7pTnW1gkOKs0x3YpiLpzplVHAkkHztaXiJdtpBcY1OXyo6jTQCa3Lk2Q3va1dPkh_d--GU2M5flgd8xNBPYw4vxyt0mP59XZlHMpztZt0soSgObf7G3GXArreF_6tpbFsS3z2t5zkEiHuWJXpzcYr5zWTRPDEHsejeBSG8EgpLDce2380ROQ

בקשת טוקן גישה

אחרי שיוצרים את ה-JWT החתום, אפשר להשתמש בו באפליקציה כדי לבקש אסימון גישה. בקשת טוקן הגישה הזו היא בקשת HTTPS POST, והגוף שלה מקודד בכתובת URL. לדוגמה:

https://oauth2.googleapis.com/token

חובה לציין את הפרמטרים הבאים בבקשת ה-HTTPS‏ POST:

שם תיאור
grant_type משתמשים במחרוזת הבאה, בקידוד URL לפי הצורך: urn:ietf:params:oauth:grant-type:jwt-bearer
assertion אסימון ה-JWT, כולל החתימה.

זוהי פריקה גולמית של בקשת ה-HTTPS‏ POST שמשמשת בבקשה של טוקן גישה:

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU3MzM4MSwiaWF0IjoxMzI4NTY5NzgxfQ.ixOUGehweEVX_UKXv5BbbwVEdcz6AYS-6uQV6fGorGKrHf3LIJnyREw9evE-gs2bmMaQI5_UbabvI4k-mQE4kBqtmSpTzxYBL1TCd7Kv5nTZoUC1CmwmWCFqT9RE6D7XSgPUh_jF1qskLa2w0rxMSjwruNKbysgRNctZPln7cqQ

זאת אותה בקשה, באמצעות curl:

curl -d 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU3MzM4MSwiaWF0IjoxMzI4NTY5NzgxfQ.RZVpzWygMLuL-n3GwjW1_yhQhrqDacyvaXkuf8HcJl8EtXYjGjMaW5oiM5cgAaIorrqgYlp4DPF_GuncFqg9uDZrx7pMmCZ_yHfxhSCXru3gbXrZvAIicNQZMFxrEEn4REVuq7DjkTMyCMGCY1dpMa8aWfTQFt3Eh7smLchaZsU
' https://oauth2.googleapis.com/token

טיפול בתגובות

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

{
  "access_token": "1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M",
  "scope": "https://www.googleapis.com/auth/prediction"
  "token_type": "Bearer",
  "expires_in": 3600
}

אפשר לעשות שימוש חוזר בטוקנים של גישה במהלך חלון הזמן שצוין בערך expires_in.

שיקול חשוב בנושא אבטחה: הבנת התחזות

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

קריאה לממשקי Google APIs

Java

כדי לקרוא לממשקי Google API באמצעות האובייקט GoogleCredentials, צריך לבצע את השלבים הבאים:

  1. יוצרים אובייקט שירות עבור ה-API שרוצים להפעיל באמצעות האובייקט GoogleCredentials. לדוגמה:
    SQLAdmin sqladmin =
        new SQLAdmin.Builder(httpTransport, JSON_FACTORY, credentials).build();
  2. שולחים בקשות לשירות ה-API באמצעות הממשק שמסופק על ידי אובייקט השירות. לדוגמה, כדי להציג רשימה של המכונות של מסדי נתונים ב-Cloud SQL בפרויקט exciting-example-123:
    SQLAdmin.Instances.List instances =
        sqladmin.instances().list("exciting-example-123").execute();

Python

כדי להשתמש באובייקט המורשה Credentials כדי לקרוא לממשקי Google API, צריך לבצע את השלבים הבאים:

  1. יוצרים אובייקט שירות עבור ה-API שרוצים להפעיל. יוצרים אובייקט שירות על ידי קריאה לפונקציה build עם השם והגרסה של ה-API ואובייקט Credentials המורשה. לדוגמה, כדי להפעיל את גרסה 1beta3 של Cloud SQL Administration API:
    import googleapiclient.discovery
    
    sqladmin = googleapiclient.discovery.build('sqladmin', 'v1beta3', credentials=credentials)
  2. שולחים בקשות לשירות ה-API באמצעות הממשק שמסופק על ידי אובייקט השירות. לדוגמה, כדי להציג רשימה של המכונות של מסדי נתונים ב-Cloud SQL בפרויקט exciting-example-123:
    response = sqladmin.instances().list(project='exciting-example-123').execute()

HTTP/REST

אחרי שהאפליקציה מקבלת אסימון גישה, אפשר להשתמש באסימון כדי לבצע קריאות ל-Google API מטעם חשבון שירות או חשבון משתמש מסוים, אם הוענקו היקפי הגישה שנדרשים על ידי ה-API. כדי לעשות את זה, צריך לכלול את אסימון הגישה בבקשה ל-API באמצעות הפרמטר access_token של השאילתה או הערך Bearer של כותרת ה-HTTP Authorization. כשניתן, עדיף להשתמש בכותרת HTTP, כי מחרוזות של שאילתות נוטות להיות גלויות ביומני השרת. ברוב המקרים, אפשר להשתמש בספריית לקוח כדי להגדיר את הקריאות ל-Google APIs (לדוגמה, כשקוראים ל-Drive Files API).

ב-OAuth 2.0 Playground אפשר להתנסות בכל ממשקי Google APIs ולראות את היקפי ההרשאות שלהם.

דוגמאות ל-HTTP GET

קריאה לנקודת הקצה drive.files (ה-API של קובצי Drive) באמצעות כותרת ה-HTTP‏ Authorization: Bearer עשויה להיראות כך: חשוב לציין את טוקן הגישה שלכם:

GET /drive/v2/files HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer access_token

זוהי קריאה לאותו API עבור המשתמש המאומת באמצעות פרמטר מחרוזת השאילתה access_token:

GET https://www.googleapis.com/drive/v2/files?access_token=access_token

curl דוגמאות

אפשר לבדוק את הפקודות האלה באמצעות אפליקציית שורת הפקודה curl. הנה דוגמה לשימוש באפשרות של כותרת HTTP (מומלץ):

curl -H "Authorization: Bearer access_token" https://www.googleapis.com/drive/v2/files

או לחלופין, האפשרות של פרמטר מחרוזת השאילתה:

curl https://www.googleapis.com/drive/v2/files?access_token=access_token

מתי טוקנים של גישה פוקעים

התוקף של אסימוני גישה שמונפקים על ידי שרת ההרשאות של Google OAuth 2.0 יפוג אחרי משך הזמן שמוגדר בערך expires_in. כשפג התוקף של אסימון גישה, האפליקציה צריכה ליצור עוד JWT, לחתום עליו ולבקש עוד אסימון גישה.

קודי שגיאה של JWT

error שדה error_description שדה משמעות איך פותרים את הבעיה
unauthorized_client Unauthorized client or scope in request. אם אתם מנסים להשתמש בהענקת הרשאות גישה ברמת הדומיין, חשבון השירות לא מורשה במסוף Admin של הדומיין של המשתמש.

מוודאים שחשבון השירות מורשה בדף הענקת הרשאות גישה ברמת הדומיין במסוף Admin עבור המשתמש בטענה (שדה) sub.

בדרך כלל, האישור מתעדכן אצל כל המשתמשים בחשבון Google תוך כמה דקות, אבל יכולות לחלוף עד 24 שעות.

unauthorized_client Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested. חשבון שירות קיבל הרשאה באמצעות כתובת האימייל של הלקוח ולא באמצעות מזהה הלקוח (מספרי) במסוף Admin, או שנעשה שימוש בקבוצת Google לצורך הרשאה. בדף הענקת הרשאות גישה ברמת הדומיין במסוף Admin, מסירים את הלקוח ומוסיפים אותו מחדש עם מזהה מספרי, או מסירים את קבוצת Google ומחליפים אותה בשירות או בחשבון משתמש נפרדים.
access_denied (כל ערך) אם אתם משתמשים בהענקת גישה ברמת הדומיין, לא ניתנה הרשאה לאחד או יותר מההיקפים המבוקשים במסוף Admin.

מוודאים שחשבון השירות מורשה בדף הענקת הרשאות גישה ברמת הדומיין במסוף Admin עבור המשתמש בטענת sub (שדה), ושהוא כולל את כל ההיקפים שאתם מבקשים בטענת scope של ה-JWT.

כדי לוודא שהגישה לשירותי Google לא מוגבלת, אפשר לעיין במאמר בנושא ניהול הגישה לשירותים שאין להם מתג נפרד.

בדרך כלל, האישור מתעדכן אצל כל המשתמשים בחשבון Google תוך כמה דקות, אבל יכולות לחלוף עד 24 שעות.

admin_policy_enforced (כל ערך) לא ניתן לאשר את חשבון Google לאחד או יותר מההיקפים המבוקשים בגלל כללי המדיניות של האדמין ב-Google Workspace.

מידע נוסף על האופן שבו אדמין יכול להגביל את הגישה לכל היקפי ההרשאות או להיקפי הרשאות רגישים ומוגבלים עד שתינתן גישה מפורשת למזהה לקוח OAuth זמין במאמר שליטה בגישה של אפליקציות של צד שלישי ואפליקציות פנימיות לנתונים ב-Google Workspace במרכז העזרה לאדמינים ב-Google Workspace.

invalid_client (כל ערך)

לקוח OAuth או טוקן JWT לא תקינים או שההגדרה שלהם שגויה.

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

מוודאים שטוקן ה-JWT תקין ומכיל הצהרות נכונות.

צריך לוודא שלקוח ה-OAuth וחשבון השירות מוגדרים בצורה נכונה ושאתם משתמשים בכתובת האימייל הנכונה.

צריך לוודא שטוקן ה-JWT נכון והונפק למזהה הלקוח בבקשה.

deleted_client (כל ערך)

לקוח ה-OAuth שמשמש לשליחת הבקשה נמחק. המחיקה יכולה להתבצע באופן ידני או אוטומטי במקרה של לקוחות שלא נעשה בהם שימוש . אפשר לשחזר לקוחות שנמחקו תוך 30 יום ממועד המחיקה. מידע נוסף

משתמשים במזהה לקוח שעדיין פעיל.

invalid_grant Not a valid email או Invalid email or User ID. המשתמש לא קיים. בודקים שכתובת האימייל בsubהצהרה (שדה) נכונה.
invalid_grant

Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your 'iat' and 'exp' values and use a clock with skew to account for clock differences between systems.

בדרך כלל, המשמעות היא שהשעה המקומית במערכת לא נכונה. זה יכול לקרות גם אם הערך של exp גבוה ב-65 דקות או יותר מהערך של iat, או אם הערך של exp נמוך מהערך של iat.

מוודאים שהשעון במערכת שבה נוצר ה-JWT נכון. אם צריך, מסנכרנים את השעה עם Google NTP.

invalid_grant Invalid JWT Signature.

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

לחלופין, יכול להיות שטענת הנכוֹנוּת של JWT מקודדת בצורה שגויה – היא חייבת להיות בקידוד Base64, ללא שורות חדשות או סימני ריפוד של סימן שווה.

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

נסו להשתמש בספריית OAuth שסופקה על ידי Google כדי לוודא שטוקן ה-JWT נוצר בצורה נכונה.

invalid_scope Invalid OAuth scope or ID token audience provided. לא נשלחה בקשה להיקפי הרשאות (רשימה ריקה של היקפי הרשאות), או שאחד מהיקפי ההרשאות המבוקשים לא קיים (כלומר, לא תקף).

צריך לוודא שההצהרה (השדה) scope של ה-JWT מאוכלסת, ולהשוות את היקפי ההרשאות שהיא מכילה עם היקפי ההרשאות המתועדים של ממשקי ה-API שבהם רוצים להשתמש, כדי לוודא שאין שגיאות או שגיאות הקלדה.

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

disabled_client The OAuth client was disabled. המפתח שמשמש לחתימה על טענת הנכוֹנוּת (assertion) של JWT מושבת.

עוברים אל Google API Console, ובקטע IAM & Admin > Service Accounts, מפעילים את חשבון השירות שמכיל את מזהה המפתח שמשמש לחתימה על ההצהרה.

org_internal This client is restricted to users within its organization. מזהה לקוח OAuth בבקשה הוא חלק מפרויקט שמגביל את הגישה לחשבונות Google ב ארגון ספציפי ב-Google Cloud.

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

נספח: הרשאה באמצעות חשבון שירות ללא OAuth

בחלק מממשקי ה-API של Google, אפשר לבצע קריאות מורשות ל-API באמצעות JWT חתום ישירות כאסימון מסוג bearer, במקום כאסימון גישה מסוג OAuth 2.0. אם זה אפשרי, לא תצטרכו לשלוח בקשה ברשת לשרת ההרשאות של Google לפני שתבצעו קריאה ל-API.

אם ל-API שאליו רוצים להתקשר יש הגדרת שירות שפורסמה במאגר Google APIs ב-GitHub, אפשר לבצע קריאות מורשות ל-API באמצעות JWT במקום אסימון גישה. לשם כך:

  1. יוצרים חשבון שירות. חשוב לשמור את קובץ ה-JSON שמקבלים כשיוצרים את החשבון.
  2. באמצעות ספריית JWT רגילה, כמו זו שמופיעה בכתובת jwt.io, יוצרים JWT עם כותרת ומטען ייעודי (payload) כמו בדוגמה הבאה:
    {
      "alg": "RS256",
      "typ": "JWT",
      "kid": "abcdef1234567890"
    }
    .
    {
      "iss": "123456-compute@developer.gserviceaccount.com",
      "sub": "123456-compute@developer.gserviceaccount.com",
      "aud": "https://firestore.googleapis.com/",
      "iat": 1511900000,
      "exp": 1511903600
    }
    • בשדה kid בכותרת, מציינים את המזהה של המפתח הפרטי של חשבון השירות. אפשר למצוא את הערך הזה בשדה private_key_id בקובץ ה-JSON של חשבון השירות.
    • בשדות iss ו-sub, מציינים את כתובת האימייל של חשבון השירות. אפשר למצוא את הערך הזה בשדה client_email בקובץ ה-JSON של חשבון השירות. הערך הזה מזהה באופן ייחודי את הלקוח, והוא זהה למזהה הלקוח.
    • בשדה aud, מציינים את נקודת קצה ל-API. לדוגמה: https://SERVICE.googleapis.com/.
    • בשדה iat מציינים את חותמת הזמן הנוכחית של מערכת Unix, ובשדה exp מציינים את השעה בדיוק 3,600 שניות מאוחר יותר, שבה יפוג תוקף ה-JWT.

חותמים על ה-JWT באמצעות RSA-256 עם המפתח הפרטי שנמצא בקובץ ה-JSON של חשבון השירות.

לדוגמה:

Java

שימוש ב-google-auth-library-java וב-java-jwt:

import com.google.auth.oauth2.ServiceAccountCredentials;
...
GoogleCredentials credentials =
        GoogleCredentials.fromStream(new FileInputStream("MyProject-1234.json"));
PrivateKey privateKey = ((ServiceAccountCredentials) credentials).getPrivateKey();
String privateKeyId = ((ServiceAccountCredentials) credentials).getPrivateKeyId();

long now = System.currentTimeMillis();

try {
    Algorithm algorithm = Algorithm.RSA256(null, privateKey);
    String signedJwt = JWT.create()
        .withKeyId(privateKeyId)
        .withIssuer("123456-compute@developer.gserviceaccount.com")
        .withSubject("123456-compute@developer.gserviceaccount.com")
        .withAudience("https://firestore.googleapis.com/")
        .withIssuedAt(new Date(now))
        .withExpiresAt(new Date(now + 3600 * 1000L))
        .sign(algorithm);
} catch ...

Python

שימוש ב-PyJWT:

iat = time.time()
exp = iat + 3600
payload = {'iss': '123456-compute@developer.gserviceaccount.com',
           'sub': '123456-compute@developer.gserviceaccount.com',
           'aud': 'https://firestore.googleapis.com/',
           'iat': iat,
           'exp': exp}
additional_headers = {'kid': PRIVATE_KEY_ID_FROM_JSON}
signed_jwt = jwt.encode(payload, PRIVATE_KEY_FROM_JSON, headers=additional_headers,
                       algorithm='RS256')
  1. קוראים ל-API באמצעות ה-JWT החתום כאסימון למוכ"ז:
    GET /v1/projects/abc/databases/123/indexes HTTP/1.1
    Authorization: Bearer SIGNED_JWT
    Host: firestore.googleapis.com

הטמעה של הגנה על כל החשבונות

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

הנה כמה דוגמאות לסוגי האירועים שנשלחים לאפליקציה על ידי שירות ההגנה על כל החשבונות של Google:

  • https://schemas.openid.net/secevent/risc/event-type/sessions-revoked
  • https://schemas.openid.net/secevent/oauth/event-type/token-revoked
  • https://schemas.openid.net/secevent/risc/event-type/account-disabled

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