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

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

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

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

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

ספריות לקוח

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

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

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

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

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

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

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

  1. Open the API Library ב Google API Console.
  2. If prompted, select a project, or create a new one.
  3. ב- API Library מפורטים כל ממשקי ה-API הזמינים, שמקובצים לפי משפחת מוצרים ופופולריות. אם ממשק ה-API שרוצים להפעיל לא מופיע ברשימה, אפשר לחפש אותו או ללחוץ על הצגת הכול במשפחת המוצרים שאליה הוא שייך.
  4. בוחרים את ה-API שרוצים להפעיל ולוחצים על הלחצן הפעלה.
  5. If prompted, enable billing.
  6. If prompted, read and accept the API's Terms of Service.

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

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

  1. Go to the Credentials page.
  2. לוחצים על Create credentials > מזהה לקוח 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, מומלץ לזהות את היקפי ההרשאות שאליהם האפליקציה תצטרך גישה.

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

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

דרישות ספציפיות לשפה

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

PHP

כדי להריץ את דוגמאות הקוד של PHP במסמך הזה, צריך:

  • PHP מגרסה 5.6 ואילך עם ממשק שורת הפקודה (CLI) ותוסף JSON מותקנים.
  • הכלי לניהול יחסי התלות של Composer.
  • ספריית הלקוח של Google APIs ל-PHP:

    composer require google/apiclient:^2.10

Python

כדי להריץ את דוגמאות הקוד של Python במסמך הזה, צריך:

  • Python בגרסה 2.6 ומעלה
  • הכלי לניהול חבילות pip.
  • ספריית הלקוח של 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

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.

לדוגמה, הקוד הזה מבקש גישה לקריאה בלבד במצב אופליין ל-Google Drive של משתמש:

$client = new Google\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);

// 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'])

# 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 'google/apis/drive_v3'
require "googleauth"
require 'googleauth/stores/redis_token_store'

client_id = Google::Auth::ClientId.from_file('/path/to/client_secret.json')
scope = 'https://www.googleapis.com/auth/drive.metadata.readonly'
token_store = Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new)
authorizer = Google::Auth::WebUserAuthorizer.new(client_id, scope, token_store, '/oauth2callback')

האפליקציה משתמשת באובייקט הלקוח כדי לבצע פעולות 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 read-only Drive activity.
const scopes = [
  'https://www.googleapis.com/auth/drive.metadata.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 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 מציגה למשתמש.

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

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

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(login_hint: user_id, 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 לדוגמה, עם הפסקות שורות ורווחים לשיפור הקריאוּת.

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly&
 access_type=offline&
 include_granted_scopes=true&
 response_type=code&
 state=state_parameter_passthrough_value&
 redirect_uri=https%3A//oauth2.example.com/code&
 client_id=client_id

לאחר יצירת כתובת ה-URL של הבקשה, הפנה את המשתמש אליה.

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

שלב 3: Google מבקשת מהמשתמש להביע הסכמה

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

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

שגיאות

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

admin_policy_enforced

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

disallowed_useragent

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

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

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly&
 access_type=offline&
 include_granted_scopes=true&
 response_type=code&
 state=state_parameter_passthrough_value&
 redirect_uri=https%3A//oauth2.example.com/code&
 client_id=client_id

אחרי שתשלימו את התהליך ב-OAuth 2.0, תופנו אל http://localhost/oauth2callback, שככל הנראה תניב שגיאת 404 NOT FOUND, אלא אם המחשב המקומי יציג קובץ בכתובת הזו. בשלב הבא נספק פרטים נוספים על המידע שמוחזר ב-URI כשהמשתמש מופנה חזרה לאפליקציה.

שלב 5: החלפת קוד ההרשאה באסימוני רענון וגישה

אחרי ששרת האינטרנט מקבל את קוד ההרשאה, הוא יכול להמיר את קוד ההרשאה לטוקן גישה.

PHP

כדי להמיר קוד הרשאה לטוקן גישה, משתמשים ב-method‏ authenticate:

$client->authenticate($_GET['code']);

אפשר לאחזר את אסימון הגישה באמצעות השיטה getAccessToken:

$access_token = $client->getAccessToken();

Python

בדף הקריאה החוזרת, משתמשים בספרייה google-auth כדי לאמת את התגובה של שרת ההרשאות. אחרי זה, משתמשים ב-method flow.fetch_token כדי להחליף את קוד ההרשאה בתגובה הזו באסימון גישה:

state = flask.session['state']
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
    'client_secret.json',
    scopes=['https://www.googleapis.com/auth/drive.metadata.readonly'],
    state=state)
flow.redirect_uri = flask.url_for('oauth2callback', _external=True)

authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response)

