שימוש ב-OAuth 2.0 לאפליקציות של שרת אינטרנט

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

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

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

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

ספריות לקוח

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

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

ספריות הלקוח של Google API לאפליקציות בצד השרת זמינות בשפות הבאות:

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

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

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

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

  1. Open the API Library ב Google API Console.
  2. If prompted, select a project, or create a new one.
  3. משתמשים בדף Library כדי למצוא את YouTube Data API ולהפעיל אותו. מוצאים את כל ממשקי ה-API האחרים שבהם האפליקציה תשתמש ומפעילים אותם גם כן.

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

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

  1. Go to the Credentials page.
  2. לוחצים על Create credentials (יצירת פרטי כניסה) > OAuth client ID (מזהה לקוח OAuth).
  3. בוחרים את סוג האפליקציה Web application.
  4. ממלאים את הטופס ולוחצים על 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, מומלץ לזהות את היקפי ההרשאות שאליהם האפליקציה תצטרך גישה.

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

ב-YouTube Data API גרסה 3 נעשה שימוש בהיקפי הגישה הבאים:

טווחים
https://www.googleapis.com/auth/youtubeניהול חשבון YouTube שלך
https://www.googleapis.com/auth/youtube.channel-memberships.creatorהצגת רשימה מעודכנת של החברים הפעילים במועדון החברים של הערוץ, הרמה הנוכחית שלהם והתאריך שבו הם הצטרפו למועדון
https://www.googleapis.com/auth/youtube.force-sslהצגה, עריכה ומחיקה לצמיתות של סרטונים, דירוגים, תגובות וכתוביות ב-YouTube
https://www.googleapis.com/auth/youtube.readonlyהצגת חשבון YouTube שלך
https://www.googleapis.com/auth/youtube.uploadניהול הסרטונים שלך ב-YouTube
https://www.googleapis.com/auth/youtubepartnerהצגה וניהול של הנכסים והתכנים הקשורים שלך ב-YouTube
https://www.googleapis.com/auth/youtubepartner-channel-auditהצגת מידע פרטי של ערוץ YouTube שלך הרלוונטי בתהליך הביקורת של שותף YouTube.

במסמך היקפי הרשאות 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
  • מסגרת לאפליקציות אינטרנט ב-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 שמחייבת הרשאה מהמשתמש.

הרשימה הבאה מסכמת במהירות את השלבים האלה:

  1. האפליקציה מזהה את ההרשאות הנחוצות לה.
  2. האפליקציה מפנה את המשתמש אל Google יחד עם רשימת ההרשאות המבוקשות.
  3. המשתמש מחליט אם להעניק את ההרשאות לאפליקציה.
  4. האפליקציה תדע מה המשתמש החליט.
  5. אם המשתמש העניק את ההרשאות המבוקשות, האפליקציה מאחזרת את האסימונים הנדרשים לשליחת בקשות 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.

לדוגמה, הקוד הזה מבקש גישה אופליין לניהול חשבון YouTube של משתמש:

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_YOUTUBE::YOUTUBE_FORCE_SSL);

// 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.

לדוגמה, הקוד הזה מבקש גישה אופליין לניהול חשבון YouTube של משתמש:

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/youtube.force-ssl'])

# 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.

לדוגמה, הקוד הזה מבקש גישה אופליין לניהול חשבון YouTube של משתמש:

require 'googleauth'
require 'googleauth/web_user_authorizer'
require 'googleauth/stores/redis_token_store'

require 'google/apis/youtube_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 
scope = 'https://www.googleapis.com/auth/youtube.force-ssl'

# 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 YouTube API
const scopes = [
  'https://www.googleapis.com/auth/youtube.force-ssl'
];

// 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 מורשה להפניה אוטומטית של client_id שצוין, תופיע הודעת השגיאה redirect_uri_mismatch.

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

response_type חובה

הפונקציה קובעת אם נקודת הקצה של Google OAuth 2.0 מחזירה קוד הרשאה.

מגדירים את ערך הפרמטר ל-code לאפליקציות של שרת אינטרנט.

scope חובה

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

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

ב-YouTube Data API גרסה 3 נעשה שימוש בהיקפי הגישה הבאים:

