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

ונחיש לעבוד

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

בדרך כלל, אפליקציה משתמשת בחשבון שירות כשהיא משתמשת ב-Google APIs כדי לעבוד עם הנתונים שלה במקום עם נתוני המשתמש. לדוגמה, אפליקציה שמשתמשת ב-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 Workspace, היא תשתמש בחשבון שירות כדי לגשת ל-Google Calendar API בשמם של משתמשים. כשמעניקים לחשבון שירות הרשאה לגשת לנתונים מטעם משתמשים בדומיין, הדבר מכונה לפעמים "הענקת סמכויות ברמת הדומיין" לחשבון שירות.

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

  1. ב מסוף Admin של הדומיין ב-Google Workspace, נכנסים אל התפריט הראשי > אבטחה > שליטה בגישה ובנתונים > בקרות API.
  2. בחלונית הענקת גישה ברמת הדומיין, בוחרים באפשרות ניהול הענקת גישה ברמת הדומיין.
  3. לוחצים על הוספת חדש.
  4. בשדה Client ID (מזהה לקוח), מזינים את Client-ID של חשבון השירות. מזהה הלקוח של חשבון השירות שלך נמצא בקטע Service accounts page.
  5. בשדה היקפים של OAuth (מופרדים בפסיקים), מזינים את הרשימה של היקפי ההרשאות שהאפליקציה צריכה לקבל אליהם גישה. לדוגמה, אם האפליקציה שלך צריכה גישה מלאה לממשק ה-API של Google Drive ולממשק ה-API של יומן Google ברמת הדומיין, עליך להזין: 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 Platform, תוכלו להשתמש במקום זאת בפרטי הכניסה שמוגדרים כברירת מחדל באפליקציה. כך התהליך יכול להיות פשוט יותר.

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

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

GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"))
    .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN))
    .createDelegated("workspace-user@example.com");

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

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 Platform, תוכלו להשתמש במקום זאת בפרטי הכניסה שמוגדרים כברירת מחדל באפליקציה. כך התהליך יכול להיות פשוט יותר.

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

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

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

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

HTTP/REST

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

  1. עליך ליצור אסימון אינטרנט מסוג JSON (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

הכותרת מורכבת משלושה שדות שמציינים את אלגוריתם החתימה, את הפורמט של טענת הנכוֹנוּת (assertion) ואת [מזהה המפתח של המפתח של חשבון השירות](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 זמן התפוגה של טענת הנכוֹנוּת (assertion), שמצוין בשניות מאז 00:00:00 UTC, 1 בינואר 1970. הערך הזה מוגבל לשעה אחת אחרי מועד ההנפקה.
iat השעה שבה טענת הנכוֹנוּת (assertion) צוינה בשניות מאז 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-safe. לפניכם דוגמה לייצוג 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, כולל החתימה.

בהמשך מופיעה קובץ dump של בקשת POST מסוג HTTPS, שנעשה בו שימוש בבקשה לאסימון גישה:

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 בפרויקטירו 123 לדוגמה:
    SQLAdmin.Instances.List instances =
        sqladmin.instances().list("exciting-example-123").execute();

Python

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

  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 בפרויקטירו 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).

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

דוגמאות ל-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 (בשדה).

בדרך כלל התהליך נמשך כמה דקות, אבל יכול להיות שיעברו עד 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. בדף הענקת גישה ברמת הדומיין במסוף Admin, מסירים את הלקוח ומוסיפים אותו מחדש עם המזהה המספרי.
access_denied (כל ערך) אם משתמשים בהרשאה ברמת הדומיין, אחד או יותר מההיקפים המבוקשים לא מורשים במסוף Admin.

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

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

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

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

invalid_client (כל ערך)

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

אפשר לקרוא פרטים נוספים בתיאור השגיאה.

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

ודאו שלקוח OAuth וחשבון השירות של 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 חתומה באמצעות מפתח פרטי שלא משויך לחשבון השירות שזוהה באימייל של הלקוח, או שהמפתח שהשתמשת בו נמחק, הושבת או שתוקפו פג.

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

צריך לפענח את קבוצת תביעת ה-JWT ולאמת שהמפתח שחתום על טענת הנכוֹנוּת (assertion) משויך לחשבון השירות.

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

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

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

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

disabled_client The OAuth client was disabled. המפתח שמשמש לחתימה על טענת ה-JWT מושבת.

עוברים אל Google API Console, ובקטע IAM & Admin > Service Accounts, מפעילים את חשבון השירות שמכיל את "Key ID" המשמש לחתימה על טענת הנכוֹנוּת (assertion).

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

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

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

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

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

  1. יוצרים חשבון שירות כמו שמתואר למעלה. חשוב לשמור את קובץ ה-JSON שמתקבל כשיוצרים את החשבון.
  2. באמצעות כל ספריית JWT סטנדרטית, כמו ספריית 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-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("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