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 שרוצים להפעיל ולוחצים על הלחצן הפעלה.
  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 Client (יצירת לקוח).
  3. בוחרים את סוג האפליקציה Web application.
  4. ממלאים את הטופס. אפליקציות שמשתמשות ב-JavaScript כדי לשלוח בקשות מורשות ל-Google API חייבות לציין מקורות JavaScript מורשים. המקורות מזהים את הדומיינים שמהם האפליקציה יכולה לשלוח בקשות לשרת OAuth 2.0. המקורות האלה צריכים לעמוד בכללי האימות של Google.

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

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

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

במסמך היקפי OAuth 2.0 API מופיעה רשימה מלאה של היקפי הרשאות שאפשר להשתמש בהם כדי לגשת אל 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 חובה

מזהה הלקוח של האפליקציה. אפשר למצוא את הערך הזה ב .

redirect_uri חובה

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

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

response_type חובה

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

scope חובה

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

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

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

state מומלץ

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

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

disallowed_useragent

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

Android

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

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

iOS

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

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

org_internal

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

invalid_client

המקור שממנו נשלחה הבקשה לא מורשה עבור הלקוח הזה. מידע נוסף זמין במאמר origin_mismatch.

deleted_client

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

invalid_grant

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

origin_mismatch

יכול להיות שהסכימה, הדומיין או היציאה של ה-JavaScript שממנו נשלחת בקשת ההרשאה לא תואמים ל-URI של מקור JavaScript מורשה שרשום עבור מזהה לקוח OAuth. בודקים את המקורות המורשים של JavaScript ב .

redirect_uri_mismatch

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

יכול להיות שהסכימה, הדומיין או היציאה של ה-JavaScript שממנו נשלחת בקשת ההרשאה לא תואמים ל-URI של מקור JavaScript מורשה שרשום עבור מזהה לקוח OAuth. בודקים את המקורות המורשים של JavaScript ב .

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

invalid_request

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

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

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

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

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

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

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

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

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

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

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

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

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

דוגמאות ל-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

קוד 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 מתועדת במסוף לניפוי באגים בדפדפן.

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

כדי להריץ את הקוד הזה באופן מקומי, צריך להגדיר ערכים למשתנים YOUR_CLIENT_ID ו-YOUR_REDIRECT_URI שמתאימים לפרטי ההרשאה שלכם. המשתנה YOUR_REDIRECT_URI צריך להיות מוגדר לאותה כתובת URL שבה הדף מוצג. הערך חייב להיות זהה לאחד מכתובות ה-URI המורשות להפניה אוטומטית של לקוח OAuth 2.0, שהגדרתם ב- . אם הערך הזה לא תואם ל-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 חייבות לעמוד בכללים האלה. ההגדרות של domain,‏ host ו-scheme שמוזכרות בהמשך מופיעות ב-RFC 3986 section 3.

כללי אימות
Scheme

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

מארח

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

דומיין
  • דומיינים ברמה העליונה (TLD) של המארח (Top Level Domains) צריכים להיות חלק מרשימת הסיומות הציבוריות.
  • דומיינים מארחים לא יכולים להיות “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. מכיוון שנקודת הקצה של Google OAuth 2.0 לביטול אסימונים לא תומכת בשיתוף משאבים בין מקורות (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

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