במסמך הזה מוסבר איך אפליקציות של שרת אינטרנט משתמשות בספריות לקוח של Google API או בנקודות קצה של Google OAuth 2.0 כדי להטמיע הרשאה של OAuth 2.0 לגישה לממשקי Google API.
OAuth 2.0 מאפשר למשתמשים לשתף נתונים ספציפיים עם אפליקציה תוך שמירה על הפרטיות של שמות המשתמשים, הסיסמאות והמידע הנוסף שלהם. לדוגמה, אפליקציה יכולה להשתמש ב-OAuth 2.0 כדי לקבל מהמשתמשים הרשאה לאחסן קבצים ב-Google Drive שלהם.
התהליך הזה של OAuth 2.0 מיועד במיוחד להרשאת משתמשים. הוא מיועד לאפליקציות שיכולות לאחסן מידע סודי ולשמור מצב. אפליקציה של שרת אינטרנט עם הרשאה מתאימה יכולה לגשת ל-API בזמן שהמשתמש מקיים אינטראקציה עם האפליקציה או אחרי שהמשתמש עזב את האפליקציה.
אפליקציות של שרת אינטרנט משתמשות לעיתים קרובות גם ב חשבונות שירות כדי לאשר בקשות API, במיוחד כשמבצעים קריאה לממשקי Cloud API כדי לגשת לנתונים שמבוססים על פרויקט ולא לנתונים ספציפיים למשתמש. אפליקציות של שרת אינטרנט יכולות להשתמש בחשבונות שירות בשילוב עם הרשאת משתמש.
ספריות לקוח
בדוגמאות הספציפיות לשפות שבדף הזה נעשה שימוש בספריות הלקוח של Google API כדי להטמיע הרשאות OAuth 2.0. כדי להריץ את דוגמאות הקוד, צריך קודם להתקין את ספריית הלקוח בשפה הרצויה.
כשמשתמשים בספריית לקוח של Google API כדי לטפל בתהליך OAuth 2.0 של האפליקציה, ספריית הלקוח מבצעת פעולות רבות שהאפליקציה הייתה צריכה לטפל בהן בעצמה. לדוגמה, הוא קובע מתי האפליקציה יכולה להשתמש באסימוני גישה שמאוחסנים או לרענן אותם, וגם מתי האפליקציה צריכה לקבל הסכמה מחדש. ספריית הלקוח גם יוצרת כתובות URL נכונות להפניה אוטומטית, ומסייעת להטמיע מנהלים של הפניות אוטומטיות שממירים קודי הרשאה לאסימוני גישה.
ספריות הלקוח של Google API לאפליקציות בצד השרת זמינות בשפות הבאות:
דרישות מוקדמות
הפעלת ממשקי API בפרויקט
כל אפליקציה שמבצעת קריאה ל-Google APIs צריכה להפעיל את ממשקי ה-API האלה ב- API Console.
כדי להפעיל ממשק API בפרויקט:
- Open the API Library ב Google API Console.
- If prompted, select a project, or create a new one.
- ב- API Library מפורטים כל ממשקי ה-API הזמינים, שמקובצים לפי משפחת מוצרים ופופולריות. אם ממשק ה-API שרוצים להפעיל לא מופיע ברשימה, אפשר לחפש אותו או ללחוץ על הצגת הכול במשפחת המוצרים שאליה הוא שייך.
- בוחרים את ה-API שרוצים להפעיל ולוחצים על הלחצן Enable.
- If prompted, enable billing.
- If prompted, read and accept the API's Terms of Service.
יצירת פרטי כניסה להרשאה
כל אפליקציה שמשתמשת ב-OAuth 2.0 כדי לגשת ל-Google APIs חייבת לכלול פרטי כניסה שמזהים את האפליקציה לשרת OAuth 2.0 של Google. בשלבים הבאים מוסבר איך ליצור פרטי כניסה לפרויקט. לאחר מכן, האפליקציות שלכם יוכלו להשתמש בפרטי הכניסה כדי לגשת לממשקי ה-API שהפעלתם בפרויקט הזה.
- Go to the Credentials page.
- לוחצים על Create credentials (יצירת פרטי כניסה) > OAuth client ID (מזהה לקוח OAuth).
- בוחרים את סוג האפליקציה Web application.
- ממלאים את הטופס ולוחצים על Create. באפליקציות שמשתמשות בשפות ובמסגרות כמו PHP, Java, Python, Ruby ו-NET, צריך לציין מזהי URI להפניה אוטומטית מורשים. מזהי ה-URI להפניה אוטומטית הם נקודות הקצה שאליהן שרת OAuth 2.0 יכול לשלוח תגובות. נקודות הקצה האלה חייבות לעמוד בכללי האימות של Google.
לצורך בדיקה, אפשר לציין מזהי URI שמפנים למכונה המקומית, כמו
http://localhost:8080
. לכן, חשוב לזכור שבכל הדוגמאות במסמך הזה נעשה שימוש ב-http://localhost:8080
בתור ה-URI להפניה אוטומטית.מומלץ לתכנן את נקודות הקצה לאימות של האפליקציה כך שהאפליקציה לא תחשוף קודי הרשאה למשאבים אחרים בדף.
אחרי שיוצרים את פרטי הכניסה, מורידים את הקובץ client_secret.json מ- API Console. שומרים את הקובץ באופן מאובטח במיקום שרק לאפליקציה יש גישה אליו.
זיהוי היקפי הגישה
היקפי הרשאות מאפשרים לאפליקציה לבקש גישה רק למשאבים שנחוצים לה, וגם מאפשרים למשתמשים לקבוע את רמת הגישה שהם מעניקים לאפליקציה. לכן, יכול להיות שיש קשר הפוך בין מספר ההיקפים המבוקשים לבין הסבירות לקבלת הסכמה מהמשתמשים.
לפני שמתחילים להטמיע הרשאה מסוג OAuth 2.0, מומלץ לזהות את היקפי ההרשאות שאליהם האפליקציה תצטרך גישה.
מומלץ גם שהאפליקציה תבקש גישה להיקפי הרשאות באמצעות תהליך הרשאה מצטברת, שבו האפליקציה מבקשת גישה לנתוני המשתמשים בהקשר המתאים. השיטה המומלצת הזו עוזרת למשתמשים להבין בקלות רבה יותר למה לאפליקציה שלכם דרושה הגישה שהיא מבקשת.
במסמך היקפי הרשאות API של OAuth 2.0 מופיעה רשימה מלאה של ההיקפים שבהם אפשר להשתמש כדי לגשת לממשקי 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
- מסגרת לאפליקציות אינטרנט ב-Python של Flask.
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
-
ספריות הלקוח של ממשקי Google API של Drive ויומן Google:
gem install google-apis-drive_v3 google-apis-calendar_v3
-
מסגרת לאפליקציות אינטרנט ב-Ruby של Sinatra.
gem install sinatra
Node.js
כדי להריץ את דוגמאות הקוד של Node.js במסמך הזה, צריך:
- מהדורת LTS לתחזוקה, מהדורת LTS פעילה או מהדורת Node.js הנוכחית.
-
לקוח Google APIs ל-Node.js:
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 של המשתמש:
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 של המשתמש:
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 של המשתמש:
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
נקודת הקצה של OAuth 2.0 של Google נמצאת בכתובת https://accounts.google.com/o/oauth2/v2/auth
. אפשר לגשת לנקודת הקצה הזו רק דרך HTTPS. חיבורי HTTP רגילים נדחים.
שרת ההרשאות של Google תומך בפרמטרים הבאים של מחרוזת השאילתה לאפליקציות של שרת אינטרנט:
פרמטרים | |||||||
---|---|---|---|---|---|---|---|
client_id |
חובה
מזהה הלקוח של האפליקציה. הערך הזה מופיע ב- API Console Credentials page. |
||||||
redirect_uri |
חובה
קובע לאן שרת ה-API מפנה את המשתמש אחרי שהוא משלים את תהליך ההרשאה. הערך צריך להתאים בדיוק לאחד מכתובות ה-URI המורשות להפניה אוטומטית של לקוח OAuth 2.0, שהגדרתם ב- API Console
Credentials pageשל הלקוח. אם הערך הזה לא תואם למזהה URI מורשה להפניה אוטומטית של חשוב לשים לב שצריך להתאים את הסכימה, האותיות הגדולות והקטנות והקו האופק ( |
||||||
response_type |
חובה
הפונקציה קובעת אם נקודת הקצה של Google OAuth 2.0 מחזירה קוד הרשאה. מגדירים את ערך הפרמטר ל- |
||||||
scope |
חובה
רשימה של היקפי הרשאות שמפרידה אותם באמצעות רווחים, ומזהה את המשאבים שהאפליקציה יכולה לגשת אליהם מטעם המשתמש. הערכים האלה מופיעים במסך ההסכמה ש-Google מציגה למשתמש. היקפי הרשאות מאפשרים לאפליקציה לבקש גישה רק למשאבים שנחוצים לה, ומאפשרים גם למשתמשים לקבוע את רמת הגישה שהם מעניקים לאפליקציה. לכן, יש קשר הפוך בין מספר ההיקפים המבוקשים לבין הסבירות לקבלת הסכמה מהמשתמשים. מומלץ לבקש מהאפליקציה לבקש גישה להיקפי הרשאה לפי הקשר, כשהדבר אפשרי. כשמבקשים גישה לנתוני משתמשים בהקשר, באמצעות הרשאה מצטברת, המשתמשים יכולים להבין בקלות רבה יותר למה לאפליקציה נדרשת הגישה שהיא מבקשת. |
||||||
access_type |
מומלץ
מציין אם האפליקציה יכולה לרענן אסימוני גישה כשהמשתמש לא נמצא בדפדפן. הערכים החוקיים של הפרמטר הם מגדירים את הערך ל- |
||||||
state |
מומלץ
מציין ערך מחרוזת שבו האפליקציה משתמשת כדי לשמור את המצב בין בקשת ההרשאה לבין התשובה של שרת ההרשאות.
השרת מחזיר את הערך המדויק ששלחתם כצמד אפשר להשתמש בפרמטר הזה למספר מטרות, למשל כדי להפנות את המשתמש למשאב הנכון באפליקציה, לשלוח ערכים חד-פעמיים (nonces) ולצמצם זיוף בקשות בין אתרים. מכיוון שאפשר לנחש את הערך של |
||||||
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
, משלב 1 שיטהgenerateAuthUrl
כדי לבקש גישה משרת 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//oauth2.example.com/code& 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 לא יכול להעניק הרשאה להיקף אחד או יותר מהיקפי הגישה המבוקשים בגלל המדיניות של האדמין ב-Google Workspace. במאמר העזרה שליטה בגישה של אפליקציות של צד שלישי ואפליקציות פנימיות לנתונים ב-Google Workspace מוסבר איך אדמין יכול להגביל את הגישה לכל ההיקפים או להיקפים רגישים ומוגבלים, עד שתוענק גישה מפורשת למזהה הלקוח של OAuth.
disallowed_useragent
נקודת הקצה של ההרשאה מוצגת בתוך סוכן משתמש מוטמע שאסור לפי כללי המדיניות של Google בנושא OAuth 2.0.
Android
מפתחי Android עשויים להיתקל בהודעת השגיאה הזו כשהם פותחים בקשות הרשאה ב-android.webkit.WebView
.
במקום זאת, המפתחים צריכים להשתמש בספריות של Android, כמו Google Sign-In ל-Android או AppAuth ל-Android של OpenID Foundation.
מפתחי אינטרנט עשויים להיתקל בשגיאה הזו כשאפליקציה ל-Android פותחת קישור אינטרנט כללי בסוכנות משתמש מוטמעת, ומשתמש מנווט מנקודת הקצה של הרשאת OAuth 2.0 של Google מהאתר שלכם. מפתחים צריכים לאפשר לקישורים כלליים להיפתח בבורר הקישורים שמוגדר כברירת מחדל במערכת ההפעלה, שכולל גם את הבוררים של קישורים לאפליקציות Android או את אפליקציית הדפדפן שמוגדרת כברירת מחדל. גם הספרייה Android Custom Tabs נתמכת.
iOS
מפתחים של iOS ו-macOS עשויים להיתקל בשגיאה הזו כשהם פותחים בקשות הרשאה ב-WKWebView
.
במקום זאת, המפתחים צריכים להשתמש בספריות ל-iOS כמו Google Sign-In ל-iOS או AppAuth ל-iOS של OpenID Foundation.
מפתחי אינטרנט עשויים להיתקל בשגיאה הזו כשאפליקציה ל-iOS או ל-macOS פותחת קישור אינטרנט כללי בסוכן משתמש מוטמע, ומשתמש מנווט מנקודת הקצה של הרשאת OAuth 2.0 של Google מהאתר שלכם. מפתחים צריכים לאפשר לקישורים כלליים להיפתח במנהל הקישורים שמוגדר כברירת מחדל במערכת ההפעלה, שכולל גם את מנהלי Universal Links או את אפליקציית הדפדפן שמוגדרת כברירת מחדל. גם הספרייה SFSafariViewController
נתמכת.
org_internal
מזהה לקוח OAuth בבקשה הוא חלק מפרויקט שמגביל את הגישה לחשבונות Google ב ארגון ספציפי ב-Google Cloud. מידע נוסף על אפשרות ההגדרה הזו זמין בקטע סוג המשתמש במאמר העזרה בנושא הגדרת מסך הסכמה ל-OAuth.
invalid_client
סוד הלקוח של OAuth שגוי. בודקים את הגדרת הלקוח ב-OAuth, כולל מזהה הלקוח והסוד ששימשו לבקשה הזו.
invalid_grant
כשמרעננים אסימון גישה או משתמשים בהרשאה מצטברת, יכול להיות שתוקף האסימון פג או שהוא לא תקף. מאמתים שוב את המשתמש ומבקשים ממנו הסכמה לקבלת אסימונים חדשים. אם השגיאה ממשיכה להופיע, צריך לוודא שהאפליקציה מוגדרת בצורה נכונה ושאתם משתמשים באסימונים ובפרמטרים הנכונים בבקשה. אחרת, יכול להיות שחשבון המשתמש נמחק או הושבת.
redirect_uri_mismatch
הערך של redirect_uri
שהוענק בבקשת ההרשאה לא תואם לכתובת URI מורשית להפניה אוטומטית למזהה הלקוח של OAuth. בודקים את מזהי ה-URI המורשים להפניה אוטומטית בקטע Google API Console Credentials page.
הפרמטר 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//oauth2.example.com/code& client_id=client_id
אחרי השלמת תהליך OAuth 2.0, אמורה להתבצע הפניה אוטומטית לכתובת http://localhost/oauth2callback
, שתגרום כנראה לשגיאה 404 NOT FOUND
, אלא אם במחשב המקומי שלכם מוצג קובץ בכתובת הזו. בשלב הבא נספק פרטים נוספים על המידע שמוחזר ב-URI כשהמשתמש מופנה חזרה לאפליקציה.
שלב 5: מחליפים את קוד ההרשאה באסימוני רענון ואסימוני גישה
אחרי ששרת האינטרנט מקבל את קוד ההרשאה, הוא יכול להמיר את קוד ההרשאה לטוקן גישה.
PHP
כדי להמיר קוד הרשאה לטוקן גישה, משתמשים ב-method fetchAccessTokenWithAuthCode
:
$access_token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
Python
בדף הקריאה החוזרת, משתמשים בספרייה 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 the session. # ACTION ITEM for developers: # Store user's access and refresh tokens in your data store if # incorporating this code into your real app. credentials = flow.credentials flask.session['credentials'] = { 'token': credentials.token, 'refresh_token': credentials.refresh_token, 'token_uri': credentials.token_uri, 'client_id': credentials.client_id, 'client_secret': credentials.client_secret, 'granted_scopes': credentials.granted_scopes}
Ruby
בדף הקריאה החוזרת, משתמשים בספרייה 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
כדי להמיר קוד הרשאה לאסימון גישה, צריך להפעיל את נקודת הקצה https://oauth2.googleapis.com/token
ולהגדיר את הפרמטרים הבאים:
שדות | |
---|---|
client_id |
מזהה הלקוח שהתקבל מ- API Console Credentials page. |
client_secret |
סוד הלקוח שהתקבל מ- API Console Credentials page. |
code |
קוד ההרשאה שהוחזר מהבקשה הראשונית. |
grant_type |
כפי שמוגדר במפרט של OAuth 2.0, הערך של השדה הזה צריך להיות authorization_code . |
redirect_uri |
אחד מ-URI להפניה אוטומטית שמפורטים לפרויקט שלכם ב- API Console
Credentials page עבור client_id הנתון. |
בקטע הקוד הבא מוצגת בקשה לדוגמה:
POST /token HTTP/1.1 Host: oauth2.googleapis.com Content-Type: application/x-www-form-urlencoded code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7& client_id=your_client_id& client_secret=your_client_secret& redirect_uri=https%3A//oauth2.example.com/code& grant_type=authorization_code
בתגובה לבקשה הזו, Google מחזירה אובייקט JSON שמכיל אסימון גישה לטווח קצר ואסימון רענון.
חשוב לזכור שאסימון הרענון מוחזר רק אם האפליקציה שלכם מגדירה את הפרמטר access_type
לערך offline
בבקשה הראשונית לשרת ההרשאות של Google.
התגובה כוללת את השדות הבאים:
שדות | |
---|---|
access_token |
האסימון שהאפליקציה שולחת כדי לאשר בקשה ל-Google API. |
expires_in |
משך החיים שנותר של אסימון הגישה, בשניות. |
refresh_token |
אסימון שאפשר להשתמש בו כדי לקבל אסימון גישה חדש. אסימוני הרענון בתוקף עד שהמשתמש מבטל את הגישה.
שוב, השדה הזה מופיע בתגובה הזו רק אם מגדירים את הפרמטר access_type ל-offline בבקשה הראשונית לשרת ההרשאות של Google.
|
scope |
היקפי הגישה שמוענקים על ידי access_token מפורטים כרשימה של מחרוזות תלויות-אותיות רישיות שמפרידות ביניהן רווחים. |
token_type |
סוג הטוקן שהוחזר. בשלב זה, הערך של השדה הזה תמיד מוגדר ל-Bearer . |
קטע הקוד הבא מציג תגובה לדוגמה:
{ "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: בודקים אילו היקפי הרשאות המשתמשים העניקו
כשמבקשים מספר היקפי הרשאה בו-זמנית, יכול להיות שהמשתמשים לא יאשרו את כל ההיקפים שהאפליקציה מבקשת. האפליקציה תמיד צריכה לבדוק אילו היקפי הרשאות הוקצו על ידי המשתמש, ולטפל בכל דחייה של היקפי הרשאות על ידי השבתת התכונות הרלוונטיות. מידע נוסף זמין במאמר איך מטפלים בהרשאות מפורטות.
PHP
כדי לבדוק אילו היקפי הרשאות המשתמש העניק, משתמשים בשיטה 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, 'token_uri': credentials.token_uri, 'client_id': credentials.client_id, 'client_secret': credentials.client_secret, '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
בתשובה לאסימון הגישה. היקפי הגישה שמוענקים על ידי access_token מפורטים כרשימה של מחרוזות רגישות ל-case שמפרידות ביניהן רווחים.
לדוגמה, התגובה לדוגמה הבאה של אסימון הגישה מראה שהמשתמש העניק לאפליקציה שלכם גישה להרשאות 'קריאה בלבד' לפעילות ב-Drive ולאירועים ביומן:
{ "access_token": "1/fFAGRNJru1FTz70BzhT3Zg", "expires_in": 3920, "token_type": "Bearer", "scope": "https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly", "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI" }
קריאה ל-Google APIs
PHP
כדי להשתמש באסימון הגישה כדי לבצע קריאה ל-Google APIs, מבצעים את השלבים הבאים:
- אם צריך להחיל אסימון גישה על אובייקט
Google\Client
חדש – לדוגמה, אם שמרתם את אסימון הגישה בסשן של משתמש – צריך להשתמש בשיטהsetAccessToken
:$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
בכותרת Authorization
של HTTP. כשהדבר אפשרי, עדיף להשתמש בכותרת ה-HTTP, כי מחרוזות השאילתות נוטים להיות גלויות ביומנים של השרת. ברוב המקרים אפשר להשתמש בספריית לקוח כדי להגדיר את הקריאות לממשקי Google API (לדוגמה, כשקוראים ל-Drive Files API).
אפשר לנסות את כל ממשקי Google APIs ולראות את היקפי ההרשאות שלהם ב-OAuth 2.0 Playground.
דוגמאות לבקשות HTTP GET
קריאה לנקודת הקצה
drive.files
(Drive Files API) באמצעות הכותרת Authorization: Bearer
של HTTP עשויה להיראות כך: חשוב לשים לב שצריך לציין את טוקן הגישה שלכם:
GET /drive/v2/files HTTP/1.1 Host: www.googleapis.com Authorization: Bearer access_token
זוהי קריאה לאותו ממשק API עבור המשתמש המאומת באמצעות הפרמטר access_token
של מחרוזת השאילתה:
GET https://www.googleapis.com/drive/v2/files?access_token=access_token
curl
דוגמאות
אפשר לבדוק את הפקודות האלה באמצעות אפליקציית שורת הפקודה curl
. הנה דוגמה שמשתמשת באפשרות של כותרת ה-HTTP (האפשרות המועדפת):
curl -H "Authorization: Bearer access_token" https://www.googleapis.com/drive/v2/files
לחלופין, אפשר לבחור באפשרות 'פרמטר של מחרוזת שאילתה':
curl https://www.googleapis.com/drive/v2/files?access_token=access_token
דוגמה מלאה
בדוגמה הבאה מודפסת רשימה של קבצים בפורמט JSON ב-Google Drive של משתמש, אחרי שהמשתמש מבצע אימות ומביע הסכמה לאפליקציה לגשת למטא-נתונים של המשתמש ב-Drive.
PHP
כדי להריץ את הדוגמה הזו:
- ב- API Console, מוסיפים את כתובת ה-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: הקישור הזה מפנה לדף דמה שמנסה להריץ בקשת דוגמה ל-Calendar API אם המשתמשים העניקו את ההרשאה. אם צריך, הוא מתחיל את תהליך ההרשאה. אם הפעולה בוצעה ללא שגיאות, התגובה של ה-API תוצג בדף.
- בדיקת תהליך האימות ישירות: הקישור הזה מפנה לדף שמנסה לשלוח את המשתמש דרך תהליך ההרשאה. האפליקציה מבקשת הרשאה לשלוח בקשות API מורשות בשם המשתמש.
- ביטול פרטי הכניסה הנוכחיים: הקישור הזה מפנה לדף שבו מבטלים הרשאות שהמשתמש כבר העניק לאפליקציה.
- ניקוי פרטי הכניסה של סשן Flask: הקישור הזה מנקה את פרטי הכניסה להרשאה שמאוחסנים בסשן Flask. כך תוכלו לראות מה יקרה אם משתמש שכבר העניק הרשאה לאפליקציה שלכם ינסה להריץ בקשת API בסשן חדש. הוא גם מאפשר לכם לראות את תגובת ה-API שהאפליקציה תקבל אם משתמש ביטל את ההרשאות שהוקצו לאפליקציה, והאפליקציה עדיין ניסתה לאשר בקשה עם אסימון גישה שהתבטל.
# -*- coding: utf-8 -*- import os import flask 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 credentials from the session. credentials = google.oauth2.credentials.Credentials( **flask.session['credentials']) 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. # ACTION ITEM: In a production app, you likely want to save these # credentials in a persistent database instead. flask.session['credentials'] = credentials_to_dict(credentials) return flask.jsonify(**files) else: # User didn't authorize read-only Drive activity permission. # Update UX and application accordingly 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.') credentials = google.oauth2.credentials.Credentials( **flask.session['credentials']) 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: 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, 'token_uri': credentials.token_uri, 'client_id': credentials.client_id, 'client_secret': credentials.client_secret, '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="/test">Test an API request</a></td>' + '<td>Submit an API request and see a formatted JSON response. ' + ' Go through the authorization flow if there are no stored ' + ' credentials for the user.</td></tr>' + '<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="/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="/test">test the ' + ' API request</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 Console, מוסיפים את כתובת ה-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 של localhost (כולל מזהי URI של כתובות IP של localhost) פטורים מהכלל הזה. |
מארח |
לא ניתן להשתמש בכתובות IP גולמיות כמארחים. כתובות IP של localhost פטורות מהכלל הזה. |
דומיין |
“googleusercontent.com” .goo.gl ), אלא אם הדומיין הוא בבעלות האפליקציה. בנוסף, אם אפליקציה שבבעלותה דומיין קיצור בוחרת להפנות לדומיין הזה, מזהה ה-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: הגדרת הפרמטרים של ההרשאה ובכתובת ה-URL לדוגמה להפניה אוטומטית מסוג HTTP/REST שמפורטת בקטע שלב 2: הפניה אוטומטית לשרת 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//oauth2.example.com/code& prompt=consent& include_granted_scopes=true
רענון אסימון גישה (גישה אופליין)
מדי פעם פג התוקף של אסימוני הגישה והם הופכים לפרטי כניסה לא חוקיים לבקשת API קשורה. אם ביקשת גישה אופליין להיקפי הרשאה שמשויכים לטוקן, אפשר לרענן את טוקן הגישה בלי לבקש מהמשתמש הרשאה (כולל כשהמשתמש לא נמצא).
- אם אתם משתמשים בספריית לקוח של Google API, אובייקט הלקוח מעדכן את אסימון הגישה לפי הצורך, כל עוד הגדרתם את האובייקט הזה לגישה אופליין.
- אם אתם לא משתמשים בספריית לקוח, עליכם להגדיר את פרמטר השאילתה של HTTP
access_type
לערך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
כשקוראים לשיטה generateAuthUrl
כדי לקבל את אסימון הרענון. אם כבר הענקתם לאפליקציה את ההרשאות הנדרשות בלי להגדיר את האילוצים המתאימים לקבלת אסימון רענון, תצטרכו להעניק לאפליקציה הרשאה מחדש כדי לקבל אסימון רענון חדש.
כדי להגדיר את refresh_token
במועד מאוחר יותר, אפשר להשתמש בשיטה setCredentials
:
oauth2Client.setCredentials({ refresh_token: `STORED_REFRESH_TOKEN` });
אחרי שהלקוח יקבל אסימון רענון, אסימוני הגישה יירכשו ויתרעננו באופן אוטומטי בקריאה הבאה ל-API.
HTTP/REST
כדי לרענן אסימון גישה, האפליקציה שולחת בקשת POST
ב-HTTPS לשרת ההרשאות של Google (https://oauth2.googleapis.com/token
) שכוללת את הפרמטרים הבאים:
שדות | |
---|---|
client_id |
מזהה הלקוח שהתקבל מ- API Console. |
client_secret |
סוד הלקוח שהתקבל מ- API Console. |
grant_type |
כפי שמוגדר במפרט של OAuth 2.0, הערך של השדה הזה צריך להיות refresh_token . |
refresh_token |
אסימון הרענון שהוחזר מההמרה של קוד ההרשאה. |
בקטע הקוד הבא מוצגת בקשה לדוגמה:
POST /token HTTP/1.1 Host: oauth2.googleapis.com Content-Type: application/x-www-form-urlencoded client_id=your_client_id& client_secret=your_client_secret& refresh_token=refresh_token& grant_type=refresh_token
כל עוד המשתמש לא ביטל את הגישה שהוקצה לאפליקציה, שרת האסימונים מחזיר אובייקט JSON שמכיל אסימון גישה חדש. קטע הקוד הבא מציג תשובה לדוגמה:
{ "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 לנקודת הקצה oauth2.revoke
:
uri = URI('https://oauth2.googleapis.com/revoke') response = Net::HTTP.post_form(uri, 'token' => auth_client.access_token)
האסימון יכול להיות אסימון גישה או אסימון רענון. אם האסימון הוא אסימון גישה ויש לו אסימון רענון תואם, גם אסימון הרענון יבוטל.
אם תהליך הביטול הושלם, קוד הסטטוס של התשובה יהיה 200
. בתנאים של שגיאה, קוד הסטטוס 400
מוחזר יחד עם קוד שגיאה.
Node.js
כדי לבטל אסימון באופן פרוגרמטי, שולחים בקשת POST מסוג HTTPS לנקודת הקצה /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();
פרמטר האסימון יכול להיות אסימון גישה או אסימון רענון. אם האסימון הוא אסימון גישה ויש לו אסימון רענון תואם, גם אסימון הרענון יבוטל.
אם תהליך הביטול הושלם, קוד הסטטוס של התשובה יהיה 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. השירות הזה מאפשר לכם להירשם לקבלת התראות על אירועי אבטחה, שמספקות לאפליקציה מידע על שינויים משמעותיים בחשבון המשתמש. לאחר מכן תוכלו להשתמש במידע הזה כדי לבצע פעולות בהתאם לאופן שבו תבחרו להגיב לאירועים.
דוגמאות לסוגי האירועים שנשלחים לאפליקציה שלכם על ידי שירות ההגנה על חשבונות שונים של 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
במאמר הגנה על חשבונות משתמשים באמצעות הגנה על כל החשבונות מוסבר איך מטמיעים את ההגנה על כל החשבונות ומופיעה רשימה מלאה של האירועים הזמינים.