טווחים
https://www.googleapis.com/auth/youtubeניהול חשבון YouTube שלך
https://www.googleapis.com/auth/youtube.channel-memberships.creatorהצגת רשימה מעודכנת של החברים הפעילים במועדון החברים של הערוץ, הרמה הנוכחית שלהם והתאריך שבו הם הצטרפו למועדון
https://www.googleapis.com/auth/youtube.force-sslהצגה, עריכה ומחיקה לצמיתות של סרטונים, דירוגים, תגובות וכתוביות ב-YouTube
https://www.googleapis.com/auth/youtube.readonlyהצגת חשבון YouTube שלך
https://www.googleapis.com/auth/youtube.uploadניהול הסרטונים שלך ב-YouTube
https://www.googleapis.com/auth/youtubepartnerהצגה וניהול של הנכסים והתכנים הקשורים שלך ב-YouTube
https://www.googleapis.com/auth/youtubepartner-channel-auditהצגת מידע פרטי של ערוץ YouTube שלך הרלוונטי בתהליך הביקורת של שותף YouTube.

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

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

access_type מומלץ

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

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

state מומלץ

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

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

include_granted_scopes אופציונלי

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

login_hint אופציונלי

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

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

prompt אופציונלי

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

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

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

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

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

PHP

  1. יוצרים כתובת URL לבקשת גישה משרת OAuth 2.0 של Google:
    $auth_url = $client->createAuthUrl();
  2. מפנים את המשתמש אל $auth_url:
    header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));

Python

בדוגמה הזו מוסבר איך להפנות את המשתמש לכתובת ה-URL של ההרשאה באמצעות מסגרת אפליקציות האינטרנט של Flask:

return flask.redirect(authorization_url)

Ruby

  1. יוצרים כתובת URL לבקשת גישה משרת OAuth 2.0 של Google:
    auth_uri = authorizer.get_authorization_url(request: request)
  2. מפנים את המשתמש אל auth_uri.

Node.js

  1. משתמשים בכתובת ה-URL שנוצרה, authorizationUrl, משלב 1 שיטה generateAuthUrl כדי לבקש גישה משרת OAuth 2.0 של Google.
  2. מפנים את המשתמש אל authorizationUrl.
    res.redirect(authorizationUrl);

HTTP/REST

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

כתובת ה-URL לדוגמה שבהמשך מבקשת גישה אופליין (access_type=offline) להיקף שמאפשר גישה לצפייה בחשבון YouTube של המשתמש. הוא משתמש באישור מצטבר כדי לוודא שטוקן הגישה החדש מכסה את כל ההיקפים שהמשתמש העניק לאפליקציה גישה אליהם בעבר. כתובת ה-URL גם מגדירה ערכים לפרמטר redirect_uri, לפרמטר response_type ולפרמטר client_id הנדרשים, וגם לפרמטר state. כתובת ה-URL מכילה מעברי שורה ורווחים כדי לשפר את הקריאוּת שלה.

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.readonly&
 access_type=offline&
 include_granted_scopes=true&
 state=state_parameter_passthrough_value&
 redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
 response_type=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%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.readonly&
 access_type=offline&
 include_granted_scopes=true&
 state=state_parameter_passthrough_value&
 redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
 response_type=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/youtube.force-ssl'],
    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/youtube.force-ssl",
  "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();

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}

Ruby

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

# User authorized the request. Now, check which scopes were granted.
if credentials.scope.include?(Google::Apis::YoutubeV3::AUTH_YOUTUBE_FORCE_SSL)
  # User authorized permission to see, edit, and permanently delete the
  # YouTube videos, ratings, comments and captions.
  # Calling the APIs, etc
else
  # User didn't authorize the 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/youtube.force-ssl'))
{
  // User authorized permission to see, edit, and permanently delete the
  // YouTube videos, ratings, comments and captions.
  // Calling the APIs, etc.
}
else
{
  // User didn't authorize read-only Drive activity permission.
  // Update UX and application accordingly
}

HTTP/REST

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

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

  {
    "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
    "expires_in": 3920,
    "token_type": "Bearer",
    "scope": "https://www.googleapis.com/auth/youtube.force-ssl",
    "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
  }

קריאה ל-Google APIs

PHP

