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

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

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

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

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

סקירה כללית

כדי לתמוך באינטראקציות בין שרתים, קודם צריך ליצור חשבון שירות לפרויקט ב- API Console. אם רוצים לגשת לנתוני משתמשים בחשבון 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 page.
  2. If prompted, select a project, or create a new one.
  3. לחץ על צור חשבון שירות .
  4. תחת פרטי חשבון שירות , הקלד שם, מזהה ותיאור עבור חשבון השירות ולאחר מכן לחץ על צור והמשך .
  5. אופציונלי: תחת הענק גישה לחשבון שירות זה לפרויקט , בחר את תפקידי IAM להעניק לחשבון השירות.
  6. לחץ על המשך .
  7. אופציונלי: תחת הענק למשתמשים גישה לחשבון שירות זה , הוסף את המשתמשים או הקבוצות המורשים להשתמש ולנהל את חשבון השירות.
  8. לחץ על סיום .

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

  1. לחץ על כתובת הדוא"ל של חשבון השירות שיצרת.
  2. לחץ על הכרטיסייה מפתחות .
  3. ברשימה הנפתחת הוסף מפתח , בחר צור מפתח חדש .
  4. לחץ על צור .

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

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

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

הענקת הרשאה ברמת הדומיין לחשבון השירות

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

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

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

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

הכנה לקריאה להענקת גישה ל-API

Java

אחרי שמקבלים את כתובת האימייל והמפתח הפרטי של הלקוח מ- API Console, משתמשים בספריית הלקוח של Google APIs ל-Java כדי ליצור אובייקט GoogleCredential מפרטי הכניסה של חשבון השירות ומתחומי הגישה שהאפליקציה צריכה גישה אליהם. לדוגמה:

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.sqladmin.SQLAdminScopes;

// ...

GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"))
    .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN));

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

הענקת גישה ברמת הדומיין

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

GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"))
    .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN))
    .createDelegated("[email protected]");

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

Python

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

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

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

  2. הענקת גישה ברמת הדומיין

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

    delegated_credentials = credentials.with_subject('[email protected]')

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

HTTP/REST

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

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

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

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

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

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

בהמשך הקטע הזה מתוארים הפרטים הספציפיים של יצירת 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

הכותרת מורכבת משלושה שדות שמציינים את אלגוריתם החתימה, את הפורמט של טענת הנכוֹנוּת ואת [מזהה המפתח של מפתח חשבון השירות](https://cloud.google.com/iam/docs/reference/rest/v1/projects.serviceAccounts.keys) ששימש לחתימה על ה-JWT. חובה לציין את האלגוריתם והפורמט, וכל שדה יכול להכיל רק ערך אחד. ככל שנוסיף אלגוריתמים ופורמטים, הכותרת הזו תשתנה בהתאם. מזהה המפתח הוא אופציונלי. אם יצוין מזהה מפתח שגוי, GCP ינסה את כל המפתחות שמשויכים לחשבון השירות כדי לאמת את הטוקן, ויידחה את הטוקן אם לא יימצא מפתח תקין. 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": "[email protected]",
  "scope": "https://www.googleapis.com/auth/prediction",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
קידוד של קבוצת ההצהרות של JWT

בדומה לכותרת של JWT, צריך לבצע סריאליזציה של קבוצת ההצהרות של JWT ל-UTF-8 ולקידוד Base64 ללא סיכון לאבטחת ה-URL. בהמשך מוצגת דוגמה לייצוג 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 Web Signature (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 החתום, אפליקציה יכולה להשתמש בו כדי לבקש אסימון גישה. בקשת אסימון הגישה הזו היא בקשת POST ב-HTTPS, והגוף שלה מקודד ככתובת URL. כתובת ה-URL מוצגת בהמשך:

https://oauth2.googleapis.com/token

הפרמטרים הבאים נדרשים בבקשה POST ב-HTTPS:

שם תיאור
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.

קריאה ל-Google APIs

Java

כדי להשתמש באובייקט GoogleCredential כדי לקרוא ל-Google APIs, מבצעים את השלבים הבאים:

  1. יוצרים אובייקט שירות ל-API שרוצים לקרוא באמצעות אובייקט GoogleCredential. לדוגמה:
    SQLAdmin sqladmin =
        new SQLAdmin.Builder(httpTransport, JSON_FACTORY, credential).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 בכותרת Authorization של HTTP. כשהדבר אפשרי, עדיף להשתמש בכותרת ה-HTTP, כי מחרוזות השאילתות נוטים להיות גלויות ביומנים של השרת. ברוב המקרים אפשר להשתמש בספריית לקוח כדי להגדיר את הקריאות לממשקי Google API (לדוגמה, כשקוראים ל-Drive Files API).

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

דוגמאות לבקשות HTTP GET

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

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.

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

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

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

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

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

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

invalid_client (כל ערך)

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

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

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

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

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

invalid_grant Not a valid email. המשתמש לא קיים. בודקים שכתובת האימייל בטענת הנכונות (שדה) 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.

טענת הנכוֹנוּת (assertion) של 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

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

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

  1. יוצרים חשבון שירות כפי שמתואר למעלה. חשוב לשמור את קובץ ה-JSON שמקבלים כשיוצרים את החשבון.
  2. באמצעות כל ספריית JWT רגילה, כמו זו שזמינה ב-jwt.io, יוצרים JWT עם כותרת ומטען ייעודי (payload), כמו בדוגמה הבאה:
    {
      "alg": "RS256",
      "typ": "JWT",
      "kid": "abcdef1234567890"
    }
    .
    {
      "iss": "[email protected]",
      "sub": "[email protected]",
      "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 מציינים את השעה בדיוק 3600 שניות מאוחר יותר, שבה יפוג תוקף ה-JWT.

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

לדוגמה:

Java

באמצעות google-api-java-client ו-‎java-jwt:

GoogleCredential credential =
        GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"));
PrivateKey privateKey = credential.getServiceAccountPrivateKey();
String privateKeyId = credential.getServiceAccountPrivateKeyId();

long now = System.currentTimeMillis();

try {
    Algorithm algorithm = Algorithm.RSA256(null, privateKey);
    String signedJwt = JWT.create()
        .withKeyId(privateKeyId)
        .withIssuer("[email protected]")
        .withSubject("[email protected]")
        .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': '[email protected]',
           'sub': '[email protected]',
           '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

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