במאמר הזה מוסבר איך אפליקציות של שרתי אינטרנט משתמשות בספריות לקוח של 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 Client Libraries כדי להטמיע הרשאה של 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 שרוצים להפעיל ולוחצים על הלחצן הפעלה.
- 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 Client (יצירת לקוח).
- בוחרים את סוג האפליקציה Web application.
- ממלאים את הטופס ולוחצים על יצירה. באפליקציות שמשתמשות בשפות ובמסגרות כמו 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, מומלץ לזהות את היקפי ההרשאות שהאפליקציה תזדקק להם כדי לגשת למשאבים.
מומלץ גם שהאפליקציה תבקש גישה להיקפי הרשאות באמצעות תהליך של הרשאה מצטברת, שבו האפליקציה מבקשת גישה לנתוני המשתמש בהקשר. השיטה המומלצת הזו עוזרת למשתמשים להבין בקלות למה האפליקציה שלכם צריכה את הגישה שהיא מבקשת.
במסמך היקפי 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 web application framework.
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 APIs ל-Drive וליומן:
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:
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 |
חובה
מזהה הלקוח של האפליקציה. אפשר למצוא את הערך הזה ב . |
||||||
redirect_uri |
חובה
הפרמטר הזה קובע לאן שרת ה-API מפנה את המשתמש אחרי שהוא מסיים את תהליך ההרשאה. הערך חייב להיות זהה לאחד מכתובות ה-URI המורשות להפניה אוטומטית של לקוח OAuth 2.0, שהגדרתם ב
של הלקוח. אם הערך הזה לא תואם ל-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
משלב 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. מידע נוסף על האופן שבו אדמין יכול להגביל את הגישה לכל היקפי ההרשאות או להיקפי הרשאות רגישים ומוגבלים עד למתן גישה מפורשת למזהה לקוח OAuth זמין במאמר שליטה בגישה של אפליקציות של צד שלישי ואפליקציות פנימיות לנתונים ב-Google Workspace במרכז העזרה לאדמינים של Google Workspace.
disallowed_useragent
נקודת הקצה של ההרשאה מוצגת בתוך סוכן משתמש מוטמע שאסור לשימוש על פי מדיניות Google בנושא OAuth 2.0.
Android
מפתחי Android עשויים להיתקל בהודעת השגיאה הזו כשהם פותחים בקשות הרשאה ב-android.webkit.WebView
.
במקום זאת, מפתחים צריכים להשתמש בספריות של Android כמו כניסה באמצעות חשבון Google ל-Android או AppAuth ל-Android של OpenID Foundation.
מפתחי אתרים עשויים להיתקל בשגיאה הזו כשמשתמש עובר מאתר שלכם לנקודת ההרשאה של Google OAuth 2.0, ואפליקציית Android פותחת קישור כללי לאתר בסוכן משתמש מוטמע. מפתחים צריכים לאפשר פתיחה של קישורים כלליים בטיפול בקישורים שמוגדר כברירת מחדל במערכת ההפעלה, כולל טיפול בקישורים לאפליקציות ל-Android או באפליקציית הדפדפן שמוגדרת כברירת מחדל. ספריית Android Custom Tabs היא גם אפשרות נתמכת.
iOS
מפתחים של iOS ו-macOS עשויים להיתקל בשגיאה הזו כשהם פותחים בקשות הרשאה ב-WKWebView
.
במקום זאת, מפתחים צריכים להשתמש בספריות iOS כמו Google Sign-In for iOS או AppAuth for iOS של OpenID Foundation.
מפתחי אתרים עשויים להיתקל בשגיאה הזו כשמשתמש מנווט באתר שלכם מאפליקציית iOS או macOS שפותחת קישור כללי לאינטרנט בסוכן משתמש מוטמע, אל נקודת הקצה של Google להרשאה באמצעות OAuth 2.0. מפתחים צריכים לאפשר פתיחה של קישורים כלליים בטיפול הקישורים שמוגדר כברירת מחדל במערכת ההפעלה, כולל טיפול בקישורים אוניברסליים או באפליקציית הדפדפן שמוגדרת כברירת מחדל. ספריית SFSafariViewController
היא גם אפשרות נתמכת.
org_internal
מזהה לקוח OAuth בבקשה הוא חלק מפרויקט שמגביל את הגישה לחשבונות Google ב ארגון ספציפי ב-Google Cloud. מידע נוסף על אפשרות ההגדרה הזו זמין בקטע סוג המשתמש במאמר העזרה בנושא הגדרת מסך הסכמה ל-OAuth.
invalid_client
סוד הלקוח של OAuth שגוי. כדאי לבדוק את ההגדרה של לקוח OAuth, כולל מזהה הלקוח והסוד שמשמשים לבקשה הזו.
deleted_client
לקוח ה-OAuth שמשמש לשליחת הבקשה נמחק. המחיקה יכולה להתבצע באופן ידני או אוטומטי במקרה של לקוחות שלא נעשה בהם שימוש . אפשר לשחזר לקוחות שנמחקו תוך 30 יום ממועד המחיקה. מידע נוסף
invalid_grant
כשמרעננים אסימון גישה או משתמשים בהרשאה מצטברת, יכול להיות שתוקף האסימון פג או שהוא בוטל. מאמתים שוב את המשתמש ומבקשים ממנו להביע הסכמה כדי לקבל טוקנים חדשים. אם השגיאה הזו ממשיכה להופיע, צריך לוודא שהאפליקציה הוגדרה בצורה נכונה ושהשתמשתם בטוקנים ובפרמטרים הנכונים בבקשה. אחרת, יכול להיות שחשבון המשתמש נמחק או הושבת.
redirect_uri_mismatch
הפרמטר redirect_uri
שמועבר בבקשת ההרשאה לא תואם לכתובת ה-URI המורשית להפניה אוטומטית עבור מזהה הלקוח של OAuth. בודקים את כתובות ה-URI המורשות להפניה אוטומטית ב-
.
הפרמטר redirect_uri
עשוי להתייחס לתהליך OAuth out-of-band (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
כדי להחליף קוד הרשאה בטוקן גישה, משתמשים בשיטה fetchAccessTokenWithAuthCode
:
$access_token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
Python
בדף הקריאה החוזרת, משתמשים בספרייה google-auth
כדי לאמת את התגובה של שרת ההרשאות. לאחר מכן, משתמשים בשיטה 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
כדי לאמת את התגובה של שרת ההרשאות. משתמשים בשיטה authorizer.handle_auth_callback_deferred
כדי לשמור את קוד ההרשאה ולחזור לכתובת ה-URL שבה נשלחה במקור בקשת ההרשאה. הפעולה הזו דוחה את החלפת הקוד על ידי אחסון התוצאות באופן זמני בסשן של המשתמש.
target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request) redirect target_url
Node.js
כדי להחליף קוד הרשאה באסימון גישה, משתמשים בשיטה 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 |
מזהה הלקוח שהתקבל מ . |
client_secret |
סוד הלקוח שהתקבל מ- . |
code |
קוד ההרשאה שמוחזר מהבקשה הראשונית. |
grant_type |
כמו שמוגדר במפרט של OAuth 2.0, הערך של השדה הזה צריך להיות authorization_code . |
redirect_uri |
אחת מכתובות ה-URI להפניה אוטומטית שמופיעות בפרויקט ב- עבור 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.
|
refresh_token_expires_in |
משך החיים שנותר לאסימון הרענון בשניות. הערך הזה מוגדר רק כשהמשתמש מעניק גישה מוגבלת בזמן. |
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: בדיקה של היקפי ההרשאות שהמשתמשים העניקו
כשמבקשים כמה הרשאות (היקפי הרשאות), יכול להיות שהמשתמשים לא יאשרו לאפליקציה שלכם גישה לכולן. האפליקציה צריכה לאמת אילו היקפי הרשאות ניתנו בפועל, ולטפל בצורה תקינה במצבים שבהם נדחות הרשאות מסוימות. בדרך כלל, האפליקציה משביתה את התכונות שמסתמכות על היקפי ההרשאות שנדחו.
עם זאת, יש מקרים חריגים. אפליקציות של Google Workspace Enterprise עם הענקת הרשאות ברמת הדומיין, או אפליקציות שמסומנות כמהימנות, לא יציגו את מסך ההסכמה להרשאות המפורטות. במקרה של האפליקציות האלה, המשתמשים לא יראו את מסך ההסכמה עם ההרשאות המפורטות. במקום זאת, האפליקציה תקבל את כל ההיקפים המבוקשים או אף אחד מהם.
מידע מפורט יותר זמין במאמר בנושא איך מטפלים בהרשאות גרנולריות.
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, שמוצגים כרשימה של מחרוזות תלויות-אותיות רישיות שמופרדות ברווחים.
לדוגמה, התגובה הבאה של אסימון גישה לדוגמה מציינת שהמשתמש העניק לאפליקציה שלכם גישה להרשאות קריאה בלבד של פעילות ב-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 API, צריך לבצע את השלבים הבאים:
- אם אתם צריכים להחיל אסימון גישה על אובייקט
Google\Client
חדש – לדוגמה, אם שמרתם את אסימון הגישה בסשן משתמש – השתמשו בשיטהsetAccessToken
:$client->setAccessToken($access_token);
- יוצרים אובייקט שירות עבור ה-API שרוצים להפעיל. כדי ליצור אובייקט שירות, צריך לספק אובייקט
Google\Client
מורשה לקונסטרוקטור של ה-API שאליו רוצים להתקשר. לדוגמה, כדי להפעיל את Drive API:$drive = new Google\Service\Drive($client);
- שולחים בקשות לשירות ה-API באמצעות
הממשק שמסופק על ידי אובייקט השירות.
לדוגמה, כדי להציג את רשימת הקבצים ב-Google Drive של המשתמש המאומת:
$files = $drive->files->listFiles(array());
Python
אחרי קבלת אסימון גישה, האפליקציה יכולה להשתמש באסימון הזה כדי לאשר בקשות API מטעם חשבון משתמש או חשבון שירות מסוים. משתמשים בפרטי ההרשאה הספציפיים למשתמש כדי ליצור אובייקט שירות עבור ה-API שאליו רוצים לשלוח קריאה, ואז משתמשים באובייקט הזה כדי לשלוח בקשות API עם הרשאה.
- יוצרים אובייקט שירות עבור ה-API שרוצים להפעיל. כדי ליצור אובייקט שירות, קוראים לשיטה
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
של שאילתה או ערך של Authorization
כותרת HTTP Bearer
. כשניתן, עדיף להשתמש בכותרת HTTP, כי מחרוזות שאילתה נוטות להיות גלויות ביומני השרת. ברוב המקרים, אפשר להשתמש בספריית לקוח כדי להגדיר את הקריאות ל-Google APIs (לדוגמה, כשקוראים ל-Drive Files API).
אתם יכולים להתנסות בכל ממשקי Google APIs ולראות את היקפי ההרשאות שלהם ב-OAuth 2.0 Playground.
דוגמאות ל-HTTP GET
קריאה לנקודת הקצה
drive.files
(ה-API של קובצי Drive) באמצעות כותרת ה-HTTP Authorization: Bearer
עשויה להיראות כך: שימו לב: צריך לציין טוקן גישה משלכם:
GET /drive/v2/files HTTP/1.1 Host: www.googleapis.com Authorization: Bearer access_token
זוהי קריאה לאותו API עבור המשתמש המאומת באמצעות פרמטר מחרוזת השאילתה access_token
:
GET https://www.googleapis.com/drive/v2/files?access_token=access_token
curl
דוגמאות
אפשר לבדוק את הפקודות האלה באמצעות אפליקציית שורת הפקודה curl
. הנה דוגמה לשימוש באפשרות של כותרת HTTP (מומלץ):
curl -H "Authorization: Bearer access_token" https://www.googleapis.com/drive/v2/files
או לחלופין, האפשרות של פרמטר מחרוזת השאילתה:
curl https://www.googleapis.com/drive/v2/files?access_token=access_token
דוגמה מלאה
בדוגמה הבאה מודפסת רשימה של קבצים ב-Google Drive של משתמש בפורמט JSON, אחרי שהמשתמש מאמת את עצמו ומביע הסכמה לאפליקציה לגשת למטא-נתונים של 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 הזו, אמורים להופיע חמישה קישורים:
- קריאה ל-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
בדוגמה הזו נעשה שימוש ב-framework 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 framework ובספריית 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 להפניה אוטומטית חייבות לעמוד בכללים הבאים. ההגדרות של domain, host, path, query, scheme ו-userinfo שמוזכרות בהמשך מופיעות בסעיף 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, אובייקט הלקוח מרענן את אסימון הגישה לפי הצורך, כל עוד הגדרתם את האובייקט הזה לגישה אופליין.
- אם אתם לא משתמשים בספריית לקוח, אתם צריכים להגדיר את פרמטר השאילתה
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 מטעם המשתמש כשהוא במצב אופליין. אובייקט הלקוח ירענן את אסימון הגישה לפי הצורך.
תוקף הטוקנים של הגישה פג. הספרייה הזו תשתמש אוטומטית באסימון רענון כדי לקבל אסימון גישה חדש אם התוקף של אסימון הגישה עומד לפוג. דרך קלה לוודא שתמיד מאחסנים את האסימונים העדכניים ביותר היא להשתמש באירוע tokens:
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
כדי לרענן אסימון גישה, האפליקציה שולחת בקשת HTTPS POST
לשרת ההרשאות של 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
כדי לבטל אסימון באופן פרוגרמטי, שולחים בקשת HTTPS POST לנקודת הקצה /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 במהלך תהליך בקשת ההסכמה, ומאפשרת למשתמשים להעניק גישה לתקופה מוגבלת. דוגמה לכך היא 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
מידע נוסף על הטמעה של הגנה על כל החשבונות ורשימה מלאה של האירועים הזמינים מופיע במאמר הגנה על חשבונות משתמשים באמצעות הגנה על כל החשבונות .