כדי להשתמש באסימון הגישה כדי לבצע קריאה ל-Google APIs, מבצעים את השלבים הבאים:

  1. אם צריך להחיל אסימון גישה על אובייקט Google\Client חדש – לדוגמה, אם שמרתם את אסימון הגישה בסשן של משתמש – צריך להשתמש בשיטה setAccessToken:
    $client->setAccessToken($access_token);
  2. יוצרים אובייקט שירות ל-API שרוצים להפעיל. כדי ליצור אובייקט שירות, מספקים אובייקט Google\Client מורשה למבנה ה-constructor של ה-API שרוצים לקרוא אליו. לדוגמה, כדי לקרוא ל-YouTube Data API:
    $youtube = new Google_Service_YouTube($client);
  3. שולחים בקשות לשירות ה-API באמצעות הממשק שסופק על ידי אובייקט השירות. לדוגמה, כדי לאחזר נתונים על ערוץ YouTube של המשתמש המורשה:
    $channel = $youtube->channels->listChannels('snippet', array('mine' => $mine));

Python

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

  1. יוצרים אובייקט שירות ל-API שרוצים להפעיל. כדי ליצור אובייקט שירות, צריך לבצע קריאה ל-method‏ build בספרייה googleapiclient.discovery עם השם והגרסה של ה-API ופרטי הכניסה של המשתמש: לדוגמה, כדי לקרוא לגרסה 3 של YouTube Data API:
    from googleapiclient.discovery import build
    
    youtube = build('youtube', 'v3', credentials=credentials)
  2. שולחים בקשות לשירות ה-API באמצעות הממשק שסופק על ידי אובייקט השירות. לדוגמה, כדי לאחזר נתונים על ערוץ YouTube של המשתמש המורשה:
    channel = youtube.channels().list(mine=True, part='snippet').execute()

Ruby

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

  1. יוצרים אובייקט שירות ל-API שרוצים להפעיל. לדוגמה, כדי לקרוא לגרסה 3 של YouTube Data API:
    youtube = Google::Apis::YoutubeV3::YouTubeService.new
  2. מגדירים את פרטי הכניסה בשירות:
    youtube.authorization = credentials
  3. שולחים בקשות לשירות ה-API באמצעות הממשק שסופק על ידי אובייקט השירות. לדוגמה, כדי לאחזר נתונים על ערוץ YouTube של המשתמש המורשה:
    channel = youtube.list_channels(part, :mine => mine)

לחלופין, אפשר לספק הרשאה לפי שיטות על ידי העברת הפרמטר options לשיטה:

channel = youtube.list_channels(part, :mine => mine, options: { authorization: auth_client })

Node.js

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

const { google } = require('googleapis');

// Example of using YouTube API to list channels.
var service = google.youtube('v3');
service.channels.list({
  auth: oauth2Client,
  part: 'snippet,contentDetails,statistics',
  forUsername: 'GoogleDevelopers'
}, function (err, response) {
  if (err) {
    console.log('The API returned an error: ' + err);
    return;
  }
  var channels = response.data.items;
  if (channels.length == 0) {
    console.log('No channel found.');
  } else {
    console.log('This channel\'s ID is %s. Its title is \'%s\', and ' +
      'it has %s views.',
      channels[0].id,
      channels[0].snippet.title,
      channels[0].statistics.viewCount);
  }
});

HTTP/REST

אחרי שהאפליקציה מקבלת אסימון גישה, אפשר להשתמש באסימון כדי לבצע קריאות ל-Google API מטעם חשבון משתמש נתון, אם היקפי הגישה הנדרשים ל-API הוקצו. כדי לעשות זאת, צריך לכלול את אסימון הגישה בבקשה ל-API באמצעות פרמטר של שאילתה access_token או ערך Bearer בכותרת Authorization של HTTP. כשהדבר אפשרי, עדיף להשתמש בכותרת ה-HTTP, כי מחרוזות השאילתות נוטים להיות גלויות ביומנים של השרת. ברוב המקרים אפשר להשתמש בספריית לקוח כדי להגדיר את הקריאות ל-Google APIs (לדוגמה, כשקוראים ל-YouTube Data API).

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

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

דוגמאות לבקשות HTTP GET