# Store the credentials in the session.
# ACTION ITEM for developers:
#     Store user's access and refresh tokens in your data store if
#     incorporating this code into your real app.
credentials = flow.credentials
flask.session['credentials'] = {
    'token': credentials.token,
    'refresh_token': credentials.refresh_token,
    'token_uri': credentials.token_uri,
    'client_id': credentials.client_id,
    'client_secret': credentials.client_secret,
    'scopes': credentials.scopes}

Ruby

בדף הקריאה החוזרת, משתמשים בספרייה googleauth כדי לאמת את התשובה של שרת ההרשאות. משתמשים ב-method‏ authorizer.handle_auth_callback_deferred כדי לשמור את קוד ההרשאה ולהפנות חזרה לכתובת ה-URL שבה נשלחה במקור בקשת ההרשאה. כך ניתן למנוע את החלפת הקוד על ידי הסתרה זמנית של התוצאות בסשן של המשתמש.

  target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request)
  redirect target_url

Node.js

כדי להמיר קוד הרשאה לטוקן גישה, משתמשים ב-method‏ getToken:

const url = require('url');

// Receive the callback from Google's OAuth 2.0 server.
app.get('/oauth2callback', async (req, res) => {
  let q = url.parse(req.url, true).query;

  if (q.error) { // An error response e.g. error=access_denied
    console.log('Error:' + q.error);
  } else if (q.state !== req.session.state) { //check state value
    console.log('State mismatch. Possible CSRF attack');
    res.end('State mismatch. Possible CSRF attack');
  } else { // Get access and refresh tokens (if access_type is offline)

    let { tokens } = await oauth2Client.getToken(q.code);
    oauth2Client.setCredentials(tokens);
});

HTTP/REST

כדי להמיר קוד הרשאה לאסימון גישה, צריך להפעיל את נקודת הקצה https://oauth2.googleapis.com/token ולהגדיר את הפרמטרים הבאים:

שדות
client_id מזהה הלקוח שהתקבל מ- API Console Credentials page.
client_secret סוד הלקוח שהתקבל מ- API Console Credentials page.
code קוד ההרשאה שהוחזר מהבקשה הראשונית.
grant_type כפי שמוגדר במפרט של OAuth 2.0, הערך של השדה הזה צריך להיות authorization_code.
redirect_uri אחד מ-URI להפניה אוטומטית שמפורטים לפרויקט שלכם ב- API Console Credentials page עבור client_id הנתון.

בקטע הקוד הבא מוצגת בקשה לדוגמה:

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https%3A//oauth2.example.com/code&
grant_type=authorization_code

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

התגובה כוללת את השדות הבאים:

שדות
access_token האסימון שהאפליקציה שלכם שולחת כדי לאשר בקשת Google API.
expires_in משך החיים שנותר של אסימון הגישה, בשניות.
refresh_token אסימון שאפשר להשתמש בו כדי לקבל אסימון גישה חדש. אסימוני הרענון בתוקף עד שהמשתמש מבטל את הגישה. שוב, השדה הזה מופיע בתגובה הזו רק אם מגדירים את הפרמטר access_type ל-offline בבקשה הראשונית לשרת ההרשאות של Google.
scope היקפי הגישה שהוענקו על ידי access_token, מוצגים כרשימה של מחרוזות תלויות-רישיות שמופרדות ברווחים.
token_type סוג הטוקן שהוחזר. בשלב זה, הערך של השדה הזה תמיד מוגדר ל-Bearer.

קטע הקוד הבא מציג תגובה לדוגמה:

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

שגיאות

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

invalid_grant

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

קריאה ל-Google APIs

PHP

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

  1. אם צריך להחיל אסימון גישה על אובייקט Google\Client חדש – לדוגמה, אם שמרתם את אסימון הגישה בסשן של משתמש – צריך להשתמש בשיטה setAccessToken:
    $client->setAccessToken($access_token);
  2. יוצרים אובייקט שירות ל-API שאליו רוצים לקרוא. כדי ליצור אובייקט שירות, מספקים אובייקט Google\Client מורשה למבנה ה-constructor של ה-API שרוצים לקרוא אליו. לדוגמה, כדי לקרוא ל-Drive API:
    $drive = new Google\Service\Drive($client);
  3. שולחים בקשות לשירות ה-API באמצעות הממשק שסופק על ידי אובייקט השירות. לדוגמה, כדי להציג את הקבצים ב-Google Drive של המשתמש המאומת:
    $files = $drive->files->listFiles(array())->getItems();

Python

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

  1. יוצרים אובייקט שירות ל-API שרוצים להפעיל. כדי ליצור אובייקט שירות, צריך לבצע קריאה ל-method‏ build בספרייה googleapiclient.discovery עם השם והגרסה של ה-API ופרטי הכניסה של המשתמש: לדוגמה, כדי לקרוא לגרסה 3 של Drive API:
    from googleapiclient.discovery import build
    
    drive = build('drive', 'v2', credentials=credentials)
  2. שולחים בקשות לשירות ה-API באמצעות הממשק של אובייקט השירות. לדוגמה, כדי להציג את הקבצים ב-Google Drive של המשתמש המאומת:
    files = drive.files().list().execute()

Ruby

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

  1. יוצרים אובייקט שירות ל-API שרוצים להפעיל. לדוגמה, כדי לקרוא לגרסה 3 של Drive API:
    drive = Google::Apis::DriveV3::DriveService.new
  2. מגדירים את פרטי הכניסה בשירות:
    drive.authorization = credentials
  3. שולחים בקשות לשירות ה-API באמצעות הממשק שסופק על ידי אובייקט השירות. לדוגמה, כדי להציג את הקבצים ב-Google Drive של המשתמש המאומת:
    files = drive.list_files

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

files = drive.list_files(options: { authorization: credentials })

Node.js

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

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

// Example of using Google Drive API to list filenames in user's Drive.
const drive = google.drive('v3');
drive.files.list({
  auth: oauth2Client,
  pageSize: 10,
  fields: 'nextPageToken, files(id, name)',
}, (err1, res1) => {
  if (err1) return console.log('The API returned an error: ' + err1);
  const files = res1.data.files;
  if (files.length) {
    console.log('Files:');
    files.map((file) => {
      console.log(`${file.name} (${file.id})`);
    });
  } else {
    console.log('No files found.');
  }
});

HTTP/REST

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

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

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

קריאה לנקודת הקצה drive.files (Drive Files API) באמצעות כותרת ה-HTTP Authorization: Bearer עשויה להיראות כך. חשוב לשים לב שצריך לציין את טוקן הגישה שלכם:

GET /drive/v2/files HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer access_token

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

GET https://www.googleapis.com/drive/v2/files?access_token=access_token

curl דוגמאות

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

curl -H "Authorization: Bearer access_token" https://www.googleapis.com/drive/v2/files

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

curl https://www.googleapis.com/drive/v2/files?access_token=access_token

דוגמה מלאה

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

PHP

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

  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.10
  4. יוצרים את הקבצים index.php ו-oauth2callback.php עם התוכן הבא.
  5. מריצים את הדוגמה באמצעות שרת אינטרנט שמוגדר להציג PHP. אם אתם משתמשים ב-PHP 5.6 ואילך, תוכלו להשתמש בשרת האינטרנט המובנה לבדיקה של 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_secrets.json');
$client->addScope(Google\Service\Drive::DRIVE_METADATA_READONLY);

if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
  $client->setAccessToken($_SESSION['access_token']);
  $drive = new Google\Service\Drive($client);
  $files = $drive->files->listFiles(array())->getItems();
  echo json_encode($files);
} else {
  $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();
$client->setAuthConfigFile('client_secrets.json');
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php');
$client->addScope(Google\Service\Drive::DRIVE_METADATA_READONLY);

if (! isset($_GET['code'])) {
  // Generate and set state value
  $state = bin2hex(random_bytes(16));
  $client->setState($state);
  $_SESSION['state'] = $state;

  $auth_url = $client->createAuthUrl();
  header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
  // Check the state value
  if (!isset($_GET['state']) || $_GET['state'] !== $_SESSION['state']) {
    die('State mismatch. Possible CSRF attack.');
  }
  $client->authenticate($_GET['code']);
  $_SESSION['access_token'] = $client->getAccessToken();
  $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/';
  header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}

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"

# This OAuth 2.0 access scope allows for full read/write access to the
# authenticated user's account and requires requests to use an SSL connection.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.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('/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'])

  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)


