במאמר הזה מוסבר איך אפליקציות של שרתי אינטרנט משתמשות בספריות הלקוח של Google API או בנקודות הקצה של Google OAuth 2.0 כדי להטמיע הרשאת OAuth 2.0 לגישה ל-Google APIs.
פרוטוקול OAuth 2.0 מאפשר למשתמשים לשתף נתונים ספציפיים עם אפליקציה, תוך שמירה על הפרטיות של שמות המשתמשים, הסיסמאות ומידע אחר. לדוגמה, אפליקציה יכולה להשתמש ב-OAuth 2.0 כדי לקבל הרשאה מהמשתמשים לאחסן קבצים ב-Google Drive שלהם.
תהליך OAuth 2.0 הזה מיועד ספציפית להרשאת משתמשים. הוא מיועד לאפליקציות שיכולות לאחסן מידע סודי ולשמור על המצב. אפליקציה של שרת אינטרנט שקיבלה הרשאה מתאימה יכולה לגשת ל-API בזמן שהמשתמש יוצר אינטראקציה עם האפליקציה או אחרי שהמשתמש יצא מהאפליקציה.
אפליקציות של שרתי אינטרנט משתמשות לעיתים קרובות גם ב חשבונות שירות כדי לאשר בקשות API, במיוחד כששולחים קריאה ל-Cloud APIs כדי לגשת לנתונים שמבוססים על פרויקטים, ולא לנתונים ספציפיים למשתמשים. אפליקציות של שרתי אינטרנט יכולות להשתמש בחשבונות שירות בשילוב עם הרשאת משתמש.
ספריות לקוח
הדוגמאות הספציפיות לשפה בדף הזה משתמשות בספריות הלקוח של Google API כדי להטמיע הרשאת OAuth 2.0. כדי להריץ את דוגמאות הקוד, קודם צריך להתקין את ספריית הלקוח בשפה הרצויה.
כשמשתמשים בספריית לקוח של Google API כדי לטפל בתהליך OAuth 2.0 של האפליקציה, ספריית הלקוח מבצעת פעולות רבות שהאפליקציה הייתה צריכה לטפל בהן בעצמה. לדוגמה, הוא קובע מתי האפליקציה יכולה להשתמש בטוקני גישה מאוחסנים או לרענן אותם, ומתי האפליקציה צריכה לקבל הסכמה מחדש. ספריית הלקוח גם יוצרת כתובות URL נכונות להפניה אוטומטית, ועוזרת להטמיע פונקציות טיפול בהפניה אוטומטית שמחליפות קודי הרשאה בטוקני גישה.
ספריות הלקוח של Google API לאפליקציות בצד השרת זמינות בשפות הבאות:
דרישות מוקדמות
הפעלת ממשקי API בפרויקט
בכל אפליקציה ששולחת קריאות ל-Google APIs, צריך להפעיל את ממשקי ה-API האלה בקונסולה לממשקי API.
כדי להפעיל API בפרויקט:
- פותחים את ספריית ה-API ב-Google API Console.
- אם מתבקשים, בוחרים פרויקט או יוצרים פרויקט חדש.
- בספריית ה-API מופיעים כל ממשקי ה-API הזמינים, מקובצים לפי משפחת מוצרים ופופולריות. אם ה-API שרוצים להפעיל לא מופיע ברשימה, אפשר לחפש אותו או ללחוץ על View All במשפחת המוצרים שאליה הוא שייך.
- בוחרים את ה-API שרוצים להפעיל ולוחצים על הכפתור Enable.
- אם מוצגת בקשה, מפעילים את החיוב.
- אם מוצגת בקשה, קוראים את התנאים וההגבלות של ה-API ומאשרים אותם.
יצירת פרטי כניסה לאימות
כל אפליקציה שמשתמשת ב-OAuth 2.0 כדי לגשת ל-Google APIs צריכה פרטי כניסה לאימות שמזהים את האפליקציה בשרת OAuth 2.0 של Google. כך יוצרים פרטי כניסה לפרויקט: לאחר מכן, האפליקציות שלכם יוכלו להשתמש בפרטי הכניסה כדי לגשת לממשקי ה-API שהפעלתם בפרויקט הזה.
- עוברים אל הדף "לקוחות".
- לוחצים על Create Client.
- בוחרים בסוג האפליקציה אפליקציית אינטרנט.
- ממלאים את הטופס ולוחצים על יצירה. באפליקציות שמשתמשות בשפות ובמסגרות כמו PHP, Java, Python, Ruby ו- .NET, צריך לציין מזהי URI מורשים להפניה אוטומטית. כתובות ה-URI להפניה אוטומטית הן נקודות הקצה שאליהן שרת OAuth 2.0 יכול לשלוח תגובות. נקודות הקצה האלה חייבות לעמוד בכללי האימות של Google.
למטרות בדיקה, אפשר לציין מזהי URI שמפנים למכונה המקומית, כמו
http://localhost:8080. בכל הדוגמאות במסמך הזה, כתובת ה-URI להפניה אוטומטית היאhttp://localhost:8080.מומלץ לעצב את נקודות הקצה לאימות של האפליקציה כך שהאפליקציה לא תחשוף קודי הרשאה למשאבים אחרים בדף.
אחרי שיוצרים את פרטי הכניסה, מורידים את הקובץ client_secret.json מקונסולה לממשקי API. שומרים את הקובץ באופן מאובטח במיקום שרק לאפליקציה יש גישה אליו.
זיהוי היקפי הגישה
היקפי ההרשאות מאפשרים לאפליקציה לבקש גישה רק למשאבים שהיא צריכה, וגם מאפשרים למשתמשים לשלוט במידת הגישה שהם נותנים לאפליקציה. לכן, יכול להיות שיהיה קשר הפוך בין מספר ההיקפים המבוקשים לבין הסבירות לקבלת הסכמה מהמשתמש.
לפני שמתחילים להטמיע הרשאת OAuth 2.0, מומלץ לזהות את היקפי ההרשאות שהאפליקציה תצטרך הרשאה כדי לגשת אליהם.
אנחנו גם ממליצים שהאפליקציה תבקש גישה להיקפי הרשאות באמצעות תהליך של הרשאה מצטברת, שבו האפליקציה מבקשת גישה לנתוני המשתמש בהקשר. השיטה המומלצת הזו עוזרת למשתמשים להבין בקלות רבה יותר למה האפליקציה צריכה את הגישה שהיא מבקשת.
במסמך היקפי ההרשאות של OAuth 2.0 API מפורטת רשימה מלאה של היקפי ההרשאות שבהם אפשר להשתמש כדי לגשת ל-Google APIs.
דרישות ספציפיות לשפה
כדי להריץ את דוגמאות הקוד במסמך הזה, צריך חשבון Google, גישה לאינטרנט ודפדפן אינטרנט. אם אתם משתמשים באחת מספריות הלקוח של ה-API, כדאי גם לעיין בדרישות הספציפיות לשפה בקטעים הבאים.
PHP
כדי להריץ את דוגמאות הקוד ב-PHP שמופיעות במסמך הזה, צריך:
- PHP בגרסה 8.0 ואילך עם ממשק שורת הפקודה (CLI) ותוסף JSON מותקן.
- כלי ניהול התלות Composer.
-
ספריית הלקוח של Google APIs ל-PHP:
composer require google/apiclient:^2.15.0
למידע נוסף, ראו ספריית הלקוח של Google APIs ל-PHP.
Python
כדי להריץ את דוגמאות הקוד ב-Python שמופיעות במסמך הזה, צריך:
- Python 3.7 ואילך
- הכלי לניהול חבילות pip.
- גרסה 2.0 של ספריית הלקוח של Google APIs ל-Python:
pip install --upgrade google-api-python-client
-
google-auth, google-auth-oauthlibו-google-auth-httplib2לאישור המשתמש.pip install --upgrade google-auth google-auth-oauthlib google-auth-httplib2
- מסגרת Flask לאפליקציות אינטרנט ב-Python.
pip install --upgrade flask
- ספריית ה-HTTP של
requests.pip install --upgrade requests
אם אתם לא יכולים לשדרג את Python ואת המדריך המשויך להעברת נתונים, כדאי לעיין בהערת הגרסה של ספריית הלקוח של Google API ל-Python.
Ruby
כדי להריץ את דוגמאות הקוד ב-Ruby שמופיעות במסמך הזה, צריך:
- Ruby 2.6 ואילך
-
ספריית Google Auth ל-Ruby:
gem install googleauth
-
ספריות הלקוח של ממשקי ה-API של Google Drive ויומן Google:
gem install google-apis-drive_v3 google-apis-calendar_v3
-
מסגרת לאפליקציות אינטרנט של Sinatra Ruby.
gem install sinatra
Node.js
כדי להריץ את דוגמאות הקוד של Node.js במסמך הזה, צריך:
- גרסת ה-LTS לתחזוקה, גרסת ה-LTS הפעילה או הגרסה הנוכחית של Node.js.
-
Google APIs Node.js Client:
npm install googleapis crypto express express-session
HTTP/REST
לא צריך להתקין ספריות כדי לשלוח קריאה ישירות לנקודות הקצה של OAuth 2.0.
איך מקבלים טוקני גישה מסוג OAuth 2.0
השלבים הבאים מראים איך האפליקציה יוצרת אינטראקציה עם שרת OAuth 2.0 של Google כדי לקבל את הסכמת המשתמש לביצוע בקשת API בשמו. האפליקציה חייבת לקבל את ההסכמה הזו לפני שהיא יכולה להפעיל בקשת Google API שדורשת הרשאת משתמש.
הרשימה הבאה מסכמת את השלבים האלה:
- האפליקציה מזהה את ההרשאות שהיא צריכה.
- האפליקציה מפנה את המשתמש ל-Google יחד עם רשימת ההרשאות המבוקשות.
- המשתמש מחליט אם להעניק את ההרשאות לאפליקציה.
- האפליקציה שלכם תגלה מה המשתמש החליט.
- אם המשתמש נתן את ההרשאות המבוקשות, האפליקציה מאחזרת את האסימונים שנדרשים כדי לשלוח בקשות API בשם המשתמש.
שלב 1: הגדרת פרמטרים של הרשאה
השלב הראשון הוא ליצור את בקשת ההרשאה. הבקשה הזו מגדירה פרמטרים שמזהים את האפליקציה ומגדירים את ההרשאות שהמשתמש יתבקש להעניק לאפליקציה.
- אם אתם משתמשים בספריית לקוח של Google לאימות ולהרשאה באמצעות OAuth 2.0, אתם צריכים ליצור ולהגדיר אובייקט שמגדיר את הפרמטרים האלה.
- אם אתם קוראים ישירות לנקודת הקצה של Google OAuth 2.0, אתם צריכים ליצור כתובת URL ולהגדיר את הפרמטרים בכתובת ה-URL הזו.
בכרטיסיות הבאות מוגדרים פרמטרים של הרשאה שנתמכים באפליקציות של שרתי אינטרנט. בדוגמאות הספציפיות לשפה מוצג גם איך להשתמש בספריית לקוח או בספריית הרשאות כדי להגדיר אובייקט שמגדיר את הפרמטרים האלה:
PHP
קטע הקוד הבא יוצר אובייקט Google\Client(), שמגדיר את הפרמטרים בבקשת ההרשאה.
האובייקט הזה משתמש במידע מהקובץ client_secret.json כדי לזהות את האפליקציה. (למידע נוסף על הקובץ הזה, ראו יצירת פרטי כניסה לאימות). האובייקט גם מזהה את היקפי ההרשאות שהאפליקציה מבקשת גישה אליהם, ואת כתובת ה-URL של נקודת הקצה לאימות של האפליקציה, שתטפל בתגובה משרת OAuth 2.0 של Google. לבסוף, הקוד מגדיר את הפרמטרים האופציונליים access_type ו-include_granted_scopes.
לדוגמה, הקוד הזה מבקש גישה אופליין לקריאה בלבד למטא-נתונים של המשתמש ב-Google Drive ולאירועים ביומן:
use Google\Client; $client = new Client(); // Required, call the setAuthConfig function to load authorization credentials from // client_secret.json file. $client->setAuthConfig('client_secret.json'); // Required, to set the scope value, call the addScope function $client->addScope([Google\Service\Drive::DRIVE_METADATA_READONLY, Google\Service\Calendar::CALENDAR_READONLY]); // Required, call the setRedirectUri function to specify a valid redirect URI for the // provided client_id $client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php'); // Recommended, offline access will give you both an access and refresh token so that // your app can refresh the access token without user interaction. $client->setAccessType('offline'); // Recommended, call the setState function. Using a state value can increase your assurance that // an incoming connection is the result of an authentication request. $client->setState($sample_passthrough_value); // Optional, if your application knows which user is trying to authenticate, it can use this // parameter to provide a hint to the Google Authentication Server. $client->setLoginHint('hint@example.com'); // Optional, call the setPrompt function to set "consent" will prompt the user for consent $client->setPrompt('consent'); // Optional, call the setIncludeGrantedScopes function with true to enable incremental // authorization $client->setIncludeGrantedScopes(true);
Python
קטע הקוד הבא משתמש במודול google-auth-oauthlib.flow כדי ליצור את בקשת ההרשאה.
הקוד יוצר אובייקט Flow שמזהה את האפליקציה באמצעות מידע מהקובץ client_secret.json שהורדתם אחרי יצירת פרטי הכניסה לאימות. האובייקט הזה גם מזהה את היקפי ההרשאות שהאפליקציה מבקשת גישה אליהם, ואת כתובת ה-URL של נקודת הקצה לאימות של האפליקציה, שתטפל בתגובה משרת OAuth 2.0 של Google. לבסוף, הקוד מגדיר את הפרמטרים האופציונליים access_type ו-include_granted_scopes.
לדוגמה, הקוד הזה מבקש גישה אופליין לקריאה בלבד למטא-נתונים של המשתמש ב-Google Drive ולאירועים ביומן:
import google.oauth2.credentials import google_auth_oauthlib.flow # Required, call the from_client_secrets_file method to retrieve the client ID from a # client_secret.json file. The client ID (from that file) and access scopes are required. (You can # also use the from_client_config method, which passes the client configuration as it originally # appeared in a client secrets file but doesn't access the file itself.) flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file('client_secret.json', scopes=['https://www.googleapis.com/auth/drive.metadata.readonly', 'https://www.googleapis.com/auth/calendar.readonly']) # Required, indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. flow.redirect_uri = 'https://www.example.com/oauth2callback' # Generate URL for request to Google's OAuth 2.0 server. # Use kwargs to set optional request parameters. authorization_url, state = flow.authorization_url( # Recommended, enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Optional, enable incremental authorization. Recommended as a best practice. include_granted_scopes='true', # Optional, if your application knows which user is trying to authenticate, it can use this # parameter to provide a hint to the Google Authentication Server. login_hint='hint@example.com', # Optional, set prompt to 'consent' will prompt the user for consent prompt='consent')
Ruby
משתמשים בקובץ client_secrets.json שיצרתם כדי להגדיר אובייקט לקוח באפליקציה. כשמגדירים אובייקט לקוח, מציינים את היקפי ההרשאות שהאפליקציה צריכה לגשת אליהם, יחד עם כתובת ה-URL של נקודת הקצה לאימות של האפליקציה, שתטפל בתגובה משרת OAuth 2.0.
לדוגמה, הקוד הזה מבקש גישה אופליין לקריאה בלבד למטא-נתונים של המשתמש ב-Google Drive ולאירועים ביומן:
require 'googleauth' require 'googleauth/web_user_authorizer' require 'googleauth/stores/redis_token_store' require 'google/apis/drive_v3' require 'google/apis/calendar_v3' # Required, call the from_file method to retrieve the client ID from a # client_secret.json file. client_id = Google::Auth::ClientId.from_file('/path/to/client_secret.json') # Required, scope value # Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. scope = ['Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY', 'Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY'] # Required, Authorizers require a storage instance to manage long term persistence of # access and refresh tokens. token_store = Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new) # Required, indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. callback_uri = '/oauth2callback' # To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI # from the client_secret.json file. To get these credentials for your application, visit # https://console.cloud.google.com/apis/credentials. authorizer = Google::Auth::WebUserAuthorizer.new(client_id, scope, token_store, callback_uri)
האפליקציה משתמשת באובייקט הלקוח כדי לבצע פעולות של OAuth 2.0, כמו יצירת כתובות URL של בקשות הרשאה והחלת טוקני גישה על בקשות HTTP.
Node.js
קטע הקוד הבא יוצר אובייקט google.auth.OAuth2, שמגדיר את הפרמטרים בבקשת ההרשאה.
האובייקט הזה משתמש במידע מהקובץ client_secret.json כדי לזהות את האפליקציה. כדי לבקש ממשתמש הרשאות לאחזור של טוקן גישה, צריך להפנות אותו לדף הסכמה. כדי ליצור כתובת URL של דף הסכמה:
const {google} = require('googleapis'); const crypto = require('crypto'); const express = require('express'); const session = require('express-session'); /** * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI * from the client_secret.json file. To get these credentials for your application, visit * https://console.cloud.google.com/apis/credentials. */ const oauth2Client = new google.auth.OAuth2( YOUR_CLIENT_ID, YOUR_CLIENT_SECRET, YOUR_REDIRECT_URL ); // Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. const scopes = [ 'https://www.googleapis.com/auth/drive.metadata.readonly', 'https://www.googleapis.com/auth/calendar.readonly' ]; // Generate a secure random state value. const state = crypto.randomBytes(32).toString('hex'); // Store state in the session req.session.state = state; // Generate a url that asks permissions for the Drive activity and Google Calendar scope const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true, // Include the state parameter to reduce the risk of CSRF attacks. state: state });
הערה חשובה – הערך refresh_token מוחזר רק בהרשאה הראשונה.
כאן אפשר למצוא פרטים נוספים.
HTTP/REST
נקודת הקצה של Google OAuth 2.0 נמצאת בכתובת https://accounts.google.com/o/oauth2/v2/auth. אפשר לגשת לנקודת הקצה הזו רק באמצעות HTTPS. חיבורי HTTP רגילים נדחים.
שרת ההרשאות של Google תומך בפרמטרים הבאים של מחרוזת שאילתה לאפליקציות של שרת אינטרנט:
| פרמטרים | |||||||
|---|---|---|---|---|---|---|---|
client_id |
חובה
מזהה הלקוח של האפליקציה. אפשר למצוא את הערך הזה בדף Clients במסוף Cloud. |
||||||
redirect_uri |
חובה
קובע לאן שרת ה-API מפנה את המשתמש אחרי שהוא משלים את תהליך ההרשאה. הערך חייב להיות זהה לאחד ממזהי ה-URI המורשים להפניה אוטומטית של לקוח OAuth 2.0, שהגדרתם בדף Clients של הלקוח במסוף Cloud. אם הערך הזה לא תואם ל-URI מורשה להפניה אוטומטית של ה- חשוב לשים לב שהסכמה |
||||||
response_type |
חובה
קובע אם נקודת הקצה של Google OAuth 2.0 מחזירה קוד הרשאה. באפליקציות של שרת אינטרנט, מגדירים את ערך הפרמטר ל- |
||||||
scope |
חובה
רשימה מופרדת ברווחים של היקפי הרשאות שמזהים את המשאבים שהאפליקציה יכולה לגשת אליהם בשם המשתמש. הערכים האלה משמשים את מסך ההסכמה ש-Google מציגה למשתמש. היקפי ההרשאות מאפשרים לאפליקציה לבקש גישה רק למשאבים שהיא צריכה, וגם מאפשרים למשתמשים לשלוט ברמת הגישה שהם נותנים לאפליקציה. לכן, יש קשר הפוך בין מספר ההיקפים המבוקשים לבין הסבירות לקבלת הסכמה מהמשתמש. מומלץ שהאפליקציה תבקש גישה להיקפי הרשאות בהקשר, כשאפשר. כשמבקשים גישה לנתוני משתמשים בהקשר המתאים, באמצעות הרשאה מצטברת, עוזרים למשתמשים להבין למה האפליקציה צריכה את הגישה שהיא מבקשת. |
||||||
access_type |
מומלץ
מציין אם האפליקציה יכולה לרענן טוקני גישה כשהמשתמש לא נמצא
בדפדפן. ערכי הפרמטרים החוקיים הם אם האפליקציה צריכה לרענן את טוקני הגישה כשהמשתמש לא נמצא בדפדפן, צריך להגדיר את הערך ל- |
||||||
state |
מומלץ
מציין ערך מחרוזת כלשהו שהאפליקציה משתמשת בו כדי לשמור על המצב בין בקשת ההרשאה לבין התגובה של שרת ההרשאות.
השרת מחזיר את הערך המדויק ששלחתם כצמד אתם יכולים להשתמש בפרמטר הזה למספר מטרות, כמו הפניית המשתמש למשאב הנכון באפליקציה, שליחת מספרים חד-פעמיים (nonce) והפחתת הסיכון לזיוף בקשות בין אתרים. מכיוון שאפשר לנחש את |
||||||
include_granted_scopes |
אופציונלי
מאפשר לאפליקציות להשתמש בהרשאה מצטברת כדי לבקש גישה להיקפים נוספים בהקשר. אם מגדירים את הערך של הפרמטר הזה כ- |
||||||
enable_granular_consent |
אופציונלי
ברירת המחדל היא כש-Google תפעיל הרשאות פרטניות לאפליקציה, הפרמטר הזה לא ישפיע יותר. |
||||||
login_hint |
אופציונלי
אם האפליקציה יודעת איזה משתמש מנסה לבצע אימות, היא יכולה להשתמש בפרמטר הזה כדי לספק רמז לשרת האימות של Google. השרת משתמש ברמז כדי לפשט את תהליך ההתחברות, על ידי מילוי מראש של שדה האימייל בטופס הכניסה או על ידי בחירת הסשן המתאים של ריבוי התחברויות. מגדירים את ערך הפרמטר לכתובת אימייל או למזהה |
||||||
prompt |
אופציונלי
רשימה של הנחיות שמוצגות למשתמש, מופרדות ברווחים ותלויות אותיות רישיות. אם לא מציינים את הפרמטר הזה, המשתמש יתבקש רק בפעם הראשונה שהפרויקט מבקש גישה. מידע נוסף זמין במאמר בקשה להסכמה מחדש. הערכים שאפשר לבחור הם:
|
||||||
שלב 2: הפניה לשרת OAuth 2.0 של Google
מפנים את המשתמש לשרת OAuth 2.0 של Google כדי להתחיל את תהליך האימות וההרשאה. בדרך כלל, זה קורה כשהאפליקציה צריכה לגשת לנתוני המשתמש בפעם הראשונה. במקרה של הרשאה מצטברת, השלב הזה מתרחש גם כשהאפליקציה צריכה לגשת בפעם הראשונה למשאבים נוספים שאין לה הרשאה לגשת אליהם.
PHP
- יוצרים כתובת URL כדי לבקש גישה משרת OAuth 2.0 של Google:
$auth_url = $client->createAuthUrl(); - מפנים את המשתמש אל
$auth_url:header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
Python
בדוגמה הבאה מוסבר איך להפנות את המשתמש לכתובת ה-URL לאימות באמצעות מסגרת אפליקציית האינטרנט Flask:
return flask.redirect(authorization_url)
Ruby
- יוצרים כתובת URL כדי לבקש גישה משרת OAuth 2.0 של Google:
auth_uri = authorizer.get_authorization_url(request: request)
- מפנים את המשתמש אל
auth_uri.
Node.js
-
משתמשים בכתובת ה-URL שנוצרה
authorizationUrlמהשיטהgenerateAuthUrlבשלב 1 כדי לבקש גישה משרת OAuth 2.0 של Google. -
מפנים את המשתמש אל
authorizationUrl.res.redirect(authorizationUrl);
HTTP/REST
הפניה לדוגמה לשרת ההרשאות של 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& access_type=offline& include_granted_scopes=true& response_type=code& state=state_parameter_passthrough_value& redirect_uri=https%3A//developers.google.com/oauthplayground& client_id=client_id
אחרי שיוצרים את כתובת ה-URL של הבקשה, מפנים אליה את המשתמש.
שרת OAuth 2.0 של Google מאמת את המשתמש ומקבל ממנו הסכמה לכך שהאפליקציה תיגש להיקפי ההרשאות המבוקשים. התגובה נשלחת בחזרה לאפליקציה באמצעות כתובת ה-URL להפניה אוטומטית שציינתם.
שלב 3: Google מבקשת הסכמה מהמשתמש
בשלב הזה, המשתמש מחליט אם להעניק לאפליקציה את הגישה המבוקשת. בשלב הזה, Google מציגה חלון הסכמה שבו מופיעים שם האפליקציה ושירותי Google API שהיא מבקשת הרשאת גישה אליהם באמצעות פרטי הכניסה של המשתמש, וסיכום של היקפי הגישה שיוענקו. לאחר מכן, המשתמש יכול להסכים להעניק גישה להיקף אחד או יותר שהאפליקציה ביקשה, או לדחות את הבקשה.
בשלב הזה, האפליקציה לא צריכה לעשות שום דבר בזמן שהיא ממתינה לתגובה משרת OAuth 2.0 של Google, שמציינת אם ניתנה גישה כלשהי. התגובה הזו מוסברת בשלב הבא.
שגיאות
יכול להיות שבבקשות לנקודת הקצה של הרשאת OAuth 2.0 של Google יוצגו הודעות שגיאה גלויות למשתמשים במקום תהליכי האימות וההרשאה הצפויים. אלה קודי השגיאה הנפוצים והפתרונות המוצעים:
admin_policy_enforced
בגלל המדיניות של האדמין ב-Google Workspace, אי אפשר לאשר בחשבון Google אחד או יותר מההיקפים שהתבקשו. במאמר העזרה לאדמינים ב-Google Workspace, איך בוחרים לאילו אפליקציות של צד שלישי ואפליקציות פנימיות תהיה גישה לנתונים של Google Workspace, מוסבר איך אדמינים יכולים להגביל את הגישה לכל היקפי ההרשאות או להיקפי הרשאות רגישים ומוגבלים עד שניתנת גישה מפורשת למזהה הלקוח ב-OAuth.
disallowed_useragent
נקודת הקצה של ההרשאה מוצגת בתוך סוכן משתמש מוטמע שאסור לפי מדיניות OAuth 2.0 של Google.
מפתחים ב-iOS וב-macOS עלולים להיתקל בשגיאה הזו כשהם פותחים בקשות הרשאה ב-WKWebView.
במקום זאת, המפתחים צריכים להשתמש בספריות iOS כמו כניסה באמצעות חשבון Google ל-iOS או AppAuth ל-iOS של OpenID Foundation.
מפתחי אתרים עשויים להיתקל בשגיאה הזו כשאפליקציה ל-iOS או ל-macOS פותחת קישור כללי לדף אינטרנט אחר בסוכן משתמש מוטמע, והמשתמש עובר לנקודת הקצה של Google לאימות באמצעות OAuth 2.0 מהאתר שלכם. המפתחים צריכים לאפשר לקישורים כלליים להיפתח בכלי הטיפול בקישורים שמוגדר כברירת מחדל במערכת ההפעלה, כולל כלי הטיפול בקישורים אוניברסליים או אפליקציית דפדפן ברירת המחדל. גם הספרייה SFSafariViewController היא אפשרות נתמכת.
org_internal
מזהה הלקוח של OAuth בבקשה הוא חלק מפרויקט שמגביל את הגישה לחשבונות Google ב ארגון ספציפי ב-Google Cloud. למידע נוסף על אפשרות ההגדרה הזו, עיינו בקטע סוג משתמש במאמר העזרה בנושא הגדרת מסך ההסכמה ל-OAuth.
invalid_client
סוד הלקוח של OAuth שגוי. בודקים את ההגדרות של לקוח OAuth, כולל מזהה הלקוח והסוד שמשמשים לבקשה הזו.
deleted_client
לקוח OAuth שמשמש לשליחת הבקשה נמחק. המחיקה יכולה להתבצע באופן ידני או אוטומטי במקרה של לקוחות שלא בשימוש . אפשר לשחזר לקוחות שנמחקו תוך 30 יום ממועד המחיקה. מידע נוסף .
invalid_grant
כשמרעננים טוקן גישה או משתמשים בהרשאה מצטברת, יכול להיות שהתוקף של הטוקן פג או שהוא בוטל. מאמתים שוב את המשתמש ומבקשים ממנו הסכמה מהמשתמש לקבלת אסימונים חדשים. אם השגיאה הזו ממשיכה להופיע, צריך לוודא שהאפליקציה הוגדרה בצורה נכונה ושהבקשה כוללת את האסימונים והפרמטרים הנכונים. אם לא, יכול להיות שחשבון המשתמש נמחק או הושבת.
redirect_uri_mismatch
הערך של redirect_uri שהועבר בבקשת ההרשאה לא תואם לכתובת URI מורשית להפניה אוטומטית של מזהה הלקוח ב-OAuth. בודקים את כתובות ה-URI המורשות להפניה אוטומטית בדף Clients במסוף Google Cloud.
יכול להיות שהפרמטר redirect_uri מתייחס לתהליך OAuth מחוץ לתחום (OOB) שהוצא משימוש ולא נתמך יותר. כדי לעדכן את השילוב, צריך לפעול לפי המדריך להעברת נתונים (מיגרציה).
invalid_request
הייתה בעיה בבקשה ששלחת. יכולות להיות לכך כמה סיבות:
- הפורמט של הבקשה לא היה תקין
- בבקשה חסרים פרמטרים נדרשים
- הבקשה משתמשת בשיטת הרשאה ש-Google לא תומכת בה. מוודאים ששילוב ה-OAuth משתמש בשיטת שילוב מומלצת
שלב 4: טיפול בתגובה של שרת OAuth 2.0
שרת OAuth 2.0 מגיב לבקשת הגישה של האפליקציה באמצעות כתובת ה-URL שצוינה בבקשה.
אם המשתמש מאשר את בקשת הגישה, התשובה מכילה קוד הרשאה. אם המשתמש לא מאשר את הבקשה, התגובה מכילה הודעת שגיאה. קוד ההרשאה או הודעת השגיאה שמוחזרים לשרת האינטרנט מופיעים במחרוזת השאילתה, כמו בדוגמאות הבאות:
תגובת שגיאה:
https://oauth2.example.com/auth?error=access_denied
תגובה עם קוד הרשאה:
https://oauth2.example.com/auth?code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7
דוגמה לתגובה של שרת 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& access_type=offline& include_granted_scopes=true& response_type=code& state=state_parameter_passthrough_value& redirect_uri=https%3A//developers.google.com/oauthplayground& client_id=client_id
אחרי שתסיימו את תהליך ההרשאה באמצעות OAuth 2.0, הדפדפן יפנה אתכם אל OAuth 2.0 Playground, כלי לבדיקת תהליכי OAuth. תראו ש-OAuth 2.0 Playground תיעד באופן אוטומטי את קוד ההרשאה.
שלב 5: החלפת קוד ההרשאה באסימוני רענון וגישה
אחרי ששרת האינטרנט מקבל את קוד ההרשאה, הוא יכול להחליף אותו בטוקן גישה.
PHP
כדי להחליף קוד הרשאה באסימון גישה, משתמשים ב-method fetchAccessTokenWithAuthCode:
$access_token = $client->fetchAccessTokenWithAuthCode($_GET['code']);Python
בדף הקריאה החוזרת (callback), משתמשים בספרייה google-auth כדי לאמת את התגובה של שרת ההרשאות. לאחר מכן, משתמשים ב-method flow.fetch_token כדי להחליף את קוד ההרשאה בתגובה הזו באסימון גישה:
state = flask.session['state'] flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( 'client_secret.json', scopes=['https://www.googleapis.com/auth/drive.metadata.readonly'], state=state) flow.redirect_uri = flask.url_for('oauth2callback', _external=True) authorization_response = flask.request.url flow.fetch_token(authorization_response=authorization_response) # Store the credentials in browser session storage, but for security: client_id, client_secret, # and token_uri are instead stored only on the backend server. credentials = flow.credentials flask.session['credentials'] = { 'token': credentials.token, 'refresh_token': credentials.refresh_token, 'granted_scopes': credentials.granted_scopes}
Ruby
בדף הקריאה החוזרת (callback), משתמשים בספרייה googleauth כדי לאמת את התגובה של שרת ההרשאות. משתמשים ב-method authorizer.handle_auth_callback_deferred כדי לשמור את קוד ההרשאה ולהפנות חזרה לכתובת ה-URL שממנה נשלחה בקשת ההרשאה במקור. הפעולה הזו דוחה את החלפת הקוד על ידי שמירה זמנית של התוצאות בסשן של המשתמש.
target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request) redirect target_url
Node.js
כדי להחליף קוד הרשאה בטוקן גישה, משתמשים ב-method getToken:
const url = require('url'); // Receive the callback from Google's OAuth 2.0 server. app.get('/oauth2callback', async (req, res) => { let q = url.parse(req.url, true).query; if (q.error) { // An error response e.g. error=access_denied console.log('Error:' + q.error); } else if (q.state !== req.session.state) { //check state value console.log('State mismatch. Possible CSRF attack'); res.end('State mismatch. Possible CSRF attack'); } else { // Get access and refresh tokens (if access_type is offline) let { tokens } = await oauth2Client.getToken(q.code); oauth2Client.setCredentials(tokens); });
HTTP/REST
כדי להחליף קוד הרשאה בטוקן גישה, צריך לשלוח קריאה לנקודת הקצה (endpoint) https://oauth2.googleapis.com/token ולהגדיר את הפרמטרים הבאים:
| שדות | |
|---|---|
client_id |
מזהה הלקוח שהתקבל מדף הלקוחות במסוף Cloud. |
client_secret |
אופציונלי
סוד הלקוח שהתקבל מדף הלקוחות במסוף Cloud. |
code |
קוד ההרשאה שהוחזר מהבקשה הראשונית. |
grant_type |
כפי שמוגדר במפרט של OAuth 2.0, הערך של השדה הזה חייב להיות authorization_code. |
redirect_uri |
אחד ממזהי ה-URI להפניה אוטומטית שרשומים לפרויקט שלכם בדף Clients במסוף Cloud, עבור ה-client_id הנתון. |
השימוש ב-DPoP הוא אופציונלי, אבל מומלץ להשתמש בו כדי לשפר את האבטחה. האבטחה של DPoP מסתמכת על כך שהמפתח הפרטי מוגבל למכשיר אחד. מומלץ לאחסן אותו באופן שלא מאפשר להעתיק אותו מהמכשיר, למשל באמצעות TPM, Secure Enclave או מאגרי מפתחות אחרים שמגובים בחומרה. כדי להשתמש ב-DPoP, האפליקציה צריכה ליצור JWT חדש וייחודי להוכחת DPoP לכל בקשה לנקודת הקצה של האסימון, ולהוסיף אותו ככותרת של בקשת HTTP.
| כותרת | חובה | תיאור |
|---|---|---|
DPoP |
אופציונלי | הוכחת DPoP היא JWT שמוכיח את הבעלות על מפתח פרטי. זו כותרת, לא פרמטר. אם הוא מסופק, האסימונים שמוחזרים קשורים למפתח הזה. צריך ליצור הוכחה חדשה וייחודית לכל בקשה, והיא צריכה לכלול טענות נכונות (claims) של htm (שיטת HTTP) ו-htu (מזהה URI של HTTP) שתואמות לבקשה. |
בקטע הקוד הבא יש בקשה לדוגמה:
POST /token HTTP/1.1 Host: oauth2.googleapis.com Content-Type: application/x-www-form-urlencoded DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik\ VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR\ nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE\ QSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIj\ oiUE9TVCIsImh0dSI6Imh0dHBzOi8vc2VydmVyLmV4YW1wbGUuY29tL3Rva2VuIiwia\ WF0IjoxNTYyMjYyNjE2fQ.2-GxA6T8lP4vfrg8v-FdWP0A0zdrj8igiMLvqRMUvwnQg\ 4PtFLbdLXiOSsX0x7NVY-FNyJK70nfbV37xRZT3Lg code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7& client_id=your_client_id& redirect_uri=https%3A//developers.google.com/oauthplayground& grant_type=authorization_code
יצירת הוכחת DPoP
השלבים הבאים מראים איך ליצור הוכחת DPoP באמצעות OpenSSL משורת הפקודה:
- יצירת זוג מפתחות EC P-256:
openssl ecparam -name prime256v1 -genkey -noout -out dpop_private.pem openssl ec -in dpop_private.pem -pubout -out dpop_public.pem
- יוצרים את הכותרת של ה-DPoP:
הכותרת חייבת לכלול את ההצהרות
typ,algו-jwk(מפתח ציבורי). הערכיםxו-yהם הקואורדינטות של המפתח הציבורי של ה-EC בקידוד Base64Url. מקודדים את ה-JSON הזה ב-Base64Url:{ "typ":"dpop+jwt", "alg":"ES256", "jwk": { "kty":"EC", "x":"YOUR_PUBLIC_KEY_X", "y":"YOUR_PUBLIC_KEY_Y", "crv":"P-256" } } - יוצרים את המטען הייעודי (payload) של ה-DPoP:
המטען הייעודי (payload) חייב לכלול את
jti(מזהה ייחודי של הבקשה),htm(שיטת HTTP, למשלPOST),htu(HTTP URI, למשלhttps://oauth2.googleapis.com/token) ו-iat(הזמן שבו הונפק). אם קיבלתם צופן חד-פעמי (nonce) מהשרת בכותרתDPoP-Nonceבתגובה לבקשה קודמת, אתם צריכים לכלול את ערך הצופן החד-פעמי (nonce) הזה בהצהרה שלnonce. הטענה nonce היא אופציונלית להחלפות של קודי הרשאה, ומשתמשים בה רק אם הכותרת DPoP-Nonce התקבלה קודם. מקודדים את ה-JSON הזה ב-Base64Url:{ "jti":"JTI_VALUE", "htm":"POST", "htu":"https://oauth2.googleapis.com/token", "iat":YOUR_JWT_ISSUED_TIME, "nonce":"SERVER_PROVIDED_NONCE" }הערך של
jtiתלוי בסוג ההחלפה:- בהחלפות של קודי הרשאה, הערך של
jtiצריך להיות גיבוב SHA256 בקידוד Base64Url של קוד ההרשאה:"jti":"BASE64URL(SHA256(AUTHORIZATION_CODE))". - בהחלפות של טוקן רענון, הערך של
jtiחייב להיות מזהה ייחודי לכל בקשה:"jti":"YOUR_UNIQUE_PER_REQUEST_IDENTIFIER".
- בהחלפות של קודי הרשאה, הערך של
- חותמים על ההוכחה:
משרשרים את הכותרת המקודדת ואת המטען הייעודי (payload) עם נקודה (
.), ואז חותמים על התוצאה באמצעות המפתח הפרטי באמצעות ES256. שימו לב שב-JWS החתימה צריכה להיות בפורמט משורשר גולמי שלR | S(64 בייטים ל-P-256). אם משתמשים ב-OpenSSL ישירות, צריך להמיר את החתימה שקודדה ב-ASN.1 DER כברירת מחדל לפורמט הגולמי הזה.
החלפה מוצלחת מסומנת בתגובה 200 OK שמכילה את האסימונים. אם במהלך ההחלפה נעשה שימוש בהוכחת DPoP תקינה, טוקן הרענון ש-Google תחזיר יהיה קשור ל-DPoP של המפתח שלכם, אבל טוקני הגישה לא יהיו קשורים ל-DPoP. אסימוני הגישה ישמרו על token_type של Bearer במקום DPoP.
בנוסף, Google מחזירה כותרת HTTP מסוג DPoP-Nonce בתגובה.
הלקוח צריך לשמור את ה-nonce הזה במטמון ולכלול אותו בטענת הנכוֹנוּת (claim) של nonce בהוכחת ה-DPoP בבקשות הבאות (למשל, כשמחליפים טוקן רענון בטוקן גישה חדש, או כששולחים קריאה לממשקי API שמוגנים באמצעות DPoP). שימוש ב-nonce שהונפק מוקדם מאפשר להימנע משגיאה נוספת של הלוך ושוב (use_dpop_nonce) בבקשה הבאה.
צריך לכלול הוכחות DPoP בבקשות להחלפת טוקנים שבוצעו באמצעות טוקני רענון שקשורים ל-DPoP.
החלפה נכשלת אם הכותרת DPoP חסרה כשהיא צפויה, לא חוקית או אם ההוכחה משתמשת במפתח שונה מזה שקשור לאסימון. במקרים כאלה, השרת מחזיר שגיאת 400 Bad Request.
אם בהוכחת ה-DPoP יש טענות נכוֹנוּת (claims) לא תואמות של htm או htu, iat שפג תוקפו, jti בשימוש חוזר או חתימה לא חוקית, Google מחזירה קוד שגיאה invalid_dpop_proof.
אם נדרש מספר חד-פעמי (nonce) של DPoP, למשל במהלך החלפה של טוקן רענון, ובהוכחת ה-DPoP חסרה הצהרה מסוג nonce, או שערך המספר החד-פעמי לא קביל בשרת (למשל, פג תוקפו, כבר נעשה בו שימוש או שהוא שגוי), Google מחזירה קוד שגיאה use_dpop_nonce יחד עם כותרת HTTP מסוג DPoP-Nonce שמכילה מספר חד-פעמי חדש שאפשר להשתמש בו בבקשה הבאה.
במקרים אחרים של כשל, יכול להיות שיוחזר הערך invalid_grant.
Google מגיבה לבקשה הזו ומחזירה אובייקט JSON שמכיל טוקן גישה לטווח קצר וטוקן רענון.
שימו לב שטוקן הרענון מוחזר רק אם האפליקציה הגדירה את הפרמטר access_type לערך offline בבקשה הראשונית לשרת ההרשאות של Google.
התשובה מכילה את השדות הבאים:
| שדות | |
|---|---|
access_token |
האסימון שהאפליקציה שולחת כדי לאשר בקשת Google API. |
expires_in |
משך החיים שנותר לאסימון הגישה, בשניות. |
refresh_token |
טוקן שאפשר להשתמש בו כדי לקבל טוקן גישה חדש. טוקני הרענון תקפים עד שהמשתמש מבטל את הגישה או עד שתוקף הטוקן פג. אם נעשה שימוש ב-DPoP, טוקן הרענון מקושר למפתח הפרטי ששימש לחתימה על הוכחת ה-DPoP.
שוב, השדה הזה מופיע בתגובה הזו רק אם הגדרתם את הפרמטר access_type לערך offline בבקשה הראשונית לשרת ההרשאות של Google.
|
refresh_token_expires_in |
משך הזמן שנותר לטוקן הרענון, בשניות. הערך הזה מוגדר רק כשהמשתמש מעניק גישה מבוססת-זמן. |
scope |
היקפי הגישה שניתנו על ידי access_token, מבוטאים כרשימה של מחרוזות תלויות אותיות רישיות (case-sensitive) שמופרדות ברווחים. |
token_type |
סוג הטוקן שהוחזר. הערך הזה הוא תמיד Bearer, גם כשמשתמשים ב-DPoP. |
קטע הקוד הבא מציג דוגמה לכותרות ולגוף של תגובה תקינה כשמשתמשים ב-DPoP:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
DPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI
{
"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"
}שגיאות
כשמחליפים את קוד ההרשאה בטוקן גישה, יכול להיות שתופיע השגיאה הבאה במקום התגובה הצפויה. בקטע הזה מפורטים קודי שגיאה נפוצים והצעות לפתרונות.
invalid_grant
קוד האישור שסופק לא תקין או שהפורמט שלו שגוי. כדי לבקש קוד חדש, מפעילים מחדש את תהליך ה-OAuth כדי לבקש שוב את הסכמת המשתמש.
שלב 6: בודקים אילו היקפי הרשאות המשתמשים נתנו
כשמבקשים כמה הרשאות (היקפים), יכול להיות שהמשתמשים לא יתנו לאפליקציה גישה לכולן. האפליקציה צריכה לאמת אילו היקפי הרשאות ניתנו בפועל, ולטפל בצורה חלקה במצבים שבהם חלק מההרשאות נדחו, בדרך כלל על ידי השבתת התכונות שמסתמכות על היקפי ההרשאות שנדחו.
אבל יש מקרים חריגים. אפליקציות של Google Workspace Enterprise עם הענקת גישה ברמת הדומיין, או אפליקציות שמסומנות כמהימנות, עוקפות את מסך ההסכמה להרשאות פרטניות. באפליקציות האלה, המשתמשים לא יראו את מסך ההסכמה להרשאות פרטניות. במקום זאת, האפליקציה תקבל את כל ההיקפים המבוקשים או לא תקבל אף אחד מהם.
מידע נוסף זמין במאמר איך מטפלים בהרשאות פרטניות.
PHP
כדי לבדוק אילו היקפי הרשאות המשתמש אישר, משתמשים ב-method getGrantedScope():
// Space-separated string of granted scopes if it exists, otherwise null. $granted_scopes = $client->getOAuth2Service()->getGrantedScope(); // Determine which scopes user granted and build a dictionary $granted_scopes_dict = [ 'Drive' => str_contains($granted_scopes, Google\Service\Drive::DRIVE_METADATA_READONLY), 'Calendar' => str_contains($granted_scopes, Google\Service\Calendar::CALENDAR_READONLY) ];
Python
האובייקט credentials שמוחזר כולל מאפיין granted_scopes, שהוא רשימה של היקפי ההרשאות שהמשתמש העניק לאפליקציה.
credentials = flow.credentials flask.session['credentials'] = { 'token': credentials.token, 'refresh_token': credentials.refresh_token, 'granted_scopes': credentials.granted_scopes}
הפונקציה הבאה בודקת אילו היקפי הרשאות המשתמש נתן לאפליקציה.
def check_granted_scopes(credentials): features = {} if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['granted_scopes']: features['drive'] = True else: features['drive'] = False if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['granted_scopes']: features['calendar'] = True else: features['calendar'] = False return features
Ruby
כשמבקשים כמה היקפי הרשאות בבת אחת, צריך לבדוק אילו היקפי הרשאות ניתנו באמצעות המאפיין scope של האובייקט credentials.
# User authorized the request. Now, check which scopes were granted. if credentials.scope.include?(Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY) # User authorized read-only Drive activity permission. # Calling the APIs, etc else # User didn't authorize read-only Drive activity permission. # Update UX and application accordingly end # Check if user authorized Calendar read permission. if credentials.scope.include?(Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY) # User authorized Calendar read permission. # Calling the APIs, etc. else # User didn't authorize Calendar read permission. # Update UX and application accordingly end
Node.js
כשמבקשים כמה היקפי הרשאות בבת אחת, צריך לבדוק אילו היקפי הרשאות ניתנו באמצעות המאפיין scope של האובייקט tokens.
// User authorized the request. Now, check which scopes were granted. if (tokens.scope.includes('https://www.googleapis.com/auth/drive.metadata.readonly')) { // User authorized read-only Drive activity permission. // Calling the APIs, etc. } else { // User didn't authorize read-only Drive activity permission. // Update UX and application accordingly } // Check if user authorized Calendar read permission. if (tokens.scope.includes('https://www.googleapis.com/auth/calendar.readonly')) { // User authorized Calendar read permission. // Calling the APIs, etc. } else { // User didn't authorize Calendar read permission. // Update UX and application accordingly }
HTTP/REST
כדי לבדוק אם המשתמש נתן לאפליקציה גישה להיקף מסוים, בודקים את השדה scope בתגובה של אסימון הגישה. היקפי הגישה שניתנו על ידי טוקן הגישה, מבוטאים כרשימה של מחרוזות תלויות אותיות רישיות, המופרדות ברווחים.
לדוגמה, התגובה הבאה לדוגמה של טוקן גישה מציינת שהמשתמש העניק לאפליקציה שלכם גישה להרשאות לקריאה בלבד של פעילות ב-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 API
PHP
כדי להשתמש בטוקן הגישה להפעלת ממשקי Google API, צריך לבצע את השלבים הבאים:
- אם אתם צריכים להחיל טוקן גישה על אובייקט
Google\Clientחדש – לדוגמה, אם שמרתם את טוקן הגישה בסשן של משתמש – השתמשו ב-methodsetAccessToken:$client->setAccessToken($access_token); - יוצרים אובייקט שירות ל-API שרוצים לקרוא לו. כדי ליצור אובייקט שירות, צריך לספק אובייקט
Google\Clientמורשה ל-constructor של ה-API שרוצים להפעיל. לדוגמה, כדי לשלוח קריאה ל-Drive API:$drive = new Google\Service\Drive($client); - שולחים בקשות לשירות ה-API באמצעות
הממשק שסופק על ידי אובייקט השירות.
לדוגמה, כדי להציג את רשימת הקבצים ב-Google Drive של המשתמש המאומת:
$files = $drive->files->listFiles(array());
Python
אחרי שמקבלים טוקן גישה, האפליקציה יכולה להשתמש בו כדי לאשר בקשות API בשם חשבון משתמש או חשבון שירות מסוים. משתמשים בפרטי הכניסה הספציפיים למשתמש כדי ליצור אובייקט שירות ל-API שרוצים לקרוא לו, ואז משתמשים באובייקט הזה כדי לשלוח בקשות API מורשות.
- יוצרים אובייקט שירות ל-API שרוצים לקרוא לו. כדי ליצור אובייקט שירות, צריך להפעיל את ה-method
buildשל הספרייהgoogleapiclient.discoveryעם השם והגרסה של ה-API ופרטי הכניסה של המשתמש: לדוגמה, כדי להפעיל את גרסה 3 של Drive API:from googleapiclient.discovery import build drive = build('drive', 'v2', credentials=credentials)
- שולחים בקשות לשירות ה-API באמצעות הממשק שסופק על ידי אובייקט השירות.
לדוגמה, כדי להציג את רשימת הקבצים ב-Google Drive של המשתמש המאומת:
files = drive.files().list().execute()
Ruby
אחרי שמקבלים טוקן גישה, האפליקציה יכולה להשתמש בו כדי לשלוח בקשות API בשם חשבון משתמש או חשבון שירות מסוים. משתמשים בפרטי הכניסה הספציפיים למשתמש כדי ליצור אובייקט שירות ל-API שרוצים לקרוא לו, ואז משתמשים באובייקט הזה כדי לשלוח בקשות API מורשות.
- יוצרים אובייקט שירות ל-API שרוצים לקרוא לו.
לדוגמה, כדי לשלוח קריאה לגרסה 3 של Drive API:
drive = Google::Apis::DriveV3::DriveService.new
- מגדירים את פרטי הכניסה בשירות:
drive.authorization = credentials
- שולחים בקשות לשירות ה-API באמצעות הממשק שסופק על ידי אובייקט השירות.
לדוגמה, כדי להציג את רשימת הקבצים ב-Google Drive של המשתמש המאומת:
files = drive.list_files
לחלופין, אפשר לספק הרשאה לכל שיטה בנפרד על ידי הוספת הפרמטר options לשיטה:
files = drive.list_files(options: { authorization: credentials })
Node.js
אחרי שמקבלים אסימון גישה ומגדירים אותו לאובייקט OAuth2, משתמשים באובייקט כדי לשלוח קריאות ל-Google APIs. האפליקציה יכולה להשתמש בטוקן הזה כדי לאשר בקשות API בשם חשבון משתמש או חשבון שירות מסוים. יוצרים אובייקט שירות ל-API שרוצים לקרוא לו.
לדוגמה, הקוד הבא משתמש ב-Google Drive API כדי להציג רשימה של שמות קבצים ב-Drive של המשתמש.
const { google } = require('googleapis'); // Example of using Google Drive API to list filenames in user's Drive. const drive = google.drive('v3'); drive.files.list({ auth: oauth2Client, pageSize: 10, fields: 'nextPageToken, files(id, name)', }, (err1, res1) => { if (err1) return console.log('The API returned an error: ' + err1); const files = res1.data.files; if (files.length) { console.log('Files:'); files.map((file) => { console.log(`${file.name} (${file.id})`); }); } else { console.log('No files found.'); } });
HTTP/REST
אחרי שהאפליקציה מקבלת אסימון גישה, אתם יכולים להשתמש בו כדי לבצע קריאות ל-Google API מטעם חשבון משתמש מסוים, אם הוענקו היקפי הגישה הנדרשים ל-API. כדי לעשות את זה, צריך לכלול את טוקן הגישה בבקשה ל-API באמצעות הוספה של פרמטר השאילתה access_token או של ערך Bearer בכותרת ה-HTTP Authorization. כשאפשר, עדיף להשתמש בכותרת HTTP כי מחרוזות של שאילתות נוטות להופיע ביומני השרת. ברוב המקרים אפשר להשתמש בספריית לקוח כדי להגדיר את הקריאות ל-Google APIs (לדוגמה, כשקוראים ל-Drive Files API).
אתם יכולים לנסות את כל ממשקי ה-API של Google ולראות את היקפי ההרשאות שלהם ב-OAuth 2.0 Playground.
דוגמאות ל-HTTP GET
קריאה לנקודת הקצה (endpoint)
drive.files (Drive Files API) באמצעות כותרת ה-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
דוגמה פתורה
בדוגמה הבאה מודפסת רשימה בפורמט JSON של קבצים ב-Google Drive של משתמש, אחרי שהמשתמש מאמת את זהותו ונותן לאפליקציה הרשאה לגשת למטא-נתונים שלו ב-Drive.
PHP
כדי להפעיל את הדוגמה הזו:
- בקונסולה לממשקי API, מוסיפים את כתובת ה-URL של המכונה המקומית לרשימת כתובות ה-URL להפניה אוטומטית. לדוגמה, מוסיפים את
http://localhost:8080. - יוצרים ספרייה חדשה ועוברים אליה. לדוגמה:
mkdir ~/php-oauth2-example cd ~/php-oauth2-example
- מתקינים את ספריית הלקוח של Google API ל-PHP באמצעות Composer:
composer require google/apiclient:^2.15.0
- יוצרים את הקבצים
index.phpו-oauth2callback.phpעם התוכן הבא: - מריצים את הדוגמה באמצעות שרת האינטרנט המובנה של PHP לבדיקה:
php -S localhost:8080 ~/php-oauth2-example
index.php
<?php require_once __DIR__.'/vendor/autoload.php'; session_start(); $client = new Google\Client(); $client->setAuthConfig('client_secret.json'); // User granted permission as an access token is in the session. if (isset($_SESSION['access_token']) && $_SESSION['access_token']) { $client->setAccessToken($_SESSION['access_token']); // Check if user granted Drive permission if ($_SESSION['granted_scopes_dict']['Drive']) { echo "Drive feature is enabled."; echo "</br>"; $drive = new Drive($client); $files = array(); $response = $drive->files->listFiles(array()); foreach ($response->files as $file) { echo "File: " . $file->name . " (" . $file->id . ")"; echo "</br>"; } } else { echo "Drive feature is NOT enabled."; echo "</br>"; } // Check if user granted Calendar permission if ($_SESSION['granted_scopes_dict']['Calendar']) { echo "Calendar feature is enabled."; echo "</br>"; } else { echo "Calendar feature is NOT enabled."; echo "</br>"; } } else { // Redirect users to outh2call.php which redirects users to Google OAuth 2.0 $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php'; header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL)); } ?>
oauth2callback.php
<?php require_once __DIR__.'/vendor/autoload.php'; session_start(); $client = new Google\Client(); // Required, call the setAuthConfig function to load authorization credentials from // client_secret.json file. $client->setAuthConfigFile('client_secret.json'); $client->setRedirectUri('http://' . $_SERVER['HTTP_HOST']. $_SERVER['PHP_SELF']); // Required, to set the scope value, call the addScope function. $client->addScope([Google\Service\Drive::DRIVE_METADATA_READONLY, Google\Service\Calendar::CALENDAR_READONLY]); // Enable incremental authorization. Recommended as a best practice. $client->setIncludeGrantedScopes(true); // Recommended, offline access will give you both an access and refresh token so that // your app can refresh the access token without user interaction. $client->setAccessType("offline"); // Generate a URL for authorization as it doesn't contain code and error if (!isset($_GET['code']) && !isset($_GET['error'])) { // Generate and set state value $state = bin2hex(random_bytes(16)); $client->setState($state); $_SESSION['state'] = $state; // Generate a url that asks permissions. $auth_url = $client->createAuthUrl(); header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL)); } // User authorized the request and authorization code is returned to exchange access and // refresh tokens. if (isset($_GET['code'])) { // Check the state value if (!isset($_GET['state']) || $_GET['state'] !== $_SESSION['state']) { die('State mismatch. Possible CSRF attack.'); } // Get access and refresh tokens (if access_type is offline) $token = $client->fetchAccessTokenWithAuthCode($_GET['code']); /** Save access and refresh token to the session variables. * ACTION ITEM: In a production app, you likely want to save the * refresh token in a secure persistent storage instead. */ $_SESSION['access_token'] = $token; $_SESSION['refresh_token'] = $client->getRefreshToken(); // Space-separated string of granted scopes if it exists, otherwise null. $granted_scopes = $client->getOAuth2Service()->getGrantedScope(); // Determine which scopes user granted and build a dictionary $granted_scopes_dict = [ 'Drive' => str_contains($granted_scopes, Google\Service\Drive::DRIVE_METADATA_READONLY), 'Calendar' => str_contains($granted_scopes, Google\Service\Calendar::CALENDAR_READONLY) ]; $_SESSION['granted_scopes_dict'] = $granted_scopes_dict; $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/'; header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL)); } // An error response e.g. error=access_denied if (isset($_GET['error'])) { echo "Error: ". $_GET['error']; } ?>
Python
בדוגמה הזו נעשה שימוש ב-framework של Flask. הוא מריץ אפליקציית אינטרנט בכתובת http://localhost:8080 שמאפשרת לבדוק את תהליך OAuth 2.0. אם תעברו לכתובת ה-URL הזו, תראו חמישה קישורים:
- Call Drive API: הקישור הזה מפנה לדף שמנסה להריץ בקשת API לדוגמה אם המשתמשים נתנו את ההרשאה. אם צריך, הוא מתחיל את תהליך ההרשאה. אם הפעולה בוצעה ללא שגיאות, התגובה מה-API תוצג בדף.
- דף מדומה לקריאה ל-Calendar API: הקישור הזה מפנה לדף מדומה שמנסה לבצע בקשת API לדוגמה ל-Calendar API אם המשתמשים נתנו את ההרשאה. אם צריך, הוא מתחיל את תהליך ההרשאה. אם הפעולה בוצעה ללא שגיאות, התגובה מה-API תוצג בדף.
- בדיקה ישירה של תהליך האימות: הקישור הזה מפנה לדף שמנסה להעביר את המשתמש בתהליך ההרשאה. האפליקציה מבקשת הרשאה לשלוח בקשות API מורשות בשם המשתמש.
- ביטול פרטי הכניסה הנוכחיים: הקישור הזה מפנה לדף ש מבטל את ההרשאות שהמשתמש כבר נתן לאפליקציה.
- Clear Flask session credentials: הקישור הזה מוחק את פרטי הכניסה לאימות ששמורים בסשן Flask. כך תוכלו לראות מה יקרה אם משתמש שכבר נתן הרשאה לאפליקציה שלכם ינסה לבצע בקשת API בסשן חדש. היא גם מאפשרת לכם לראות את התגובה מה-API שהאפליקציה תקבל אם משתמש ביטל את ההרשאות שניתנו לאפליקציה, והאפליקציה עדיין ניסתה לאשר בקשה באמצעות טוקן גישה שבוטל.
# -*- coding: utf-8 -*- import os import flask import json import requests import google.oauth2.credentials import google_auth_oauthlib.flow import googleapiclient.discovery # This variable specifies the name of a file that contains the OAuth 2.0 # information for this application, including its client_id and client_secret. CLIENT_SECRETS_FILE = "client_secret.json" # The OAuth 2.0 access scope allows for access to the # authenticated user's account and requires requests to use an SSL connection. SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly', 'https://www.googleapis.com/auth/calendar.readonly'] API_SERVICE_NAME = 'drive' API_VERSION = 'v2' app = flask.Flask(__name__) # Note: A secret key is included in the sample so that it works. # If you use this code in your application, replace this with a truly secret # key. See https://flask.palletsprojects.com/quickstart/#sessions. app.secret_key = 'REPLACE ME - this value is here as a placeholder.' @app.route('/') def index(): return print_index_table() @app.route('/drive') def drive_api_request(): if 'credentials' not in flask.session: return flask.redirect('authorize') features = flask.session['features'] if features['drive']: # Load client secrets from the server-side file. with open(CLIENT_SECRETS_FILE, 'r') as f: client_config = json.load(f)['web'] # Load user-specific credentials from browser session storage. session_credentials = flask.session['credentials'] # Reconstruct the credentials object. credentials = google.oauth2.credentials.Credentials( refresh_token=session_credentials.get('refresh_token'), scopes=session_credentials.get('granted_scopes'), token=session_credentials.get('token'), client_id=client_config.get('client_id'), client_secret=client_config.get('client_secret'), token_uri=client_config.get('token_uri')) drive = googleapiclient.discovery.build( API_SERVICE_NAME, API_VERSION, credentials=credentials) files = drive.files().list().execute() # Save credentials back to session in case access token was refreshed. flask.session['credentials'] = credentials_to_dict(credentials) return flask.jsonify(**files) else: # User didn't authorize read-only Drive activity permission. return '<p>Drive feature is not enabled.</p>' @app.route('/calendar') def calendar_api_request(): if 'credentials' not in flask.session: return flask.redirect('authorize') features = flask.session['features'] if features['calendar']: # User authorized Calendar read permission. # Calling the APIs, etc. return ('<p>User granted the Google Calendar read permission. '+ 'This sample code does not include code to call Calendar</p>') else: # User didn't authorize Calendar read permission. # Update UX and application accordingly return '<p>Calendar feature is not enabled.</p>' @app.route('/authorize') def authorize(): # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps. flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( CLIENT_SECRETS_FILE, scopes=SCOPES) # The URI created here must exactly match one of the authorized redirect URIs # for the OAuth 2.0 client, which you configured in the API Console. If this # value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch' # error. flow.redirect_uri = flask.url_for('oauth2callback', _external=True) authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Enable incremental authorization. Recommended as a best practice. include_granted_scopes='true') # Store the state so the callback can verify the auth server response. flask.session['state'] = state return flask.redirect(authorization_url) @app.route('/oauth2callback') def oauth2callback(): # Specify the state when creating the flow in the callback so that it can # verified in the authorization server response. state = flask.session['state'] flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( CLIENT_SECRETS_FILE, scopes=SCOPES, state=state) flow.redirect_uri = flask.url_for('oauth2callback', _external=True) # Use the authorization server's response to fetch the OAuth 2.0 tokens. authorization_response = flask.request.url flow.fetch_token(authorization_response=authorization_response) # Store credentials in the session. # ACTION ITEM: In a production app, you likely want to save these # credentials in a persistent database instead. credentials = flow.credentials credentials = credentials_to_dict(credentials) flask.session['credentials'] = credentials # Check which scopes user granted features = check_granted_scopes(credentials) flask.session['features'] = features return flask.redirect('/') @app.route('/revoke') def revoke(): if 'credentials' not in flask.session: return ('You need to <a href="/authorize">authorize</a> before ' + 'testing the code to revoke credentials.') # Load client secrets from the server-side file. with open(CLIENT_SECRETS_FILE, 'r') as f: client_config = json.load(f)['web'] # Load user-specific credentials from the session. session_credentials = flask.session['credentials'] # Reconstruct the credentials object. credentials = google.oauth2.credentials.Credentials( refresh_token=session_credentials.get('refresh_token'), scopes=session_credentials.get('granted_scopes'), token=session_credentials.get('token'), client_id=client_config.get('client_id'), client_secret=client_config.get('client_secret'), token_uri=client_config.get('token_uri')) revoke = requests.post('https://oauth2.googleapis.com/revoke', params={'token': credentials.token}, headers = {'content-type': 'application/x-www-form-urlencoded'}) status_code = getattr(revoke, 'status_code') if status_code == 200: # Clear the user's session credentials after successful revocation if 'credentials' in flask.session: del flask.session['credentials'] del flask.session['features'] return('Credentials successfully revoked.' + print_index_table()) else: return('An error occurred.' + print_index_table()) @app.route('/clear') def clear_credentials(): if 'credentials' in flask.session: del flask.session['credentials'] return ('Credentials have been cleared.<br><br>' + print_index_table()) def credentials_to_dict(credentials): return {'token': credentials.token, 'refresh_token': credentials.refresh_token, 'granted_scopes': credentials.granted_scopes} def check_granted_scopes(credentials): features = {} if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['granted_scopes']: features['drive'] = True else: features['drive'] = False if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['granted_scopes']: features['calendar'] = True else: features['calendar'] = False return features def print_index_table(): return ('<table>' + '<tr><td><a href="/authorize">Test the auth flow directly</a></td>' + '<td>Go directly to the authorization flow. If there are stored ' + ' credentials, you still might not be prompted to reauthorize ' + ' the application.</td></tr>' + '<tr><td><a href="/drive">Call Drive API directly</a></td>' + '<td> Use stored credentials to call the API, you still might not be prompted to reauthorize ' + ' the application.</td></tr>' + '<tr><td><a href="/calendar">Call Calendar API directly</a></td>' + '<td> Use stored credentials to call the API, you still might not be prompted to reauthorize ' + ' the application.</td></tr>' + '<tr><td><a href="/revoke">Revoke current credentials</a></td>' + '<td>Revoke the access token associated with the current user ' + ' session. After revoking credentials, if you go to the test ' + ' page, you should see an <code>invalid_grant</code> error.' + '</td></tr>' + '<tr><td><a href="/clear">Clear Flask session credentials</a></td>' + '<td>Clear the access token currently stored in the user session. ' + ' After clearing the token, if you <a href="/authorize">authorize</a> ' + ' again, you should go back to the auth flow.' + '</td></tr></table>') if __name__ == '__main__': # When running locally, disable OAuthlib's HTTPs verification. # ACTION ITEM for developers: # When running in production *do not* leave this option enabled. os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' # This disables the requested scopes and granted scopes check. # If users only grant partial request, the warning would not be thrown. os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = '1' # Specify a hostname and port that are set as a valid redirect URI # for your API project in the Google API Console. app.run('localhost', 8080, debug=True)
Ruby
בדוגמה הזו נעשה שימוש במסגרת Sinatra.
require 'googleauth' require 'googleauth/web_user_authorizer' require 'googleauth/stores/redis_token_store' require 'google/apis/drive_v3' require 'google/apis/calendar_v3' require 'sinatra' configure do enable :sessions # Required, call the from_file method to retrieve the client ID from a # client_secret.json file. set :client_id, Google::Auth::ClientId.from_file('/path/to/client_secret.json') # Required, scope value # Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. scope = ['Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY', 'Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY'] # Required, Authorizers require a storage instance to manage long term persistence of # access and refresh tokens. set :token_store, Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new) # Required, indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. set :callback_uri, '/oauth2callback' # To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI # from the client_secret.json file. To get these credentials for your application, visit # https://console.cloud.google.com/apis/credentials. set :authorizer, Google::Auth::WebUserAuthorizer.new(settings.client_id, settings.scope, settings.token_store, callback_uri: settings.callback_uri) end get '/' do # NOTE: Assumes the user is already authenticated to the app user_id = request.session['user_id'] # Fetch stored credentials for the user from the given request session. # nil if none present credentials = settings.authorizer.get_credentials(user_id, request) if credentials.nil? # Generate a url that asks the user to authorize requested scope(s). # Then, redirect user to the url. redirect settings.authorizer.get_authorization_url(request: request) end # User authorized the request. Now, check which scopes were granted. if credentials.scope.include?(Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY) # User authorized read-only Drive activity permission. # Example of using Google Drive API to list filenames in user's Drive. drive = Google::Apis::DriveV3::DriveService.new files = drive.list_files(options: { authorization: credentials }) "<pre>#{JSON.pretty_generate(files.to_h)}</pre>" else # User didn't authorize read-only Drive activity permission. # Update UX and application accordingly end # Check if user authorized Calendar read permission. if credentials.scope.include?(Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY) # User authorized Calendar read permission. # Calling the APIs, etc. else # User didn't authorize Calendar read permission. # Update UX and application accordingly end end # Receive the callback from Google's OAuth 2.0 server. get '/oauth2callback' do # Handle the result of the oauth callback. Defers the exchange of the code by # temporarily stashing the results in the user's session. target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request) redirect target_url end
Node.js
כדי להפעיל את הדוגמה הזו:
-
בקונסולה לממשקי API, מוסיפים את כתובת ה-URL של המכונה המקומית לרשימת כתובות ה-URL להפניה אוטומטית. לדוגמה, מוסיפים את
http://localhost. - מוודאים שמותקנת גרסת LTS לתחזוקה, גרסת LTS פעילה או הגרסה הנוכחית של Node.js.
-
יוצרים ספרייה חדשה ועוברים אליה. לדוגמה:
mkdir ~/nodejs-oauth2-example cd ~/nodejs-oauth2-example
-
מתקינים את ספריית הלקוח של Google API ל-Node.js באמצעות npm:
npm install googleapis
-
יוצרים את הקבצים
main.jsעם התוכן הבא. -
מריצים את הדוגמה:
node .\main.js
main.js
const http = require('http'); const https = require('https'); const url = require('url'); const { google } = require('googleapis'); const crypto = require('crypto'); const express = require('express'); const session = require('express-session'); /** * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI. * To get these credentials for your application, visit * https://console.cloud.google.com/apis/credentials. */ const oauth2Client = new google.auth.OAuth2( YOUR_CLIENT_ID, YOUR_CLIENT_SECRET, YOUR_REDIRECT_URL ); // Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. const scopes = [ 'https://www.googleapis.com/auth/drive.metadata.readonly', 'https://www.googleapis.com/auth/calendar.readonly' ]; /* Global variable that stores user credential in this code example. * ACTION ITEM for developers: * Store user's refresh token in your data store if * incorporating this code into your real app. * For more information on handling refresh tokens, * see https://github.com/googleapis/google-api-nodejs-client#handling-refresh-tokens */ let userCredential = null; async function main() { const app = express(); app.use(session({ secret: 'your_secure_secret_key', // Replace with a strong secret resave: false, saveUninitialized: false, })); // Example on redirecting user to Google's OAuth 2.0 server. app.get('/', async (req, res) => { // Generate a secure random state value. const state = crypto.randomBytes(32).toString('hex'); // Store state in the session req.session.state = state; // Generate a url that asks permissions for the Drive activity and Google Calendar scope const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true, // Include the state parameter to reduce the risk of CSRF attacks. state: state }); res.redirect(authorizationUrl); }); // Receive the callback from Google's OAuth 2.0 server. app.get('/oauth2callback', async (req, res) => { // Handle the OAuth 2.0 server response let q = url.parse(req.url, true).query; if (q.error) { // An error response e.g. error=access_denied console.log('Error:' + q.error); } else if (q.state !== req.session.state) { //check state value console.log('State mismatch. Possible CSRF attack'); res.end('State mismatch. Possible CSRF attack'); } else { // Get access and refresh tokens (if access_type is offline) let { tokens } = await oauth2Client.getToken(q.code); oauth2Client.setCredentials(tokens); /** Save credential to the global variable in case access token was refreshed. * ACTION ITEM: In a production app, you likely want to save the refresh token * in a secure persistent database instead. */ userCredential = tokens; // User authorized the request. Now, check which scopes were granted. if (tokens.scope.includes('https://www.googleapis.com/auth/drive.metadata.readonly')) { // User authorized read-only Drive activity permission. // Example of using Google Drive API to list filenames in user's Drive. const drive = google.drive('v3'); drive.files.list({ auth: oauth2Client, pageSize: 10, fields: 'nextPageToken, files(id, name)', }, (err1, res1) => { if (err1) return console.log('The API returned an error: ' + err1); const files = res1.data.files; if (files.length) { console.log('Files:'); files.map((file) => { console.log(`${file.name} (${file.id})`); }); } else { console.log('No files found.'); } }); } else { // User didn't authorize read-only Drive activity permission. // Update UX and application accordingly } // Check if user authorized Calendar read permission. if (tokens.scope.includes('https://www.googleapis.com/auth/calendar.readonly')) { // User authorized Calendar read permission. // Calling the APIs, etc. } else { // User didn't authorize Calendar read permission. // Update UX and application accordingly } } }); // Example on revoking a token app.get('/revoke', async (req, res) => { // Build the string for the POST request let postData = "token=" + userCredential.access_token; // Options for POST request to Google's OAuth 2.0 server to revoke a token let postOptions = { host: 'oauth2.googleapis.com', port: '443', path: '/revoke', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(postData) } }; // Set up the request const postReq = https.request(postOptions, function (res) { res.setEncoding('utf8'); res.on('data', d => { console.log('Response: ' + d); }); }); postReq.on('error', error => { console.log(error) }); // Post the request with data postReq.write(postData); postReq.end(); }); const server = http.createServer(app); server.listen(8080); } main().catch(console.error);
HTTP/REST
הדוגמה הזו ב-Python משתמשת במסגרת Flask ובספרייה Requests כדי להדגים את תהליך האימות של OAuth 2.0 באינטרנט. מומלץ להשתמש בספריית הלקוח של Google API ל-Python בתהליך הזה. (בדוגמה בכרטיסייה Python נעשה שימוש בספריית הלקוח).
import json import flask import requests app = flask.Flask(__name__) # To get these credentials (CLIENT_ID CLIENT_SECRET) and for your application, visit # https://console.cloud.google.com/apis/credentials. CLIENT_ID = '123456789.apps.googleusercontent.com' CLIENT_SECRET = 'abc123' # Read from a file or environmental variable in a real app # Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly' # Indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. REDIRECT_URI = 'http://example.com/oauth2callback' @app.route('/') def index(): if 'credentials' not in flask.session: return flask.redirect(flask.url_for('oauth2callback')) credentials = json.loads(flask.session['credentials']) if credentials['expires_in'] <= 0: return flask.redirect(flask.url_for('oauth2callback')) else: # User authorized the request. Now, check which scopes were granted. if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['scope']: # User authorized read-only Drive activity permission. # Example of using Google Drive API to list filenames in user's Drive. headers = {'Authorization': 'Bearer {}'.format(credentials['access_token'])} req_uri = 'https://www.googleapis.com/drive/v2/files' r = requests.get(req_uri, headers=headers).text else: # User didn't authorize read-only Drive activity permission. # Update UX and application accordingly r = 'User did not authorize Drive permission.' # Check if user authorized Calendar read permission. if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['scope']: # User authorized Calendar read permission. # Calling the APIs, etc. r += 'User authorized Calendar permission.' else: # User didn't authorize Calendar read permission. # Update UX and application accordingly r += 'User did not authorize Calendar permission.' return r @app.route('/oauth2callback') def oauth2callback(): if 'code' not in flask.request.args: state = str(uuid.uuid4()) flask.session['state'] = state # Generate a url that asks permissions for the Drive activity # and Google Calendar scope. Then, redirect user to the url. auth_uri = ('https://accounts.google.com/o/oauth2/v2/auth?response_type=code' '&client_id={}&redirect_uri={}&scope={}&state={}').format(CLIENT_ID, REDIRECT_URI, SCOPE, state) return flask.redirect(auth_uri) else: if 'state' not in flask.request.args or flask.request.args['state'] != flask.session['state']: return 'State mismatch. Possible CSRF attack.', 400 auth_code = flask.request.args.get('code') data = {'code': auth_code, 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET, 'redirect_uri': REDIRECT_URI, 'grant_type': 'authorization_code'} # Exchange authorization code for access and refresh tokens (if access_type is offline) r = requests.post('https://oauth2.googleapis.com/token', data=data) flask.session['credentials'] = r.text return flask.redirect(flask.url_for('index')) if __name__ == '__main__': import uuid app.secret_key = str(uuid.uuid4()) app.debug = False app.run()
כללי אימות של URI להפניה אוטומטית
Google מחילה את כללי האימות הבאים על מזהי URI להפניה אוטומטית כדי לעזור למפתחים לשמור על אבטחת האפליקציות שלהם. כתובות ה-URI להפניה אוטומטית חייבות לעמוד בכללים האלה. בסעיף 3 של RFC 3986 מופיעות ההגדרות של דומיין, מארח, נתיב, שאילתה, סכימה ופרטי משתמש, שמשמשות בכללים האלה.
| כללי אימות | |
|---|---|
| Scheme |
כתובות ה-URI להפניה אוטומטית חייבות להשתמש בסכמת HTTPS, ולא ב-HTTP רגיל. מזהי URI של מארח מקומי (כולל מזהי URI של כתובות IP של מארח מקומי) פטורים מהכלל הזה. |
| מארח |
המארחים לא יכולים להיות כתובות IP גולמיות. כתובות IP של מארח מקומי פטורות מהכלל הזה. |
| דומיין |
“googleusercontent.com”.goo.gl), אלא אם הדומיין בבעלות האפליקציה. בנוסף, אם אפליקציה שבבעלותה דומיין מקוצר בוחרת להפנות לכתובת ה-URI של הדומיין הזה, כתובת ה-URI להפניה צריכה להכיל את “/google-callback/” בנתיב שלה או להסתיים ב-“/google-callback”. |
| Userinfo |
כתובות URI להפניה אוטומטית לא יכולות להכיל את רכיב המשנה userinfo. |
| נתיב |
כתובות URI להפניה אוטומטית לא יכולות להכיל מעבר נתיב (נקרא גם חזרה אחורה בספרייה), שמיוצג על ידי |
| שאילתה |
כתובות URI להפניה אוטומטית לא יכולות להכיל הפניות אוטומטיות פתוחות. |
| Fragment |
כתובות URI להפניה אוטומטית לא יכולות להכיל את רכיב המקטע. |
| דמויות |
כתובות URI להפניה אוטומטית לא יכולות להכיל תווים מסוימים, כולל:
|
הרשאה מצטברת
בפרוטוקול OAuth 2.0, האפליקציה מבקשת הרשאה לגשת למשאבים שמזוהים לפי היקפי הרשאות. כדי לספק את חוויית המשתמש הטובה ביותר, מומלץ לבקש הרשאה למשאבים בזמן שאתם צריכים אותם. כדי לאפשר את זה, שרת ההרשאות של Google תומך בהרשאה מצטברת. התכונה הזו מאפשרת לבקש היקפים לפי הצורך. אם המשתמש נותן הרשאה להיקף החדש, התכונה מחזירה קוד הרשאה שאפשר להחליף באסימון שמכיל את כל ההיקפים שהמשתמש נתן לפרויקט.
לדוגמה, אפליקציה שמאפשרת לאנשים להאזין לדגימות של טראקי מוזיקה וליצור מיקסים, עשויה לדרוש מעט מאוד משאבים בזמן הכניסה, אולי רק את השם של המשתמש שנכנס. אבל כדי לשמור את המיקס המוגמר, צריך גישה ל-Google Drive של המשתמש. רוב האנשים יראו את זה כדבר טבעי אם יבקשו מהם גישה ל-Google Drive רק כשהאפליקציה באמת תצטרך אותה.
במקרה כזה, בזמן הכניסה האפליקציה עשויה לבקש את ההיקפים openid ו-profile כדי לבצע כניסה בסיסית, ואחר כך לבקש את ההיקף https://www.googleapis.com/auth/drive.file בזמן הבקשה הראשונה לשמירת מיקס.
כדי להטמיע הרשאה מצטברת, משלימים את התהליך הרגיל של בקשת אסימון גישה, אבל מוודאים שבקשת ההרשאה כוללת היקפי הרשאות שכבר ניתנו. כך האפליקציה לא צריכה לנהל כמה אסימוני גישה.
הכללים הבאים חלים על טוקן גישה שמתקבל מהרשאה מצטברת:
- אפשר להשתמש באסימון כדי לגשת למשאבים שתואמים לכל אחד מההיקפים שנכללים בהרשאה החדשה והמשולבת.
- כשמשתמשים בטוקן הרענון של ההרשאה המשולבת כדי לקבל טוקן גישה, טוקן הגישה מייצג את ההרשאה המשולבת ואפשר להשתמש בו לכל אחד מערכי
scopeשנכללים בתגובה. - ההרשאה המשולבת כוללת את כל היקפי ההרשאות שהמשתמש העניק לפרויקט ה-API, גם אם הבקשות להענקת ההרשאות הגיעו מלקוחות שונים. לדוגמה, אם משתמש נתן גישה להיקף אחד באמצעות אפליקציית לקוח למחשב, ואז נתן גישה להיקף אחר לאותה אפליקציה באמצעות אפליקציית לקוח לנייד, ההרשאה המשולבת תכלול את שני ההיקפים.
- אם מבטלים אסימון שמייצג הרשאה משולבת, הגישה לכל היקפי ההרשאות של אותה הרשאה בשם המשתמש המשויך מבוטלת בו-זמנית.
דוגמאות הקוד הספציפיות לשפה בשלב 1: הגדרת הפניית הרשאה לשרת OAuth 2.0 של Google משתמשות כולן בהרשאה מצטברת. בדוגמאות הקוד הבאות אפשר לראות גם את הקוד שצריך להוסיף כדי להשתמש בהרשאה מצטברת.
PHP
$client->setIncludeGrantedScopes(true);Python
ב-Python, מגדירים את הארגומנט של מילת המפתח include_granted_scopes ל-true כדי לוודא שבקשת ההרשאה כוללת היקפים שהוקצו בעבר. יכול מאוד להיות ש-include_granted_scopes לא יהיה הארגומנט היחיד של מילת המפתח שתגדירו, כמו שמוצג בדוגמה הבאה.
authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Enable incremental authorization. Recommended as a best practice. include_granted_scopes='true')
Ruby
auth_client.update!( :additional_parameters => {"include_granted_scopes" => "true"} )
Node.js
const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true });
HTTP/REST
GET https://accounts.google.com/o/oauth2/v2/auth? client_id=your_client_id& response_type=code& state=state_parameter_passthrough_value& scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly& redirect_uri=https%3A//developers.google.com/oauthplayground& prompt=consent& include_granted_scopes=true
רענון של טוקן גישה (גישה אופליין)
טוקני גישה פגים מדי פעם והופכים לפרטי כניסה לא חוקיים לבקשת API קשורה. אם ביקשתם גישה אופליין להיקפי ההרשאות שמשויכים לטוקן, אתם יכולים לרענן את טוקן הגישה בלי לבקש הרשאה מהמשתמש (גם כשהמשתמש לא נוכח).
- אם אתם משתמשים בספריית לקוח של Google API, אובייקט הלקוח מרענן את אסימון הגישה לפי הצורך, כל עוד אתם מגדירים את האובייקט הזה לגישה אופליין.
- אם אתם לא משתמשים בספריית לקוח, אתם צריכים להגדיר את הפרמטר
access_typeשל שאילתת ה-HTTP לערךofflineכשאתם מפנים את המשתמש לשרת OAuth 2.0 של Google. במקרה כזה, שרת ההרשאות של Google מחזיר טוקן רענון כשאתם מחליפים קוד הרשאה בטוקן גישה. לאחר מכן, אם פג התוקף של טוקן הגישה (או בכל זמן אחר), אתם יכולים להשתמש בטוקן רענון כדי לקבל טוקן גישה חדש.
בקשת גישה אופליין היא דרישה לכל אפליקציה שצריכה לגשת ל-Google API כשהמשתמש לא נוכח. לדוגמה, אפליקציה שמבצעת שירותי גיבוי או פעולות בזמנים שנקבעו מראש צריכה להיות מסוגלת לרענן את טוקן הגישה שלה כשהמשתמש לא נוכח. סגנון הגישה שמוגדר כברירת מחדל נקרא online.
אפליקציות אינטרנט בצד השרת, אפליקציות מותקנות ומכשירים מקבלים טוקני רענון במהלך תהליך ההרשאה. בדרך כלל לא משתמשים בטוקני רענון באפליקציות אינטרנט בצד הלקוח (JavaScript).
PHP
אם האפליקציה צריכה גישה אופליין ל-Google API, צריך להגדיר את סוג הגישה של לקוח ה-API ל-offline:
$client->setAccessType("offline");אחרי שהמשתמש נותן גישה אופליין להיקפי ההרשאות המבוקשים, אתם יכולים להמשיך להשתמש בלקוח ה-API כדי לגשת לממשקי Google API בשם המשתמש כשהוא אופליין. אובייקט הלקוח ירענן את אסימון הגישה לפי הצורך.
Python
ב-Python, מגדירים את הארגומנט של מילת המפתח access_type לערך offline כדי לוודא שתוכלו לרענן את אסימון הגישה בלי לבקש שוב הרשאה מהמשתמש. יכול מאוד להיות ש-access_type לא יהיה הארגומנט היחיד של מילת המפתח שתגדירו, כמו שמוצג בדוגמה הבאה.
authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Enable incremental authorization. Recommended as a best practice. include_granted_scopes='true')
אחרי שהמשתמש נותן גישה אופליין להיקפי ההרשאות המבוקשים, אתם יכולים להמשיך להשתמש בלקוח ה-API כדי לגשת לממשקי Google API בשם המשתמש כשהוא אופליין. אובייקט הלקוח ירענן את אסימון הגישה לפי הצורך.
Ruby
אם האפליקציה צריכה גישה אופליין ל-Google API, צריך להגדיר את סוג הגישה של לקוח ה-API ל-offline:
auth_client.update!( :additional_parameters => {"access_type" => "offline"} )
אחרי שהמשתמש נותן גישה אופליין להיקפי ההרשאות המבוקשים, אתם יכולים להמשיך להשתמש בלקוח ה-API כדי לגשת לממשקי Google API בשם המשתמש כשהוא אופליין. אובייקט הלקוח ירענן את אסימון הגישה לפי הצורך.
Node.js
אם האפליקציה צריכה גישה אופליין ל-Google API, צריך להגדיר את סוג הגישה של לקוח ה-API ל-offline:
const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true });
אחרי שהמשתמש נותן גישה אופליין להיקפי ההרשאות המבוקשים, אתם יכולים להמשיך להשתמש בלקוח ה-API כדי לגשת לממשקי Google API בשם המשתמש כשהוא אופליין. אובייקט הלקוח ירענן את אסימון הגישה לפי הצורך.
לטוקני גישה יש תוקף. הספרייה הזו תשתמש באופן אוטומטי בטוקן רענון כדי לקבל טוקן גישה חדש אם התוקף שלו עומד לפוג. דרך קלה לוודא שאתם תמיד שומרים את הטוקנים העדכניים ביותר היא להשתמש באירוע הטוקנים:
oauth2Client.on('tokens', (tokens) => { if (tokens.refresh_token) { // store the refresh_token in your secure persistent database console.log(tokens.refresh_token); } console.log(tokens.access_token); });
אירוע הטוקנים הזה מתרחש רק בהרשאה הראשונה, וצריך להגדיר את access_type ל-offline כשקוראים ל-method generateAuthUrl כדי לקבל את טוקן הרענון. אם כבר נתתם לאפליקציה את ההרשאות הנדרשות בלי להגדיר את המגבלות המתאימות לקבלת טוקן רענון, תצטרכו לאשר מחדש את האפליקציה כדי לקבל טוקן רענון חדש.
כדי להגדיר את refresh_token במועד מאוחר יותר, אפשר להשתמש בשיטה setCredentials:
oauth2Client.setCredentials({ refresh_token: `STORED_REFRESH_TOKEN` });
אחרי שללקוח יש טוקן רענון, טוקני הגישה יתקבלו ויתרעננו באופן אוטומטי בקריאה הבאה ל-API.
HTTP/REST
כדי לרענן אסימון גישה, האפליקציה שולחת בקשת HTTPS POST לשרת ההרשאות של Google (https://oauth2.googleapis.com/token) שכוללת את הפרמטרים הבאים בגוף הבקשה:
| שם | ערך |
|---|---|
client_id |
מזהה הלקוח שמתקבל מ-קונסולה לממשקי API. |
client_secret |
אופציונלי
סוד הלקוח שהתקבל מקונסולה לממשקי API. |
grant_type |
כפי שמוגדר במפרט של OAuth 2.0, הערך של השדה הזה צריך להיות refresh_token. |
refresh_token |
טוקן הרענון שהוחזר מהחלפת קוד ההרשאה. |
השימוש ב-DPoP הוא אופציונלי, אבל מומלץ להשתמש בו כדי לשפר את האבטחה. כדי להשתמש ב-DPoP עם טוקן רענון, האפליקציה צריכה ליצור JWT חדש וייחודי להוכחת DPoP לכל בקשה לנקודת הקצה של הטוקן. ההוכחה הזו חייבת להיות חתומה באמצעות אותו מפתח פרטי ששימש במהלך ההחלפה הראשונית של קוד ההרשאה. האפליקציה מוסיפה את ההוכחה ככותרת של בקשת HTTP.
| כותרת | חובה | תיאור |
|---|---|---|
DPoP |
אופציונלי | הוכחת DPoP היא JWT שמוכיח את הבעלות על מפתח פרטי. זו כותרת, לא פרמטר. אם הוא מסופק, האסימונים שמוחזרים קשורים למפתח הזה. צריך ליצור הוכחה חדשה וייחודית לכל בקשה, והיא צריכה לכלול טענות נכונות (claims) של htm (שיטת HTTP) ו-htu (מזהה URI של HTTP) שתואמות לבקשה. |
בקטע הקוד הבא יש בקשה לדוגמה:
POST /token HTTP/1.1 Host: oauth2.googleapis.com Content-Type: application/x-www-form-urlencoded DPoP: DPOP_PROOF_JWT client_id=your_client_id& refresh_token=refresh_token& grant_type=refresh_token
כדי להשתמש ב-DPoP עם טוקן רענון, צריך ליצור JWT חדש וייחודי להוכחת DPoP בשביל הבקשה. במאמר יצירת הוכחת DPoP מוסבר איך ליצור את זוג המפתחות ואת ה-JWT. ראו הוראות מפורטות.
החלפה מוצלחת מסומנת בתגובה 200 OK שמכילה אסימון גישה חדש. כשמשתמשים ב-DPoP, הערך של token_type הוא Bearer. תגובה מוצלחת מאשרת שההוכחה של DPoP לטוקן הרענון התקבלה. יכול להיות ש-Google תחזיר גם כותרת HTTP חדשה של DPoP-Nonce בתגובה. אם היא מוחזרת, הלקוח צריך לשמור את ה-nonce הזה במטמון ולכלול אותו בטענת הנכוֹנוּת (claim) של nonce בהוכחת ה-DPoP בבקשות הבאות.
העברת נתונים נכשלת אם הכותרת DPoP חסרה, לא תקינה או משתמשת במפתח שגוי. לפרטים על קודי שגיאה ספציפיים של DPoP ועל הטיפול ב-nonce, ראו החלפה שנכשלה.
קטע הקוד הבא מציג דוגמה לכותרות ולגוף של תגובה תקינה כשמשתמשים ב-DPoP:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
DPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI
{
"access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in": 3920,
"scope": "https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly",
"token_type": "Bearer"
}שימו לב שיש מגבלות על מספר טוקני הרענון שיונפקו: מגבלה אחת לכל שילוב של לקוח ומשתמש, ומגבלה נוספת לכל משתמש בכל הלקוחות. כדאי לשמור את טוקני הרענון באחסון לטווח ארוך ולהמשיך להשתמש בהם כל עוד הם בתוקף. אם האפליקציה מבקשת יותר מדי טוקנים לרענון, היא עלולה להגיע למגבלות האלה, ובמקרה כזה טוקנים ישנים יותר לרענון יפסיקו לפעול.
ביטול הטוקן
במקרים מסוימים, משתמשים עשויים לרצות לבטל את הגישה שניתנה לאפליקציה. המשתמש יכול לבטל את הגישה דרך הגדרות החשבון. מידע נוסף זמין במאמר התמיכה הסרת הגישה של אתר או אפליקציה בקטע "אתרים ואפליקציות של צד שלישי שיש להם גישה לחשבון שלך".
אפליקציה יכולה גם לבטל באופן פרוגרמטי את הגישה שניתנה לה. ביטול גישה פרוגרמטי חשוב במקרים שבהם משתמש מבטל מינוי, מסיר אפליקציה או שמשאבי ה-API שנדרשים לאפליקציה השתנו באופן משמעותי. במילים אחרות, חלק מתהליך ההסרה יכול לכלול בקשת API כדי לוודא שההרשאות שניתנו לאפליקציה בעבר הוסרו.
PHP
כדי לבטל טוקן באופן פרוגרמטי, צריך להפעיל את revokeToken():
$client->revokeToken();Python
כדי לבטל טוקן באופן פרוגרמטי, שולחים בקשה אל https://oauth2.googleapis.com/revoke שכוללת את הטוקן כפרמטר ומגדירים את הכותרת Content-Type:
requests.post('https://oauth2.googleapis.com/revoke', params={'token': credentials.token}, headers = {'content-type': 'application/x-www-form-urlencoded'})
Ruby
כדי לבטל אסימון באופן פרוגרמטי, שולחים בקשת HTTP לנקודת הקצה (endpoint) oauth2.revoke:
uri = URI('https://oauth2.googleapis.com/revoke') response = Net::HTTP.post_form(uri, 'token' => auth_client.access_token)
הטוקן יכול להיות טוקן גישה או טוקן רענון. אם הטוקן הוא טוקן גישה ויש לו טוקן רענון תואם, גם טוקן הרענון יבוטל.
אם הביטול בוצע בהצלחה, קוד הסטטוס של התגובה הוא 200. במצבי שגיאה, קוד הסטטוס 400 מוחזר יחד עם קוד שגיאה.
Node.js
כדי לבטל טוקן באופן פרוגרמטי, שולחים בקשת HTTPS POST לנקודת הקצה (endpoint) של /revoke:
const https = require('https'); // Build the string for the POST request let postData = "token=" + userCredential.access_token; // Options for POST request to Google's OAuth 2.0 server to revoke a token let postOptions = { host: 'oauth2.googleapis.com', port: '443', path: '/revoke', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(postData) } }; // Set up the request const postReq = https.request(postOptions, function (res) { res.setEncoding('utf8'); res.on('data', d => { console.log('Response: ' + d); }); }); postReq.on('error', error => { console.log(error) }); // Post the request with data postReq.write(postData); postReq.end();
הפרמטר token יכול להיות טוקן גישה או טוקן רענון. אם הטוקן הוא טוקן גישה ויש לו טוקן רענון תואם, גם טוקן הרענון יבוטל.
אם הביטול בוצע בהצלחה, קוד הסטטוס של התגובה הוא 200. במצבי שגיאה, קוד הסטטוס 400 מוחזר יחד עם קוד שגיאה.
HTTP/REST
כדי לבטל אסימון באופן פרוגרמטי, האפליקציה שולחת בקשה אל 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 יחד עם קוד שגיאה.
גישה מבוססת-זמן
גישה מבוססת-זמן מאפשרת למשתמשים לתת לאפליקציה גישה לנתונים שלהם למשך זמן מוגבל כדי להשלים פעולה. במוצרים נבחרים של Google, אפשר להגדיר גישה מוגבלת בזמן בתהליך קבלת ההסכמה. כך המשתמשים יכולים לתת גישה לפרק זמן מוגבל. דוגמה לכך היא Data Portability API, שמאפשר העברת נתונים חד-פעמית.
כשמשתמש נותן לאפליקציה גישה מבוססת-זמן, התוקף של טוקן הרענון יפוג אחרי משך הזמן שצוין. חשוב לדעת שבנסיבות מסוימות, יכול להיות שטוקני הרענון יבוטלו לפני כן. פרטים נוספים זמינים במקרים האלה. השדה refresh_token_expires_in שמוחזר בתגובה להחלפת קוד ההרשאה מייצג את הזמן שנותר עד שתוקף טוקן הרענון יפוג במקרים כאלה.
הטמעת ההגנה על כל החשבונות
כדי להגן על חשבונות המשתמשים, מומלץ גם להטמיע את שירות ההגנה על כל החשבונות של 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
במאמר הגנה על חשבונות משתמשים באמצעות הגנה על כל החשבונות מוסבר איך להטמיע את ההגנה על כל החשבונות, ויש בו רשימה מלאה של האירועים הזמינים.