קריאה לנקודת הקצה youtube.channels (YouTube Data API) באמצעות הכותרת Authorization: Bearer של HTTP עשויה להיראות כך: חשוב לשים לב שצריך לציין את טוקן הגישה שלכם:

GET /youtube/v3/channels?part=snippet&mine=true HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer access_token

זוהי קריאה לאותו ממשק API עבור המשתמש המאומת באמצעות הפרמטר access_token של מחרוזת השאילתה:

GET https://www.googleapis.com/youtube/v3/channels?access_token=access_token&part=snippet&mine=true

curl דוגמאות

אפשר לבדוק את הפקודות האלה באמצעות אפליקציית שורת הפקודה curl. הנה דוגמה שמשתמשת באפשרות של כותרת ה-HTTP (האפשרות המועדפת):

curl -H "Authorization: Bearer access_token" https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true

לחלופין, אפשר לבחור באפשרות 'פרמטר של מחרוזת שאילתה':

curl https://www.googleapis.com/youtube/v3/channels?access_token=access_token&part=snippet&mine=true

דוגמה מלאה

בדוגמה הבאה מודפס אובייקט בפורמט JSON שמציג מידע על ערוץ YouTube של משתמש, אחרי שהמשתמש מאמת את האפליקציה ומעניק לה הרשאה לנהל את חשבון YouTube שלו.

PHP

כדי להריץ את הדוגמה הזו:

  1. ב- API Console, מוסיפים את כתובת ה-URL של המכונה המקומית לרשימת כתובות ה-URL להפניה אוטומטית. לדוגמה, מוסיפים את http://localhost:8080.
  2. יוצרים ספרייה חדשה ועוברים אליה. לדוגמה:
    mkdir ~/php-oauth2-example
    cd ~/php-oauth2-example
  3. מתקינים את ספריית הלקוח של Google API ל-PHP באמצעות Composer:
    composer require google/apiclient:^2.15.0
  4. יוצרים את הקבצים index.php ו-oauth2callback.php עם התוכן הבא.
  5. מריצים את הדוגמה באמצעות שרת האינטרנט המובנה לבדיקה של 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']);
  
  $youtube = new Google_Service_YouTube($client);
  $channel = $youtube->channels->listChannels('snippet', array('mine' => $mine));
  echo json_encode($channel);
  
}
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_YOUTUBE::YOUTUBE_FORCE_SSL);

// 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();
  
  $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 הזו, אמורים להופיע חמישה קישורים:

  • בדיקת בקשת API: הקישור הזה מפנה לדף שמנסה להריץ בקשת 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/youtube.force-ssl']
API_SERVICE_NAME = 'youtube'
API_VERSION = 'v3'

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('/test')
def test_api_request():
  if 'credentials' not in flask.session:
  return flask.redirect('authorize')

  # Load credentials from the session.
  credentials = google.oauth2.credentials.Credentials(
    **flask.session['credentials'])

  youtube = googleapiclient.discovery.build(
    API_SERVICE_NAME, API_VERSION, credentials=credentials)

  channel = youtube.channels().list(mine=True, part='snippet').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(**channel)
@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
  
  flask.session['credentials'] = credentials_to_dict(credentials)

  return flask.redirect(flask.url_for('test_api_request'))
  

@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 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/youtube_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 retrieving data about the user's YouTube channel.
  scope = 'Google::Apis::YoutubeV3::AUTH_YOUTUBE_FORCE_SSL'

  # 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 read-only YouTube Data API permission.
  # Example of using YouTube Data API to list user's YouTube channel
  youtube = Google::Apis::YoutubeV3::YouTubeService.new
  channel = youtube.list_channels(part, :mine => mine, options: { authorization: auth_client })
  
  "<pre>#{JSON.pretty_generate(channel.to_h)}</pre>"
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

כדי להריץ את הדוגמה הזו:

  1. בקובץ API Console, מוסיפים את כתובת ה-URL של המכונה המקומית לרשימת כתובות ה-URL להפניה אוטומטית. לדוגמה, מוסיפים את http://localhost.
  2. חשוב לוודא שהתקנתם את הגרסה הנוכחית, את הגרסה הפעילה של LTS או את הגרסה של LTS לתחזוקה של Node.js.
  3. יוצרים ספרייה חדשה ועוברים אליה. לדוגמה:
    mkdir ~/nodejs-oauth2-example
    cd ~/nodejs-oauth2-example
  4. מתקינים את ספריית הלקוח של Google API ל-Node.js באמצעות npm:
    npm install googleapis
  5. יוצרים את הקבצים main.js עם התוכן הבא.
  6. מריצים את הדוגמה:
    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 YouTube API
