OAuth 2.0 לאפליקציות אינטרנט בצד הלקוח

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

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

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

ספריית הלקוח של Google APIs ו-Google Identity Services

אם אתם משתמשים בספריית הלקוח של Google APIs ל-JavaScript כדי לבצע קריאות מורשות ל-Google, עליכם להשתמש בספריית JavaScript של Google Identity Services כדי לטפל בתהליך של OAuth 2.0. מומלץ לעיין במודל האסימונים של Google Identity Services, שמבוסס על התהליך של הענקה משתמעת ב-OAuth 2.0.

דרישות מוקדמות

הפעלת ממשקי API בפרויקט

כל אפליקציה שמבצעת קריאה ל-Google APIs צריכה להפעיל את ממשקי ה-API האלה ב- API Console.

כדי להפעיל ממשק API בפרויקט:

  1. Open the API Library ב Google API Console.
  2. If prompted, select a project, or create a new one.
  3. ב- API Library מפורטים כל ממשקי ה-API הזמינים, שמקובצים לפי משפחת מוצרים ופופולריות. אם ממשק ה-API שרוצים להפעיל לא מופיע ברשימה, אפשר לחפש אותו או ללחוץ על הצגת הכול במשפחת המוצרים שאליה הוא שייך.
  4. בוחרים את ה-API שרוצים להפעיל ולוחצים על הלחצן Enable.
  5. If prompted, enable billing.
  6. If prompted, read and accept the API's Terms of Service.

יצירת פרטי כניסה להרשאה

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

  1. Go to the Credentials page.
  2. לוחצים על Create credentials (יצירת פרטי כניסה) > OAuth client ID (מזהה לקוח OAuth).
  3. בוחרים את סוג האפליקציה Web application.
  4. ממלאים את הטופס. באפליקציות שמשתמשות ב-JavaScript כדי לשלוח בקשות מורשות ל-Google API, צריך לציין מקורות JavaScript מורשים. המקור מזהה את הדומיינים שמהם האפליקציה יכולה לשלוח בקשות לשרת OAuth 2.0. מקורות המידע האלה חייבים לעמוד בכללי האימות של Google.

זיהוי היקפי הגישה

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

לפני שמתחילים להטמיע הרשאה מסוג OAuth 2.0, מומלץ לזהות את היקפי ההרשאות שאליהם האפליקציה תצטרך גישה.

במסמך היקפי הרשאות API של OAuth 2.0 מופיעה רשימה מלאה של ההיקפים שבהם אפשר להשתמש כדי לגשת לממשקי Google APIs.

קבלת אסימוני גישה מסוג OAuth 2.0

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

שלב 1: הפניה אוטומטית לשרת OAuth 2.0 של Google

כדי לבקש הרשאה לגשת לנתונים של משתמש, מפנים את המשתמש לשרת OAuth 2.0 של Google.

נקודות קצה של OAuth 2.0

יוצרים כתובת URL לבקשת גישה מנקודת הקצה של OAuth 2.0 של Google בכתובת https://accounts.google.com/o/oauth2/v2/auth. אפשר לגשת לנקודת הקצה הזו דרך HTTPS, וחיבורי HTTP רגילים נדחים.

שרת ההרשאות של Google תומך בפרמטרים הבאים של מחרוזת השאילתה לאפליקציות של שרת אינטרנט:

פרמטרים
client_id חובה

מזהה הלקוח של האפליקציה. הערך הזה מופיע ב- API Console Credentials page.

redirect_uri חובה

קובע לאן שרת ה-API מפנה את המשתמש אחרי שהוא משלים את תהליך ההרשאה. הערך צריך להתאים בדיוק לאחד מכתובות ה-URI המורשות להפניה אוטומטית של לקוח OAuth 2.0, שהגדרתם ב- API Console Credentials pageשל הלקוח. אם הערך הזה לא תואם למזהה URI מורשה להפניה אוטומטית של client_id שצוין, תופיע הודעת השגיאה redirect_uri_mismatch.

חשוב לשים לב שצריך להתאים את הסכימה, האותיות הגדולות והקטנות והקו האופק (/) של http או https.