@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,
          'scopes': credentials.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'

  # 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 'google/apis/drive_v3'
require 'sinatra'
require 'googleauth'
require 'googleauth/stores/redis_token_store'

configure do
  enable :sessions

  set :client_id, Google::Auth::ClientId.from_file('/path/to/client_secret.json')
  set :scope, Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY
  set :token_store, Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new)
  set :authorizer, Google::Auth::WebUserAuthorizer.new(settings.client_id, settings.scope, settings.token_store, '/oauth2callback')
end

get '/' do
  user_id = settings.client_id.id
  credentials = settings.authorizer.get_credentials(user_id, request)
  if credentials.nil?
    redirect settings.authorizer.get_authorization_url(login_hint: user_id, request: request)
  end
  drive = Google::Apis::DriveV3::DriveService.new
  files = drive.list_files(options: { authorization: credentials })
  "<pre>#{JSON.pretty_generate(files.to_h)}</pre>"
end

get '/oauth2callback' do
  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 read-only Drive activity.
const scopes = [
  'https://www.googleapis.com/auth/drive.metadata.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 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 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.');
        }
      });
    }
  });

  // 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(80);
}
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__)

CLIENT_ID = '123456789.apps.googleusercontent.com'
CLIENT_SECRET = 'abc123'  # Read from a file or environmental variable in a real app
SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly'
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/drive/v2/files'
    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
    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'}
    r = requests.post('https://oauth2.googleapis.com/token', data=data)
    flask.session['credentials'] = r.text
    return flask.redirect(flask.url_for('index'))