const scopes = [
  'https://www.googleapis.com/auth/youtube.force-ssl'
];

/* 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;
      
      // Example of using YouTube API to list channels.
      var service = google.youtube('v3');
      service.channels.list({
        auth: oauth2Client,
        part: 'snippet,contentDetails,statistics',
        forUsername: 'GoogleDevelopers'
      }, function (err, response) {
        if (err) {
          console.log('The API returned an error: ' + err);
          return;
        }
        var channels = response.data.items;
        if (channels.length == 0) {
          console.log('No channel found.');
        } else {
          console.log('This channel\'s ID is %s. Its title is \'%s\', and ' +
            'it has %s views.',
            channels[0].id,
            channels[0].snippet.title,
            channels[0].statistics.viewCount);
        }
      });
    }
  });

  // 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 YouTube API
SCOPE = 'https://www.googleapis.com/auth/youtube.force-ssl'

# 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: 
    headers = {'Authorization': 'Bearer {}'.format(credentials['access_token'])}
    req_uri = 'https://www.googleapis.com/youtube/v3/channels/list'
    r = requests.get(req_uri, headers=headers)
    return r.text 

@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 פטורות מהכלל הזה.

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

    כתובות URI להפניה אוטומטית לא יכולות להכיל את רכיב המשנה userinfo.

    נתיב

    מזהי URI להפניה אוטומטית לא יכולים להכיל מעבר נתיב (נקרא גם חזרה אחורה בספרייה), שמיוצג על ידי “/..” או “\..” או על ידי קידוד של כתובת ה-URL שלהם.

    שאילתה

    מזהי URI להפניה אוטומטית לא יכולים לכלול הפניות אוטומטיות פתוחות.

    Fragment

    מזהי URI להפניה אוטומטית לא יכולים להכיל את רכיב המקטע.

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

    הרשאה מצטברת

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

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

    במקרה כזה, יכול להיות שבזמן הכניסה לאפליקציה לא תהיה צורך בגישה להיקפים מסוימים או שהאפליקציה לא תבקש גישה להיקפים כאלה. עם זאת, אם המשתמש ניסה לדרג סרטון, להוסיף סרטון לפלייליסט או לבצע פעולה אחרת ב-YouTube, האפליקציה עשויה לבקש גישה להיקף https://www.googleapis.com/auth/youtube.force-ssl. באופן דומה, האפליקציה עשויה לבקש גישה להיקף https://www.googleapis.com/auth/calendar אם המשתמש ניסה להוסיף אירוע ביומן.

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

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

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

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

    GET https://accounts.google.com/o/oauth2/v2/auth?
      scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyt-analytics.readonly&
      access_type=offline&
      state=security_token%3D138rk%3Btarget_url%3Dhttp...index&
      redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
      response_type=code&
      client_id=client_id&
      include_granted_scopes=true
    
          

    Refreshing an access token (offline access)

    Access tokens periodically expire and become invalid credentials for a related API request. You can refresh an access token without prompting the user for permission (including when the user is not present) if you requested offline access to the scopes associated with the token.

    • If you use a Google API Client Library, the client object refreshes the access token as needed as long as you configure that object for offline access.
    • If you are not using a client library, you need to set the access_type HTTP query parameter to offline when redirecting the user to Google's OAuth 2.0 server. In that case, Google's authorization server returns a refresh token when you exchange an authorization code for an access token. Then, if the access token expires (or at any other time), you can use a refresh token to obtain a new access token.

    Requesting offline access is a requirement for any application that needs to access a Google API when the user is not present. For example, an app that performs backup services or executes actions at predetermined times needs to be able to refresh its access token when the user is not present. The default style of access is called online.

    Server-side web applications, installed applications, and devices all obtain refresh tokens during the authorization process. Refresh tokens are not typically used in client-side (JavaScript) web applications.

    PHP

    If your application needs offline access to a Google API, set the API client's access type to 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",
      "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

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