response_type חובה

באפליקציות JavaScript צריך להגדיר את ערך הפרמטר כ-token. הערך הזה מציין לשרת ההרשאות של Google להחזיר את אסימון הגישה כצמד name=value במזהה הרצף של מזהה ה-URI (#) שאליו המשתמש מופנה אוטומטית אחרי השלמת תהליך ההרשאה.

scope חובה

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

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

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

state מומלץ

מציין ערך מחרוזת שבו האפליקציה משתמשת כדי לשמור את המצב בין בקשת ההרשאה לבין התשובה של שרת ההרשאות. השרת מחזיר את הערך המדויק ששלחתם כצמד name=value במזהה הרצף של כתובת ה-URL (#) של redirect_uri אחרי שהמשתמש נותן הסכמה לבקשת הגישה של האפליקציה או דוחה אותה.

אפשר להשתמש בפרמטר הזה למספר מטרות, למשל כדי להפנות את המשתמש למשאב הנכון באפליקציה, לשלוח ערכים חד-פעמיים (nonces) ולצמצם זיוף בקשות בין אתרים. מכיוון שאפשר לנחש את הערך של redirect_uri, שימוש בערך state יכול להגדיל את הוודאות שחיבור נכנס הוא תוצאה של בקשת אימות. אם יוצרים מחרוזת אקראית או מקודדים את הגיבוב של קובץ cookie או ערך אחר שמתעד את מצב הלקוח, אפשר לאמת את התגובה כדי לוודא בנוסף שהבקשה והתגובה מקורן באותו דפדפן, וכך לספק הגנה מפני התקפות כמו זיוף בקשות בין אתרים. במסמכי העזרה של OpenID Connect מוסבר איך יוצרים אסימון state ומאשרים אותו.

include_granted_scopes אופציונלי

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

login_hint אופציונלי

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

מגדירים את ערך הפרמטר ככתובת אימייל או כמזהה sub, שהוא שווה למזהה Google של המשתמש.

prompt אופציונלי

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

הערכים האפשריים הם:

none אין להציג מסכי אימות או הסכמה. אסור לציין אותו עם ערכים אחרים.
consent מבקשים מהמשתמש להביע הסכמה.
select_account מבקשים מהמשתמש לבחור חשבון.

דוגמה להפניה אוטומטית לשרת ההרשאות של Google

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

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly&
 include_granted_scopes=true&
 response_type=token&
 state=state_parameter_passthrough_value&
 redirect_uri=https%3A//oauth2.example.com/code&
 client_id=client_id

אחרי שיוצרים את כתובת ה-URL של הבקשה, מפנים את המשתמש אליה.

קוד JavaScript לדוגמה

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

/*
 * Create form to request access token from Google's OAuth 2.0 server.
 */
function oauthSignIn() {
  // Google's OAuth 2.0 endpoint for requesting an access token
  var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';

  // Create <form> element to submit parameters to OAuth 2.0 endpoint.
  var form = document.createElement('form');
  form.setAttribute('method', 'GET'); // Send as a GET request.
  form.setAttribute('action', oauth2Endpoint);

  // Parameters to pass to OAuth 2.0 endpoint.
  var params = {'client_id': 'YOUR_CLIENT_ID',
                'redirect_uri': 'YOUR_REDIRECT_URI',
                'response_type': 'token',
                'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly',
                'include_granted_scopes': 'true',
                'state': 'pass-through value'};

  // Add form parameters as hidden input values.
  for (var p in params) {
    var input = document.createElement('input');
    input.setAttribute('type', 'hidden');
    input.setAttribute('name', p);
    input.setAttribute('value', params[p]);
    form.appendChild(input);
  }

  // Add form to page and submit it to open the OAuth 2.0 endpoint.
  document.body.appendChild(form);
  form.submit();
}

שלב 2: Google מבקשת מהמשתמש להביע הסכמה

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

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

שגיאות

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

admin_policy_enforced

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

disallowed_useragent

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

Android

מפתחי Android עשויים להיתקל בהודעת השגיאה הזו כשהם פותחים בקשות הרשאה ב-android.webkit.WebView. במקום זאת, המפתחים צריכים להשתמש בספריות של Android, כמו Google Sign-In ל-Android או AppAuth ל-Android של OpenID Foundation.

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

iOS

מפתחים של iOS ו-macOS עשויים להיתקל בשגיאה הזו כשהם פותחים בקשות הרשאה ב-WKWebView. במקום זאת, המפתחים צריכים להשתמש בספריות ל-iOS כמו Google Sign-In ל-iOS או AppAuth ל-iOS של OpenID Foundation.

מפתחי אינטרנט עשויים להיתקל בשגיאה הזו כשאפליקציה ל-iOS או ל-macOS פותחת קישור אינטרנט כללי בסוכן משתמש מוטמע, ומשתמש מנווט מנקודת הקצה של הרשאת OAuth 2.0 של Google מהאתר שלכם. מפתחים צריכים לאפשר לקישורים כלליים להיפתח במנהל הקישורים שמוגדר כברירת מחדל במערכת ההפעלה, שכולל גם את מנהלי Universal Links או את אפליקציית הדפדפן שמוגדרת כברירת מחדל. גם הספרייה SFSafariViewController נתמכת.

org_internal

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

invalid_client

המקור שממנו נשלחה הבקשה לא מורשה ללקוח הזה. ראו origin_mismatch.

invalid_grant

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

origin_mismatch

ייתכן שהסכימת, הדומיין ו/או היציאה של ה-JavaScript שממנו נשלחה בקשת ההרשאה לא יהיו תואמים ל-URI של מקור מורשה של JavaScript שרשום למזהה הלקוח ב-OAuth. בודקים את המקורות המורשים של JavaScript ב- Google API Console Credentials page.

redirect_uri_mismatch

הערך של redirect_uri שהוענק בבקשת ההרשאה לא תואם לכתובת URI מורשית להפניה אוטומטית למזהה הלקוח של OAuth. בודקים את מזהי ה-URI המורשים להפניה אוטומטית בקטע Google API Console Credentials page.

ייתכן שהסכימת, הדומיין ו/או היציאה של ה-JavaScript שממנו נשלחה בקשת ההרשאה לא יהיו תואמים ל-URI של מקור מורשה של JavaScript שרשום למזהה הלקוח ב-OAuth. בודקים את המקורות המורשים של JavaScript בקובץ Google API Console Credentials page.

הפרמטר redirect_uri עשוי להתייחס לתהליך OAuth מחוץ למסגרת (OOB) שהוצא משימוש ולא נתמך יותר. כדי לעדכן את השילוב, אפשר לעיין במדריך להעברה.

invalid_request

הייתה בעיה בבקשה ששלחת. יכולות להיות לכך כמה סיבות:

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

שלב 3: טיפול בתגובת השרת של OAuth 2.0

נקודות קצה של OAuth 2.0

שרת OAuth 2.0 שולח תגובה אל redirect_uri שצוין בבקשה של אסימון הגישה.

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

  • תגובה של טוקן גישה:

    https://oauth2.example.com/callback#access_token=4/P7q7W91&token_type=Bearer&expires_in=3600

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

  • תגובת שגיאה:
    https://oauth2.example.com/callback#error=access_denied

דוגמה לתשובה של שרת OAuth 2.0

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

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly&
 include_granted_scopes=true&
 response_type=token&
 state=state_parameter_passthrough_value&
 redirect_uri=https%3A//oauth2.example.com/code&
 client_id=client_id

אחרי השלמת תהליך OAuth 2.0, תופנו לכתובת http://localhost/oauth2callback. כתובת ה-URL הזו תגרום לשגיאה 404 NOT FOUND, אלא אם המחשב המקומי יציג קובץ בכתובת הזו. בשלב הבא נספק פרטים נוספים על המידע שמוחזר ב-URI כשהמשתמש מופנה חזרה לאפליקציה.

שלב 4: בודקים אילו היקפי הרשאות המשתמשים העניקו

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

נקודות קצה של OAuth 2.0

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

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

  {
    "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
    "expires_in": 3920,
    "token_type": "Bearer",
    "scope": "https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly",
    "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
  }

קריאה ל-Google APIs

נקודות קצה של OAuth 2.0

אחרי שהאפליקציה מקבלת אסימון גישה, אפשר להשתמש באסימון כדי לבצע קריאות ל-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

קוד JavaScript לדוגמה

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

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

var xhr = new XMLHttpRequest();
xhr.open('GET',
    'https://www.googleapis.com/drive/v3/about?fields=user&' +
    'access_token=' + params['access_token']);
xhr.onreadystatechange = function (e) {
  console.log(xhr.response);
};
xhr.send(null);

דוגמה מלאה

נקודות קצה של OAuth 2.0

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

בתהליך OAuth 2.0, הדף עובר את השלבים הבאים:

  1. הוא מפנה את המשתמש לשרת OAuth 2.0 של Google, שמבקש גישה להיקפים https://www.googleapis.com/auth/drive.metadata.readonly ו-https://www.googleapis.com/auth/calendar.readonly.
  2. אחרי שמעניקים (או דוחים) גישה להיקף הרשאות אחד או יותר שביקשת, המשתמש מופנה לדף המקורי, שבו מתבצע ניתוח של אסימון הגישה מתוך מחרוזת מזהה החלקיק.
  3. בדף בודקים לאילו היקפי הרשאות המשתמש העניק גישה לאפליקציה.
  4. אם המשתמש העניק גישה להיקף הבקשה, הדף משתמש באסימון הגישה כדי לשלוח את בקשת ה-API לדוגמה.

    בקשת ה-API מפעילה את השיטה about.get של Drive API כדי לאחזר מידע על חשבון Google Drive של המשתמש המורשה.

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

אפשר לבטל את הגישה לאפליקציה דרך הדף Permissions בחשבון Google. האפליקציה תופיע כOAuth 2.0 Demo for Google API Docs.

כדי להריץ את הקוד הזה באופן מקומי, צריך להגדיר ערכים למשתנים YOUR_CLIENT_ID ו-YOUR_REDIRECT_URI שתואמים לפרטי הכניסה להרשאה. צריך להגדיר את המשתנה YOUR_REDIRECT_URI לאותה כתובת URL שבה הדף מוצג. הערך צריך להתאים בדיוק לאחד מ-URI להפניה אוטומטית המורשים של לקוח OAuth 2.0, שהגדרתם ב- API Console Credentials page. אם הערך הזה לא תואם ל-URI מורשה, תופיע הודעת השגיאה redirect_uri_mismatch. בנוסף, צריך להפעיל את ממשק ה-API המתאים בפרויקט.

<html><head></head><body>
<script>
  var YOUR_CLIENT_ID = 'REPLACE_THIS_VALUE';
  var YOUR_REDIRECT_URI = 'REPLACE_THIS_VALUE';

  // Parse query string to see if page request is coming from OAuth 2.0 server.
  var fragmentString = location.hash.substring(1);
  var params = {};
  var regex = /([^&=]+)=([^&]*)/g, m;
  while (m = regex.exec(fragmentString)) {
    params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
  }
  if (Object.keys(params).length > 0 && params['state']) {
    if (params['state'] == localStorage.getItem('state')) {
      localStorage.setItem('oauth2-test-params', JSON.stringify(params) );

      trySampleRequest();
    } else {
      console.log('State mismatch. Possible CSRF attack');
    }
  }

  // Function to generate a random state value
  function generateCryptoRandomState() {
    const randomValues = new Uint32Array(2);
    window.crypto.getRandomValues(randomValues);

    // Encode as UTF-8
    const utf8Encoder = new TextEncoder();
    const utf8Array = utf8Encoder.encode(
      String.fromCharCode.apply(null, randomValues)
    );

    // Base64 encode the UTF-8 data
    return btoa(String.fromCharCode.apply(null, utf8Array))
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=+$/, '');
  }

  // If there's an access token, try an API request.
  // Otherwise, start OAuth 2.0 flow.
  function trySampleRequest() {
    var params = JSON.parse(localStorage.getItem('oauth2-test-params'));
    if (params && params['access_token']) { 
      // User authorized the request. Now, check which scopes were granted.
      if (params['scope'].includes('https://www.googleapis.com/auth/drive.metadata.readonly')) {
        // User authorized read-only Drive activity permission.
        // Calling the APIs, etc.
        var xhr = new XMLHttpRequest();
        xhr.open('GET',
          'https://www.googleapis.com/drive/v3/about?fields=user&' +
          'access_token=' + params['access_token']);
        xhr.onreadystatechange = function (e) {
          if (xhr.readyState === 4 && xhr.status === 200) {
            console.log(xhr.response);
          } else if (xhr.readyState === 4 && xhr.status === 401) {
            // Token invalid, so prompt for user permission.
            oauth2SignIn();
          }
        };
        xhr.send(null);
      }
      else {
        // User didn't authorize read-only Drive activity permission.
        // Update UX and application accordingly
        console.log('User did not authorize read-only Drive activity permission.');
      }

      // Check if user authorized Calendar read permission.
      if (params['scope'].includes('https://www.googleapis.com/auth/calendar.readonly')) {
        // User authorized Calendar read permission.
        // Calling the APIs, etc.
        console.log('User authorized Calendar read permission.');
      }
      else {
        // User didn't authorize Calendar read permission.
        // Update UX and application accordingly
        console.log('User did not authorize Calendar read permission.');
      } 
    } else {
      oauth2SignIn();
    }
  }

  /*
   * Create form to request access token from Google's OAuth 2.0 server.
   */
  function oauth2SignIn() {
    // create random state value and store in local storage
    var state = generateCryptoRandomState();
    localStorage.setItem('state', state);

    // Google's OAuth 2.0 endpoint for requesting an access token
    var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';

    // Create element to open OAuth 2.0 endpoint in new window.
    var form = document.createElement('form');
    form.setAttribute('method', 'GET'); // Send as a GET request.
    form.setAttribute('action', oauth2Endpoint);

    // Parameters to pass to OAuth 2.0 endpoint.
    var params = {'client_id': YOUR_CLIENT_ID,
                  'redirect_uri': YOUR_REDIRECT_URI,
                  'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly',
                  'state': state,
                  'include_granted_scopes': 'true',
                  'response_type': 'token'};

    // Add form parameters as hidden input values.
    for (var p in params) {
      var input = document.createElement('input');
      input.setAttribute('type', 'hidden');
      input.setAttribute('name', p);
      input.setAttribute('value', params[p]);
      form.appendChild(input);
    }

    // Add form to page and submit it to open the OAuth 2.0 endpoint.
    document.body.appendChild(form);
    form.submit();
  }
</script>

<button onclick="trySampleRequest();">Try sample request</button>
</body></html>

כללי אימות המקור של JavaScript

Google מחילה את כללי האימות הבאים על מקורות JavaScript כדי לעזור למפתחים לשמור על אבטחת האפליקציות שלהם. מקורות ה-JavaScript חייבים לעמוד בכללים האלה. ההגדרות של דומיין, מארח וסכימה מפורטות בקטע 3 של RFC 3986.

כללי אימות
Scheme

מקורות JavaScript חייבים להשתמש בסכמת HTTPS ולא ב-HTTP רגיל. מזהי URI של localhost (כולל מזהי URI של כתובות IP של localhost) פטורים מהכלל הזה.

מארח

לא ניתן להשתמש בכתובות IP גולמיות כמארחים. כתובות IP של localhost פטורות מהכלל הזה.

דומיין
  • דומיינים ברמה העליונה (TLD) של מארח חייבים להשתייך לרשימת הסיומות הציבוריות.
  • דומיינים של מארחים לא יכולים להיות מסוג “googleusercontent.com”.
  • מקורות של JavaScript לא יכולים להכיל דומיינים של קיצורי כתובות URL (למשל goo.gl), אלא אם הדומיין הוא בבעלות האפליקציה.
  • Userinfo

    מקורות JavaScript לא יכולים להכיל את רכיב המשנה userinfo.

    נתיב

    מקורות JavaScript לא יכולים להכיל את רכיב הנתיב.

    שאילתה

    מקורות JavaScript לא יכולים להכיל את רכיב השאילתה.

    Fragment

    מקורות של JavaScript לא יכולים להכיל את רכיב השבר.

    דמויות מקורות של JavaScript לא יכולים להכיל תווים מסוימים, כולל:
    • תווים כלליים לחיפוש ('*')
    • תווים ASCII שלא ניתן להדפיס
    • קידוד אחוזים לא תקין (כל קידוד אחוזים שלא עומד בפורמט של קידוד כתובות URL, כלומר סימן אחוזים ואחריו שתי ספרות הקסדצימליות)
    • תווים ריקים (תו NULL מקודד, למשל, %00, %C0%80)

    הרשאה מצטברת

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

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

    במקרה כזה, בזמן הכניסה, האפליקציה עשויה לבקש את ההיקפים openid ו-profile כדי לבצע כניסה בסיסית, ולאחר מכן לבקש את ההיקף https://www.googleapis.com/auth/drive.file בזמן הבקשה הראשונה כדי לשמור מיקס.

    הכללים הבאים חלים על אסימון גישה שהתקבל מהרשאה מצטברת:

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

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

    נקודות קצה של OAuth 2.0

    כדי להוסיף היקפי הרשאות לאסימון גישה קיים, צריך לכלול את הפרמטר include_granted_scopes בבקשה לשרת OAuth 2.0 של Google.

    קטע הקוד הבא מדגים איך עושים את זה. קטע הקוד הזה מבוסס על ההנחה ששמרתם באחסון המקומי של הדפדפן את ההיקפים שבהם טוקן הגישה תקף. (קוד הדוגמה המלאה שומר רשימה של היקפי הגישה שבהם אסימון הגישה תקף, על ידי הגדרת המאפיין oauth2-test-params.scope באחסון המקומי של הדפדפן).

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

    var SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly';
    var params = JSON.parse(localStorage.getItem('oauth2-test-params'));
    
    var current_scope_granted = false;
    if (params.hasOwnProperty('scope')) {
      var scopes = params['scope'].split(' ');
      for (var s = 0; s < scopes.length; s++) {
        if (SCOPE == scopes[s]) {
          current_scope_granted = true;
        }
      }
    }
    
    if (!current_scope_granted) {
      oauth2SignIn(); // This function is defined elsewhere in this document.
    } else {
      // Since you already have access, you can proceed with the API request.
    }

    ביטול טוקן

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

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

    נקודות קצה של OAuth 2.0

    כדי לבטל אסימון באופן פרוגרמטי, האפליקציה שולחת בקשה אל https://oauth2.googleapis.com/revoke ומצרפת את האסימון כפרמטר:

    curl -d -X -POST --header "Content-type:application/x-www-form-urlencoded" \
            https://oauth2.googleapis.com/revoke?token={token}

    האסימון יכול להיות אסימון גישה או אסימון רענון. אם האסימון הוא אסימון גישה ויש לו אסימון רענון תואם, גם אסימון הרענון יבוטל.

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

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

    function revokeAccess(accessToken) {
      // Google's OAuth 2.0 endpoint for revoking access tokens.
      var revokeTokenEndpoint = 'https://oauth2.googleapis.com/revoke';
    
      // Create <form> element to use to POST data to the OAuth 2.0 endpoint.
      var form = document.createElement('form');
      form.setAttribute('method', 'post');
      form.setAttribute('action', revokeTokenEndpoint);
    
      // Add access token to the form so it is set as value of 'token' parameter.
      // This corresponds to the sample curl request, where the URL is:
      //      https://oauth2.googleapis.com/revoke?token={token}
      var tokenField = document.createElement('input');
      tokenField.setAttribute('type', 'hidden');
      tokenField.setAttribute('name', 'token');
      tokenField.setAttribute('value', accessToken);
      form.appendChild(tokenField);
    
      // Add form to page and submit it to actually revoke the token.
      document.body.appendChild(form);
      form.submit();
    }

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

    כדי להגן על חשבונות המשתמשים, כדאי להטמיע הגנה על חשבונות שונים באמצעות שירות ההגנה על חשבונות שונים של 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

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