if __name__ == '__main__':
  import uuid
  app.secret_key = str(uuid.uuid4())
  app.debug = False
  app.run()

כללי אימות של URI של הפניה לכתובת אחרת

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

כללי אימות
Scheme

מזהי URI להפניה אוטומטית חייבים להשתמש בפרוטוקול HTTPS, ולא ב-HTTP רגיל. מזהי URI של מארח מקומי (כולל מזהי URI של כתובות IP של Localhost) פטורים מהכלל הזה.

מארח

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

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

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

    נתיב

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

    שאילתה

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

    מקטע

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

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

    הרשאה מצטברת

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

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

    במקרה כזה, בזמן הכניסה, האפליקציה עשויה לבקש את ההיקפים openid ו-profile כדי לבצע כניסה בסיסית, ולאחר מכן לבקש את ההיקף https://www.googleapis.com/auth/drive.file בזמן הבקשה הראשונה כדי לשמור מיקס.

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

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

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

    בדוגמאות הקוד הספציפיות לשפות שמפורטות בקטע שלב 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.file&
      redirect_uri=https%3A//oauth2.example.com/code&
      prompt=consent&
      include_granted_scopes=true

    רענון אסימון גישה (גישה אופליין)

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

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

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

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

    PHP

    אם האפליקציה שלכם צריכה גישה אופליין ל-Google API, צריך להגדיר את סוג הגישה של לקוח ה-API כ-offline:

    $client->setAccessType("offline");

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

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

    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

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