استخدام 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 هذا مخصّص لتفويض المستخدم. وهو مصمَّم للتطبيقات التي يمكنها تخزين المعلومات السرية والحفاظ على الحالة. يمكن لتطبيق خادم ويب معتمَد بشكل صحيح الوصول إلى واجهة برمجة تطبيقات أثناء تفاعل المستخدم مع التطبيق أو بعد مغادرة المستخدم للتطبيق.

تستخدم تطبيقات خادم الويب أيضًا بشكل متكرر حسابات الخدمة لتفويض طلبات البيانات من واجهة برمجة التطبيقات، خاصةً عند طلب بيانات من Cloud API للوصول إلى البيانات المستندة إلى المشروع بدلاً من البيانات الخاصة بالمستخدم. يمكن لتطبيقات خادم الويب استخدام حسابات الخدمة مع تفويض المستخدم. يُرجى العِلم أنّ YouTube Data API تتيح استخدام حساب الخدمة فقط لمالكي المحتوى على YouTube الذين يملكون ويديرون قنوات متعدّدة على YouTube. على وجه التحديد، يمكن لمالكي المحتوى استخدام حسابات الخدمة لاستدعاء طرق واجهة برمجة التطبيقات التي تتيح استخدام مَعلمة الطلب onBehalfOfContentOwner.

مكتبات العملاء

تستخدم الأمثلة الخاصة باللغة في هذه الصفحة مكتبات عملاء Google API لتنفيذ تفويض OAuth 2.0. لتشغيل عينات التعليمات البرمجية، عليك أولاً تثبيت مكتبة البرامج للغتك.

عند استخدام مكتبة عميل Google API لمعالجة مسار OAuth 2.0 في تطبيقك، تنفّذ مكتبة العميل العديد من الإجراءات التي يحتاج التطبيق إلى معالجتها بنفسه في الحالات الأخرى. على سبيل المثال، يحدّد هذا الإعداد متى يمكن للتطبيق استخدام رموز الدخول المخزَّنة أو إعادة تحميلها، ومتى يجب أن يحصل التطبيق على الموافقة مرة أخرى. تنشئ مكتبة العميل أيضًا عناوين URL صحيحة لإعادة التوجيه وتساعد في تنفيذ معالِجات إعادة التوجيه التي تستبدل رموز التفويض برموز الدخول.

تتوفّر مكتبات عملاء Google API للتطبيقات من جهة الخادم باللغات التالية:

المتطلبات الأساسية

تفعيل واجهات برمجة التطبيقات لمشروعك

يجب أن يفعّل أي تطبيق يطلب بيانات من Google APIs واجهات برمجة التطبيقات هذه في "وحدة تحكّم واجهة برمجة التطبيقات".

لتفعيل واجهة برمجة تطبيقات لمشروعك، اتّبِع الخطوات التالية:

  1. افتح مكتبة واجهات برمجة التطبيقات في Google API Console.
  2. اختَر مشروعًا أو أنشِئ مشروعًا جديدًا إذا طُلب منك ذلك.
  3. استخدِم صفحة المكتبة للعثور على YouTube Data API وتفعيلها. ابحث عن أي واجهات برمجة تطبيقات أخرى سيستخدمها تطبيقك وفعِّلها أيضًا.

إنشاء بيانات اعتماد التفويض

يجب أن يتوفّر لأي تطبيق يستخدم OAuth 2.0 للوصول إلى Google APIs بيانات اعتماد تفويض تعرّف التطبيق لخادم OAuth 2.0 من Google. توضّح الخطوات التالية كيفية إنشاء بيانات اعتماد لمشروعك. يمكن لتطبيقاتك بعد ذلك استخدام بيانات الاعتماد للوصول إلى واجهات برمجة التطبيقات التي فعّلتها لهذا المشروع.

  1. انتقِل إلى صفحة "العملاء".
  2. انقر على إنشاء عميل (Create Client).
  3. اختَر نوع التطبيق تطبيق ويب.
  4. املأ النموذج وانقر على إنشاء. يجب أن تحدّد التطبيقات التي تستخدم لغات وأُطر عمل مثل 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 النطاقات التالية:

النطاق الوصف
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

يحتوي مستند نطاقات OAuth 2.0 API على قائمة كاملة بالنطاقات التي يمكنك استخدامها للوصول إلى Google APIs.

المتطلبات الخاصة باللغة

لتشغيل أي من عينات التعليمات البرمجية في هذا المستند، ستحتاج إلى حساب Google وإمكانية الوصول إلى الإنترنت ومتصفح ويب. إذا كنت تستخدم إحدى مكتبات عملاء واجهة برمجة التطبيقات، اطّلِع أيضًا على المتطلبات الخاصة بلغة البرمجة في الأقسام التالية.

PHP

لتشغيل عينات التعليمات البرمجية بلغة PHP في هذا المستند، ستحتاج إلى:

  • ‫PHP 8.0 أو إصدار أحدث مع تثبيت واجهة سطر الأوامر (CLI) وإضافة JSON
  • أداة إدارة التبعية Composer
  • مكتبة برامج Google APIs للغة PHP:

    composer require google/apiclient:^2.15.0

لمزيد من المعلومات، يمكنك الاطّلاع على مكتبة برامج Google APIs للغة PHP.

Python

لتشغيل عينات التعليمات البرمجية بلغة Python في هذا المستند، ستحتاج إلى ما يلي:

  • الإصدار 3.7 من Python أو إصدار أحدث
  • أداة إدارة حزم pip
  • إصدار مكتبة برامج Google APIs للغة Python 2.0:
    pip install --upgrade google-api-python-client
  • google-auth وgoogle-auth-oauthlib وgoogle-auth-httplib2 لتفويض المستخدم
    pip install --upgrade google-auth google-auth-oauthlib google-auth-httplib2
  • إطار عمل تطبيق الويب Flask Python
    pip install --upgrade flask
  • مكتبة HTTP‏ requests
    pip install --upgrade requests

راجِع ملاحظات الإصدار لمكتبة برامج Google API للغة Python إذا لم تتمكّن من ترقية Python ودليل نقل البيانات المرتبط بها.

Ruby

لتشغيل عينات تعليمات برمجية Ruby في هذا المستند، ستحتاج إلى:

  • ‫Ruby 2.6 أو إصدار أحدث
  • مكتبة Google Auth للغة Ruby:

    gem install googleauth
  • إطار عمل تطبيق الويب Sinatra Ruby

    gem install sinatra

Node.js

لتشغيل عيّنات التعليمات البرمجية Node.js في هذا المستند، ستحتاج إلى ما يلي:

  • إصدار الصيانة الطويل الأمد (LTS) أو الإصدار الطويل الأمد (LTS) النشط أو الإصدار الحالي من Node.js
  • عميل Google APIs Node.js:

    npm install googleapis crypto express express-session

HTTP/REST

لا تحتاج إلى تثبيت أي مكتبات لتتمكّن من طلب نقاط نهاية OAuth 2.0 مباشرةً.

الحصول على رموز الدخول عبر OAuth 2.0

توضّح الخطوات التالية كيفية تفاعل تطبيقك مع خادم OAuth 2.0 من Google للحصول على موافقة المستخدم على تنفيذ طلب بيانات من واجهة برمجة التطبيقات نيابةً عنه. ويجب أن يحصل تطبيقك على هذه الموافقة قبل أن يتمكّن من تنفيذ طلب بيانات من واجهة برمجة تطبيقات Google يتطلّب تفويضًا من المستخدم.

تلخّص القائمة التالية هذه الخطوات بسرعة:

  1. يحدّد تطبيقك الأذونات التي يحتاج إليها.
  2. يعيد تطبيقك توجيه المستخدم إلى Google مع قائمة الأذونات المطلوبة.
  3. يقرّر المستخدم ما إذا كان سيمنح الأذونات لتطبيقك.
  4. يعرف تطبيقك القرار الذي اتخذه المستخدم.
  5. إذا منح المستخدم الأذونات المطلوبة، يسترد تطبيقك الرموز المميّزة اللازمة لإرسال طلبات بيانات من واجهة برمجة التطبيقات نيابةً عن المستخدم.

الخطوة 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 مطلوب

معرّف العميل لتطبيقك يمكنك العثور على هذه القيمة في صفحة "العملاء" ضمن Cloud Console.

redirect_uri مطلوب

يحدّد هذا المَعلمة المكان الذي يعيد خادم واجهة برمجة التطبيقات توجيه المستخدم إليه بعد أن يكمل المستخدم عملية التفويض. يجب أن تتطابق القيمة تمامًا مع أحد عناوين URI لإعادة التوجيه المصرّح بها لعميل OAuth 2.0، والتي تم ضبطها في صفحة العملاء في Cloud Console الخاصة بالعميل. إذا لم تتطابق هذه القيمة مع معرّف موارد منتظم (URI) معتمَد لإعادة التوجيه لـ client_id المقدَّم، سيظهر لك الخطأ redirect_uri_mismatch.

يُرجى العِلم أنّ المخطط http أو https وحالة الأحرف والشرطة المائلة اللاحقة ('/') يجب أن تتطابق جميعها.

response_type مطلوب

يحدّد هذا الإعداد ما إذا كانت نقطة نهاية Google OAuth 2.0 ستعرض رمز تفويض.

اضبط قيمة المَعلمة على code لتطبيقات خادم الويب.

scope مطلوب

قائمة مفصولة بمسافات تتضمّن النطاقات التي تحدّد الموارد التي يمكن لتطبيقك الوصول إليها نيابةً عن المستخدم. تُعلم هذه القيم شاشة طلب الموافقة التي تعرضها Google للمستخدم.

تتيح النطاقات لتطبيقك طلب الوصول إلى الموارد التي يحتاج إليها فقط، كما تتيح للمستخدمين التحكّم في مقدار الوصول الذي يمنحونه لتطبيقك. وبالتالي، هناك علاقة عكسية بين عدد النطاقات المطلوبة واحتمال الحصول على موافقة المستخدم.

يستخدم الإصدار الثالث من YouTube Data API النطاقات التالية:

النطاق الوصف
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

يوفّر مستند نطاقات OAuth 2.0 API قائمة كاملة بالنطاقات التي يمكنك استخدامها للوصول إلى Google APIs.

ننصحك بأن يطلب تطبيقك الوصول إلى نطاقات التفويض في السياق كلما أمكن ذلك. من خلال طلب الوصول إلى بيانات المستخدم في السياق، باستخدام التفويض المتزايد، يمكنك مساعدة المستخدمين على فهم سبب احتياج تطبيقك إلى الوصول الذي يطلبه.

access_type مقترَح

يشير هذا الحقل إلى ما إذا كان بإمكان تطبيقك إعادة تحميل رموز الدخول عندما لا يكون المستخدم متصلاً بالمتصفّح. القيم الصالحة للمَعلمة هي online، وهي القيمة التلقائية، وoffline.

اضبط القيمة على offline إذا كان تطبيقك يحتاج إلى إعادة تحميل رموز الدخول عندما لا يكون المستخدم متواجدًا في المتصفّح. هذه هي طريقة إعادة تحميل رموز الدخول الموضّحة لاحقًا في هذا المستند. توجّه هذه القيمة خادم التفويض في Google إلى عرض رمز مميز لإعادة التحميل ورمز دخول في المرة الأولى التي يستبدل فيها تطبيقك رمز التفويض برموز مميزة.

state مقترَح

تحدّد أي قيمة سلسلة يستخدمها تطبيقك للحفاظ على الحالة بين طلب التفويض واستجابة خادم التفويض. يعرض الخادم القيمة الدقيقة التي ترسلها كزوج name=value في مكوّن طلب البحث لعنوان URL (?) في redirect_uri بعد موافقة المستخدم على طلب وصول تطبيقك أو رفضه.

يمكنك استخدام هذه المَعلمة لأغراض متعددة، مثل توجيه المستخدم إلى المورد الصحيح في تطبيقك وإرسال أرقام تُستخدم لمرة واحدة والحدّ من تزوير الطلبات من مواقع إلكترونية مختلفة. بما أنّه يمكن تخمين redirect_uri، فإنّ استخدام قيمة state يمكن أن يزيد من تأكيدك على أنّ الاتصال الوارد هو نتيجة طلب مصادقة. إذا أنشأت سلسلة عشوائية أو رمّزت تجزئة ملف تعريف ارتباط أو قيمة أخرى تسجّل حالة العميل، يمكنك التحقّق من صحة الاستجابة للتأكّد أيضًا من أنّ الطلب والاستجابة صدرا من المتصفّح نفسه، ما يوفّر الحماية من الهجمات، مثل تزوير الطلبات من مواقع إلكترونية مختلفة. يمكنك الاطّلاع على مستندات 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 التحكّم في اختيار التطبيقات الداخلية والخارجية التي يمكنها الوصول إلى بيانات Google Workspace لمزيد من المعلومات حول كيفية حظر المشرف للوصول إلى جميع النطاقات أو النطاقات الحساسة والمحظورة إلى أن يتم منح الإذن صراحةً لمعرّف عميل OAuth.

disallowed_useragent

يتم عرض نقطة نهاية التفويض داخل وكيل مستخدم مضمّن لا تسمح به سياسات OAuth 2.0 في Google.

قد يواجه مطوّرو تطبيقات iOS وmacOS هذا الخطأ عند فتح طلبات التفويض في WKWebView. بدلاً من ذلك، على المطوّرين استخدام مكتبات iOS، مثل تسجيل الدخول باستخدام حساب Google لنظام التشغيل iOS أو AppAuth لنظام التشغيل iOS من مؤسسة OpenID.

قد يواجه مطوّرو تطبيقات الويب هذا الخطأ عندما يفتح تطبيق iOS أو macOS رابط ويب عامًا في وكيل مستخدم مضمّن وينتقل المستخدم إلى نقطة نهاية تفويض OAuth 2.0 من Google من موقعك الإلكتروني. على المطوّرين السماح بفتح الروابط العامة في معالج الروابط التلقائي لنظام التشغيل، والذي يتضمّن كلاً من معالجات الروابط العامة أو تطبيق المتصفّح التلقائي. وتُعدّ مكتبة SFSafariViewController أيضًا خيارًا متاحًا.

org_internal

معرّف عميل OAuth في الطلب هو جزء من مشروع يقيّد الوصول إلى حسابات Google في مؤسسة Google Cloud معيّنة. لمزيد من المعلومات حول خيار الضبط هذا، يُرجى الاطّلاع على قسم نوع المستخدم في مقالة المساعدة "إعداد شاشة طلب الموافقة عبر OAuth".

invalid_client

سر عميل OAuth غير صحيح. يُرجى مراجعة إعدادات عميل OAuth، بما في ذلك معرّف العميل والسر المستخدَمَين لهذا الطلب.

deleted_client

تم حذف عميل OAuth المستخدَم لإرسال الطلب. يمكن أن يتم الحذف يدويًا أو تلقائيًا في حال عدم استخدام العملاء . يمكن استعادة العملاء المحذوفين في غضون 30 يومًا من الحذف. مزيد من المعلومات

invalid_grant

عند إعادة تحميل رمز الدخول أو استخدام التفويض المتزايد، قد يكون الرمز منتهي الصلاحية أو تم إبطاله. يُرجى مصادقة المستخدم مرة أخرى وطلب موافقته للحصول على رموز مميزة جديدة. إذا استمر ظهور هذا الخطأ، تأكَّد من ضبط تطبيقك بشكل صحيح ومن استخدام الرموز المميّزة والمَعلمات الصحيحة في طلبك. وإلا، قد يكون حساب المستخدم قد تم حذفه أو إيقافه.

redirect_uri_mismatch

لا يتطابق redirect_uri الذي تم تمريره في طلب التفويض مع معرّف موارد منتظم (URI) معتمَد لإعادة التوجيه لمعرّف عميل OAuth. راجِع معرِّفات الموارد المنتظمة (URI) المعتمَدة لإعادة التوجيه في صفحة "العملاء" ضمن Google Cloud Console.

قد تشير المَعلمة 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، يعيد المتصفّح توجيهك إلى مساحة بروتوكول OAuth 2.0، وهي أداة لاختبار مسارات OAuth. ستلاحظ أنّ مساحة بروتوكول OAuth 2.0 قد سجّلت رمز التفويض تلقائيًا.

الخطوة 5: تبديل رمز التفويض برموز مميّزة لإعادة التحميل والوصول

بعد أن يتلقّى خادم الويب رمز التفويض، يمكنه استبدال رمز التفويض برمز دخول.

PHP

لتبديل رمز التفويض برمز مميّز للوصول، استخدِم طريقة fetchAccessTokenWithAuthCode:

$access_token = $client->fetchAccessTokenWithAuthCode($_GET['code']);

Python

في صفحة معاودة الاتصال، استخدِم مكتبة google-auth للتحقّق من ردّ خادم التفويض. بعد ذلك، استخدِم طريقة flow.fetch_token لتبديل رمز التفويض في ذلك الردّ برمز دخول:

state = flask.session['state']
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
    'client_secret.json',
    scopes=['https://www.googleapis.com/auth/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 browser session storage, but for security: client_id, client_secret,
# and token_uri are instead stored only on the backend server.
credentials = flow.credentials
flask.session['credentials'] = {
    'token': credentials.token,
    'refresh_token': credentials.refresh_token,
    'granted_scopes': credentials.granted_scopes}

Ruby

في صفحة معاودة الاتصال، استخدِم مكتبة googleauth للتحقّق من ردّ خادم التفويض. استخدِم طريقة authorizer.handle_auth_callback_deferred لحفظ رمز التفويض وإعادة التوجيه إلى عنوان URL الذي طلب التفويض في الأصل. يؤدي ذلك إلى تأجيل تبادل الرمز من خلال تخزين النتائج مؤقتًا في جلسة المستخدم.

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

Node.js

لتبديل رمز التفويض برمز وصول، استخدِم الطريقة getToken:

const url = require('url');

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

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

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

HTTP/REST

لتبديل رمز التفويض برمز دخول، يمكنك استدعاء نقطة النهاية https://oauth2.googleapis.com/token وضبط المَعلمات التالية:

الحقول
client_id معرِّف العميل الذي تم الحصول عليه من صفحة "العملاء" في Cloud Console
client_secret اختياريّ

سر العميل الذي تم الحصول عليه من صفحة "العملاء" في Cloud Console.

code رمز التفويض الذي تم إرجاعه من الطلب الأولي
grant_type وفقًا لمواصفات OAuth 2.0، يجب ضبط قيمة هذا الحقل على authorization_code.
redirect_uri أحد معرّفات الموارد المنتظمة (URI) لإعادة التوجيه المدرَجة لمشروعك في صفحة "العملاء" في Cloud Console لـ client_id المحدّد.

على الرغم من أنّ استخدام DPoP اختياري، ننصح به لتعزيز الأمان. يعتمد أمان DPoP على حصر المفتاح الخاص بجهاز واحد، لذا ننصح بتخزينه بطريقة لا يمكن من خلالها نسخه خارج الجهاز، على سبيل المثال باستخدام وحدات النظام الأساسي الموثوقة (TPM) أو الجيوب الآمنة أو غيرها من مخازن المفاتيح المستنِدة إلى الأجهزة. لاستخدام DPoP، يجب أن ينشئ تطبيقك رمز JWT جديدًا وفريدًا لإثبات DPoP لكل طلب إلى نقطة نهاية الرمز المميّز وإضافته كعنوان طلب HTTP.

العنوان مطلوب الوصف
DPoP اختياري إثبات DPoP هو رمز JWT يثبت حيازة مفتاح خاص. هذا عنوان وليس مَعلمة. وفي حال توفيره، يتم ربط الرموز المميّزة التي تم عرضها بهذا المفتاح. يجب إنشاء إثبات جديد وفريد لكل طلب، ويجب أن يتضمّن المطالبتَين htm (طريقة HTTP) وhtu (معرّف الموارد المنتظم لبروتوكول HTTP) اللتين تتطابقان مع الطلب.

يعرض المقتطف التالي نموذجًا لطلب:

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik\
 VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR\
 nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE\
 QSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIj\
 oiUE9TVCIsImh0dSI6Imh0dHBzOi8vc2VydmVyLmV4YW1wbGUuY29tL3Rva2VuIiwia\
 WF0IjoxNTYyMjYyNjE2fQ.2-GxA6T8lP4vfrg8v-FdWP0A0zdrj8igiMLvqRMUvwnQg\
 4PtFLbdLXiOSsX0x7NVY-FNyJK70nfbV37xRZT3Lg

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
redirect_uri=https%3A//developers.google.com/oauthplayground&
grant_type=authorization_code

إنشاء إثبات DPoP

توضّح الخطوات التالية كيفية إنشاء إثبات DPoP باستخدام OpenSSL من سطر الأوامر:

  1. إنشاء مفتاحَي تشفير EC P-256:
    openssl ecparam -name prime256v1 -genkey -noout -out dpop_private.pem
    openssl ec -in dpop_private.pem -pubout -out dpop_public.pem
  2. إنشاء عنوان DPoP:

    يجب أن يتضمّن العنوان المطالبات typ وalg وjwk (المفتاح العام). تمثّل القيمتان x وy الإحداثيات المشفّرة باستخدام Base64Url لمفتاح EC العام. ترميز Base64Url لملف JSON هذا:

    {
      "typ":"dpop+jwt",
      "alg":"ES256",
      "jwk": {
        "kty":"EC",
        "x":"YOUR_PUBLIC_KEY_X",
        "y":"YOUR_PUBLIC_KEY_Y",
        "crv":"P-256"
      }
    }
  3. إنشاء حمولة DPoP:

    يجب أن تتضمّن الحمولة jti (معرّف فريد للطلب) وhtm (طريقة HTTP، مثل POST) وhtu (معرّف الموارد المنتظم (URI) لبروتوكول HTTP، مثل https://oauth2.googleapis.com/token) وiat (وقت الإصدار). إذا تلقّيت رقمًا خاصًا من الخادم في رأس DPoP-Nonce في الردّ على طلب سابق، يجب تضمين قيمة هذا الرقم في مطالبة nonce. تكون مطالبة nonce اختيارية لعمليات تبادل رموز التفويض، ولا تُستخدم إلا عند تلقّي عنوان DPoP-Nonce سابقًا. ترميز Base64Url لملف JSON هذا:

    {
      "jti":"JTI_VALUE",
      "htm":"POST",
      "htu":"https://oauth2.googleapis.com/token",
      "iat":YOUR_JWT_ISSUED_TIME,
      "nonce":"SERVER_PROVIDED_NONCE"
    }

    تعتمد قيمة jti على نوع التبادل:

    • بالنسبة إلى عمليات تبادل رموز التفويض، يجب أن تكون قيمة jti هي تجزئة SHA256 لرمز التفويض بترميز Base64Url: "jti":"BASE64URL(SHA256(AUTHORIZATION_CODE))".
    • بالنسبة إلى عمليات تبادل الرموز المميزة لإعادة التحميل، يجب أن تكون قيمة jti معرّفًا فريدًا لكل طلب: "jti":"YOUR_UNIQUE_PER_REQUEST_IDENTIFIER".
  4. التوقيع على الإثبات:

    ادمج العنوان المرمّز والحمولة مع نقطة (.)، ثم وقِّع النتيجة باستخدام مفتاحك الخاص باستخدام ES256. يُرجى العِلم أنّ JWS يتطلّب أن يكون التوقيع بتنسيق R | S أولي مدمج (64 بايت لـ P-256). في حال استخدام OpenSSL مباشرةً، يجب تحويل التوقيع التلقائي المرمَّز بتنسيق ASN.1 DER إلى هذا التنسيق الأولي.

تتم الإشارة إلى عملية التبادل الناجحة من خلال استجابة 200 OK تحتوي على الرموز المميّزة. إذا تم استخدام إثبات DPoP صالح أثناء عملية التبادل، سيكون الرمز المميز لإعادة التحميل الذي تعرضه Google مرتبطًا بمفتاحك من خلال DPoP، ولكن لن تكون رموز الدخول مرتبطة من خلال DPoP. ستحتفظ رموز الدخول بـ token_type Bearer بدلاً من DPoP. بالإضافة إلى ذلك، يعرض Google عنوان HTTP يتضمّن DPoP-Nonce في الاستجابة. يجب أن يخزّن برنامجك هذا الرقم الخاص مؤقتًا ويضمّنه في مطالبة nonce لإثبات DPoP في الطلبات اللاحقة (مثل عند استبدال الرمز المميز لإعادة التحميل برمز دخول جديد، أو عند طلب واجهات برمجة تطبيقات محمية ببروتوكول DPoP). باستخدام هذا الرقم الخاص الصادر مبكرًا، يمكنك تجنُّب حدوث خطأ إضافي في الذهاب والعودة (use_dpop_nonce) في طلبك التالي.

يجب تضمين إثباتات DPoP في طلبات تبادل الرموز المميزة التي يتم إجراؤها باستخدام الرموز المميزة لإعادة التحميل المرتبطة بـ DPoP.

يحدث فشل في عملية التبادل إذا كان عنوان DPoP غير متوفّر عندما يكون متوقّعًا أو غير صالح، أو إذا كان الإثبات يستخدم مفتاحًا مختلفًا عن المفتاح المرتبط بالرمز المميّز. وفي هذه الحالات، يعرض الخادم الخطأ 400 Bad Request. إذا كان إثبات DPoP يتضمّن مطالبات htm أو htu غير متطابقة، أو iat منتهي الصلاحية، أو jti مُعاد استخدامه، أو توقيع غير صالح، ستعرض Google رمز الخطأ invalid_dpop_proof. إذا كان رمز nonce لبروتوكول DPoP مطلوبًا، مثلاً أثناء تبادل الرمز المميز لإعادة التحميل، وكان إثبات DPoP لا يتضمّن مطالبة nonce، أو كانت قيمة nonce غير مقبولة للخادم (على سبيل المثال، انتهت صلاحيتها أو تم استخدامها من قبل أو غير صحيحة)، تعرض Google رمز الخطأ use_dpop_nonce مع عنوان HTTP DPoP-Nonce يحتوي على رمز nonce جديد يمكنك استخدامه في طلب لاحق. قد تؤدي حالات الفشل الأخرى إلى عرض invalid_grant.

تستجيب Google لهذا الطلب من خلال عرض عنصر JSON يحتوي على رمز مميز للوصول قصير الأجل ورمز مميز لإعادة التحميل. يُرجى العِلم أنّه لا يتم عرض الرمز المميّز لإعادة التحميل إلا إذا ضبط تطبيقك المَعلمة access_type على offline في الطلب الأولي إلى خادم التفويض في Google.

يحتوي الردّ على الحقول التالية:

الحقول
access_token الرمز المميّز الذي يرسله تطبيقك للسماح بطلب بيانات من واجهة برمجة تطبيقات Google.
expires_in المدة المتبقية لرمز الدخول بالثواني
refresh_token رمز مميّز يمكنك استخدامه للحصول على رمز دخول جديد تظل الرموز المميزة لإعادة التحميل صالحة إلى أن يلغي المستخدم إذن الوصول أو تنتهي صلاحية الرمز المميز لإعادة التحميل. في حال استخدام DPoP، يتم ربط الرمز المميز لإعادة التحميل بالمفتاح الخاص المستخدَم لتوقيع إثبات DPoP. مرة أخرى، لا يظهر هذا الحقل في هذه الاستجابة إلا إذا ضبطت المَعلمة access_type على offline في الطلب الأولي إلى خادم التفويض في Google.
refresh_token_expires_in المدة المتبقية لصلاحية الرمز المميز لإعادة التحميل بالثواني لا يتم ضبط هذه القيمة إلا عندما يمنح المستخدم إذن الوصول المستنِد إلى الوقت.
scope نطاقات الوصول التي يمنحها access_token، ويتم التعبير عنها كقائمة من السلاسل الحسّاسة لحالة الأحرف والمفصولة بمسافات.
token_type نوع الرمز المميّز الذي تم عرضه تكون هذه القيمة دائمًا Bearer، حتى عند استخدام DPoP.

يعرض المقتطف التالي نموذجًا لعناوين الاستجابة والنص الأساسي الناجحَين عند استخدام DPoP:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
DPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI

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

الأخطاء

عند استبدال رمز التفويض برمز دخول، قد يظهر لك الخطأ التالي بدلاً من الاستجابة المتوقعة. يتم في هذا القسم إدراج رموز الأخطاء الشائعة والحلول المقترَحة.

invalid_grant

رمز التفويض المقدَّم غير صالح أو تنسيقه غير صحيح. اطلب رمزًا جديدًا من خلال إعادة بدء عملية OAuth لمطالبة المستخدم بالموافقة مرة أخرى.

الخطوة 6: التحقّق من النطاقات التي منحها المستخدمون

عند طلب أذونات (نطاقات) متعددة، قد لا يمنح المستخدمون تطبيقك إذن الوصول إليها جميعًا. يجب أن يتحقّق تطبيقك من النطاقات التي تم منحها فعليًا وأن يتعامل بسلاسة مع الحالات التي يتم فيها رفض بعض الأذونات، وذلك عادةً من خلال إيقاف الميزات التي تعتمد على تلك النطاقات المرفوضة.

ولكن هناك استثناءات في هذا الصدد. تتجاوز تطبيقات Google Workspace Enterprise التي تتضمّن تفويضًا على مستوى النطاق، أو التطبيقات التي تم وضع علامة عليها على أنّها موثوق بها، شاشة طلب الموافقة على الأذونات الدقيقة. بالنسبة إلى هذه التطبيقات، لن تظهر للمستخدمين شاشة طلب الموافقة على الأذونات الدقيقة. بدلاً من ذلك، سيتلقّى تطبيقك إما جميع النطاقات المطلوبة أو لن يتلقّى أيًا منها.

لمزيد من المعلومات التفصيلية، يُرجى الاطّلاع على كيفية التعامل مع الأذونات الدقيقة.

PHP

للتحقّق من النطاقات التي منحها المستخدم، استخدِم طريقة getGrantedScope():

// Space-separated string of granted scopes if it exists, otherwise null.
$granted_scopes = $client->getOAuth2Service()->getGrantedScope();

Python

يحتوي عنصر credentials الذي تم عرضه على سمة granted_scopes، وهي قائمة بالنطاقات التي منحها المستخدم لتطبيقك.

credentials = flow.credentials
flask.session['credentials'] = {
    'token': credentials.token,
    'refresh_token': credentials.refresh_token,
    '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 في استجابة رمز الدخول. نطاقات الوصول التي يمنحها رمز الدخول، ويتم التعبير عنها كقائمة من السلاسل الحسّاسة لحالة الأحرف والمفصولة بمسافات.

على سبيل المثال، يشير نموذج استجابة رمز الدخول التالي إلى أنّ المستخدم قد منح تطبيقك الإذن بالاطّلاع على فيديوهات المستخدم على 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. أنشئ عنصر خدمة لواجهة برمجة التطبيقات التي تريد طلبها. يمكنك إنشاء كائن خدمة من خلال تقديم كائن Google\Client معتمَد إلى الدالة الإنشائية لواجهة برمجة التطبيقات التي تريد طلبها. على سبيل المثال، لطلب بيانات من YouTube Data API:
    $youtube = new Google_Service_YouTube($client);
  3. أرسِل طلبات إلى خدمة واجهة برمجة التطبيقات باستخدام الواجهة التي يوفّرها عنصر الخدمة. على سبيل المثال، لاسترداد بيانات حول قناة المستخدم المفوَّض على YouTube:
    $channel = $youtube->channels->listChannels('snippet', array('mine' => $mine));

Python

بعد الحصول على رمز الدخول، يمكن لتطبيقك استخدام هذا الرمز لتفويض طلبات البيانات من واجهة برمجة التطبيقات نيابةً عن حساب مستخدم أو حساب خدمة معيّن. استخدِم بيانات اعتماد التفويض الخاصة بالمستخدم لإنشاء عنصر خدمة لواجهة برمجة التطبيقات التي تريد طلبها، ثم استخدِم هذا العنصر لإرسال طلبات بيانات من واجهة برمجة التطبيقات المصرّح بها.

  1. أنشئ عنصر خدمة لواجهة برمجة التطبيقات التي تريد طلبها. يمكنك إنشاء عنصر خدمة من خلال استدعاء طريقة build في مكتبة googleapiclient.discovery مع اسم واجهة برمجة التطبيقات وإصدارها وبيانات اعتماد المستخدم: على سبيل المثال، لاستدعاء الإصدار 3 من YouTube Data API:
    from googleapiclient.discovery import build
    
    youtube = build('youtube', 'v3', credentials=credentials)
  2. أرسِل طلبات إلى خدمة واجهة برمجة التطبيقات باستخدام الواجهة التي يوفّرها عنصر الخدمة. على سبيل المثال، لاسترداد بيانات حول قناة المستخدم المفوَّض على YouTube:
    channel = youtube.channels().list(mine=True, part='snippet').execute()

Ruby

بعد الحصول على رمز مميّز للوصول، يمكن لتطبيقك استخدام هذا الرمز لإرسال طلبات بيانات من واجهة برمجة التطبيقات نيابةً عن حساب مستخدم أو حساب خدمة معيّن. استخدِم بيانات اعتماد التفويض الخاصة بالمستخدم لإنشاء عنصر خدمة لواجهة برمجة التطبيقات التي تريد طلبها، ثم استخدِم هذا العنصر لإرسال طلبات بيانات من واجهة برمجة التطبيقات المصرّح بها.

  1. أنشئ عنصر خدمة لواجهة برمجة التطبيقات التي تريد طلبها. على سبيل المثال، لطلب الإصدار 3 من YouTube Data API:
    youtube = Google::Apis::YoutubeV3::YouTubeService.new
  2. اضبط بيانات الاعتماد في الخدمة:
    youtube.authorization = credentials
  3. أرسِل طلبات إلى خدمة واجهة برمجة التطبيقات باستخدام الواجهة التي يوفّرها عنصر الخدمة. على سبيل المثال، لاسترداد بيانات حول قناة المستخدم المفوَّض على 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. يمكن لتطبيقك استخدام هذا الرمز المميز لتفويض طلبات البيانات من واجهة برمجة التطبيقات نيابةً عن حساب مستخدم أو حساب خدمة معيّن. أنشئ عنصر خدمة لواجهة برمجة التطبيقات التي تريد طلبها. على سبيل المثال، يستخدم الرمز التالي 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 نيابةً عن حساب مستخدم معيّن إذا تم منح نطاقات الوصول التي تتطلّبها واجهة برمجة التطبيقات. لإجراء ذلك، يجب تضمين رمز الدخول في طلب موجّه إلى واجهة برمجة التطبيقات من خلال تضمين إما مَعلمة طلب البحث access_token أو قيمة عنوان HTTP Authorization Bearer. ويُفضَّل استخدام عنوان HTTP كلما أمكن ذلك، لأنّ سلاسل طلبات البحث تكون عادةً مرئية في سجلّات الخادم. في معظم الحالات، يمكنك استخدام مكتبة برامج لإعداد طلباتك إلى Google APIs (على سبيل المثال، عند طلب YouTube Data API).

يُرجى العِلم أنّ YouTube Data API تتيح استخدام حسابات الخدمة فقط لمالكي المحتوى على YouTube الذين يملكون ويديرون قنوات متعددة على YouTube، مثل شركات الإنتاج واستوديوهات الأفلام.

يمكنك تجربة جميع واجهات Google APIs والاطّلاع على نطاقاتها في مساحة بروتوكول OAuth 2.0.

أمثلة على طلبات HTTP GET

قد يبدو طلب البيانات من نقطة النهاية youtube.channels (واجهة YouTube Data API) باستخدام عنوان HTTP‏ Authorization: Bearer على النحو التالي. يُرجى العِلم أنّه عليك تحديد رمز الدخول الخاص بك:

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

في ما يلي طلب إلى واجهة برمجة التطبيقات نفسها للمستخدم الذي تمت مصادقته باستخدام مَعلمة سلسلة طلب البحث 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. في وحدة تحكّم واجهة برمجة التطبيقات، أضِف عنوان 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

يستخدم هذا المثال إطار عمل Flask. ويشغّل تطبيق ويب على http://localhost:8080 يتيح لك اختبار مسار OAuth 2.0. إذا انتقلت إلى عنوان URL هذا، من المفترض أن تظهر لك خمسة روابط:

  • اختبار طلب بيانات من واجهة برمجة التطبيقات: يشير هذا الرابط إلى صفحة تحاول تنفيذ نموذج طلب بيانات من واجهة برمجة التطبيقات. وإذا لزم الأمر، يبدأ مسار التفويض. في حال نجاح الطلب، ستعرض الصفحة الردّ من واجهة برمجة التطبيقات.
  • اختبار مسار المصادقة مباشرةً: يشير هذا الرابط إلى صفحة تحاول إرسال المستخدم من خلال مسار التفويض. يطلب التطبيق إذنًا لإرسال طلبات بيانات من واجهة برمجة التطبيقات معتمَدة نيابةً عن المستخدم.
  • إبطال بيانات الاعتماد الحالية: يشير هذا الرابط إلى صفحة تُبطل الأذونات التي منحها المستخدم للتطبيق.
  • محو بيانات اعتماد جلسة Flask: يمحو هذا الرابط بيانات اعتماد التفويض المخزَّنة في جلسة Flask. يتيح لك ذلك معرفة ما سيحدث إذا حاول مستخدم سبق له منح الإذن لتطبيقك تنفيذ طلب بيانات من واجهة برمجة التطبيقات في جلسة جديدة. ويتيح لك أيضًا الاطّلاع على الردّ من واجهة برمجة التطبيقات الذي سيتلقّاه تطبيقك إذا ألغى المستخدم الأذونات الممنوحة لتطبيقك، وحاول تطبيقك مع ذلك تفويض طلب باستخدام رمز مميّز للوصول تم إلغاؤه.
# -*- coding: utf-8 -*-

import os
import flask
import json
import requests

import google.oauth2.credentials
import google_auth_oauthlib.flow
import googleapiclient.discovery

# This variable specifies the name of a file that contains the OAuth 2.0
# information for this application, including its client_id and client_secret.
CLIENT_SECRETS_FILE = "client_secret.json"

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

  # Load client secrets from the server-side file.
  with open(CLIENT_SECRETS_FILE, 'r') as f:
      client_config = json.load(f)['web']

  # Load user-specific credentials from the session.
  session_credentials = flask.session['credentials']

  # Reconstruct the credentials object.
  credentials = google.oauth2.credentials.Credentials(
      refresh_token=session_credentials.get('refresh_token'),
      scopes=session_credentials.get('granted_scopes'),
      token=session_credentials.get('token'),
      client_id=client_config.get('client_id'),
      client_secret=client_config.get('client_secret'),
      token_uri=client_config.get('token_uri'))

  revoke = requests.post('https://oauth2.googleapis.com/revoke',
      params={'token': credentials.token},
      headers = {'content-type': 'application/x-www-form-urlencoded'})

  status_code = getattr(revoke, 'status_code')
  if status_code == 200:
    # Clear the user's session credentials after successful revocation
    if 'credentials' in flask.session:
        del flask.session['credentials']
        del flask.session['features']
    return('Credentials successfully revoked.' + print_index_table())
  else:
    return('An error occurred.' + print_index_table())

@app.route('/clear')
def clear_credentials():
  if 'credentials' in flask.session:
    del flask.session['credentials']
  return ('Credentials have been cleared.<br><br>' +
          print_index_table())

def credentials_to_dict(credentials):
  return {'token': credentials.token,
          'refresh_token': credentials.refresh_token,
          'granted_scopes': credentials.granted_scopes}

def 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="/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. في وحدة تحكّم واجهة برمجة التطبيقات، أضِف عنوان 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) الخاصة بالمضيف المحلي (بما في ذلك معرّفات الموارد المنتظمة الخاصة بعنوان IP للمضيف المحلي).

المضيف

لا يمكن أن تكون المضيفات عناوين IP أولية. يتم استثناء عناوين IP الخاصة بالمضيف المحلي من هذه القاعدة.

النطاق
  • يجب أن تنتمي نطاقات المستوى الأعلى (TLD) للمضيف إلى قائمة اللاحقات العلنية.
  • لا يمكن أن تكون نطاقات المضيف “googleusercontent.com”.
  • لا يمكن أن تحتوي معرّفات الموارد المنتظمة (URI) لإعادة التوجيه على نطاقات لتقصير عناوين URL (مثل goo.gl) ما لم يكن التطبيق يملك النطاق. بالإضافة إلى ذلك، إذا اختار تطبيق يملك نطاقًا لخدمة اختصار عناوين URL إعادة التوجيه إلى ذلك النطاق، يجب أن يحتوي معرّف الموارد المنتظم (URI) لإعادة التوجيه على “/google-callback/” في مساره أو ينتهي بـ “/google-callback”.
  • Userinfo

    لا يمكن أن تحتوي عناوين URI لإعادة التوجيه على المكوّن الفرعي userinfo.

    Path

    لا يمكن أن تحتوي عناوين URI لإعادة التوجيه على ثغرة path traversal (تُعرف أيضًا باسم تتبُّع الدليل)، والتي يتم تمثيلها بـ “/..” أو “\..” أو ترميز عنوان 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 المضمّنة في الردّ.
    • يتضمّن التفويض المجمّع جميع النطاقات التي منحها المستخدم لمشروع واجهة برمجة التطبيقات حتى إذا تم طلب المنح من عملاء مختلفين. على سبيل المثال، إذا منح مستخدم إذن الوصول إلى نطاق واحد باستخدام عميل سطح المكتب الخاص بالتطبيق، ثم منح نطاقًا آخر إلى التطبيق نفسه من خلال عميل تطبيقات الجوّال، سيتضمن التفويض المجمّع كلا النطاقين.
    • إذا ألغيت رمزًا مميزًا يمثّل تفويضًا مجمّعًا، يتم في الوقت نفسه إلغاء إمكانية الوصول إلى جميع نطاقات هذا التفويض نيابةً عن المستخدم المرتبط به.

    تستخدم عينات التعليمات البرمجية الخاصة باللغة في الخطوة 1: ضبط التفويض إعادة التوجيه إلى خادم OAuth 2.0 من Google التفويض المتزايد. تعرض عيّنات التعليمات البرمجية التالية أيضًا الرمز الذي تحتاج إلى إضافته لاستخدام التفويض المتزايد.

    PHP

    $client->setIncludeGrantedScopes(true);

    Python

    في Python، اضبط وسيطة الكلمة الرئيسية include_granted_scopes على true لضمان أن يتضمّن طلب التفويض النطاقات التي تم منحها سابقًا. من المحتمل جدًا ألا تكون include_granted_scopes هي وسيطة الكلمة الرئيسية الوحيدة التي تضبطها، كما هو موضّح في المثال أدناه.

    authorization_url, state = flow.authorization_url(
        # Enable offline access so that you can refresh an access token without
        # re-prompting the user for permission. Recommended for web server apps.
        access_type='offline',
        # Enable incremental authorization. Recommended as a best practice.
        include_granted_scopes='true')

    Ruby

    auth_client.update!(
      :additional_parameters => {"include_granted_scopes" => "true"}
    )

    Node.js

    const authorizationUrl = oauth2Client.generateAuthUrl({
      // 'online' (default) or 'offline' (gets refresh_token)
      access_type: 'offline',
      /** Pass in the scopes array defined above.
        * Alternatively, if only one scope is needed, you can pass a scope URL as a string */
      scope: scopes,
      // Enable incremental authorization. Recommended as a best practice.
      include_granted_scopes: true
    });

    HTTP/REST

    في هذا المثال، يطلب التطبيق الذي يجري الطلب الوصول إلى بيانات "إحصاءات YouTube" الخاصة بالمستخدم، بالإضافة إلى أي إذن وصول آخر منحه المستخدم للتطبيق.

    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
    
          

    Refresh 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");

    بعد أن يمنح المستخدم إذن الوصول بلا إنترنت إلى النطاقات المطلوبة، يمكنك مواصلة استخدام عميل واجهة برمجة التطبيقات للوصول إلى 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')

    بعد أن يمنح المستخدم إذن الوصول بلا إنترنت إلى النطاقات المطلوبة، يمكنك مواصلة استخدام عميل واجهة برمجة التطبيقات للوصول إلى Google APIs نيابةً عن المستخدم عندما يكون غير متصل بالإنترنت. سيُعيد عنصر العميل تحميل رمز الدخول حسب الحاجة.

    Ruby

    إذا كان تطبيقك يحتاج إلى الوصول إلى Google API بلا إنترنت، اضبط نوع وصول عميل واجهة برمجة التطبيقات على offline:

    auth_client.update!(
      :additional_parameters => {"access_type" => "offline"}
    )

    بعد أن يمنح المستخدم إذن الوصول بلا إنترنت إلى النطاقات المطلوبة، يمكنك مواصلة استخدام عميل واجهة برمجة التطبيقات للوصول إلى Google APIs نيابةً عن المستخدم عندما يكون غير متصل بالإنترنت. سيُعيد عنصر العميل تحميل رمز الدخول حسب الحاجة.

    Node.js

    إذا كان تطبيقك يحتاج إلى الوصول إلى Google 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
    });

    بعد أن يمنح المستخدم إذن الوصول بلا إنترنت إلى النطاقات المطلوبة، يمكنك مواصلة استخدام عميل واجهة برمجة التطبيقات للوصول إلى Google APIs نيابةً عن المستخدم عندما يكون غير متصل بالإنترنت. سيُعيد عنصر العميل تحميل رمز الدخول حسب الحاجة.

    تنتهي صلاحية رموز الدخول. ستستخدم هذه المكتبة تلقائيًا رمزًا مميزًا لإعادة التحميل للحصول على رمز مميز جديد للدخول إذا كان على وشك انتهاء الصلاحية. هناك طريقة سهلة للتأكّد من تخزين أحدث الرموز المميّزة دائمًا، وهي استخدام حدث الرموز المميّزة:

    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`
    });

    بعد حصول العميل على رمز مميز لإعادة التحميل، سيتم الحصول على رموز الدخول وإعادة تحميلها تلقائيًا في الطلب التالي لواجهة برمجة التطبيقات.

    HTTP/REST

    لتحديث رمز الدخول، يرسل تطبيقك طلب HTTPS POST إلى خادم التفويض في Google (https://oauth2.googleapis.com/token) يتضمّن المَعلمات التالية في نص الطلب:

    الاسم القيمة
    client_id معرِّف العميل الذي تم الحصول عليه من وحدة تحكّم واجهة برمجة التطبيقات
    client_secret اختياريّ

    سر العميل الذي تم الحصول عليه من وحدة تحكّم واجهة برمجة التطبيقات

    grant_type كما هو محدّد في مواصفات OAuth 2.0، يجب ضبط قيمة هذا الحقل على refresh_token.
    refresh_token الرمز المميز لإعادة التحميل الذي تم إرجاعه من تبادل رمز التفويض

    على الرغم من أنّ استخدام DPoP اختياري، ننصح به لتعزيز الأمان. لاستخدام DPoP مع رمز مميز لإعادة التحميل، يجب أن ينشئ تطبيقك رمز JWT جديدًا وفريدًا لإثبات DPoP لكل طلب إلى نقطة نهاية الرمز المميز. يجب توقيع هذا الإثبات باستخدام المفتاح الخاص نفسه الذي تم استخدامه أثناء عملية تبادل رمز التفويض الأولية. يضيف تطبيقك الإثبات كعنوان طلب HTTP.

    العنوان مطلوب الوصف
    DPoP اختياري إثبات DPoP هو رمز JWT يثبت حيازة مفتاح خاص. هذا عنوان وليس مَعلمة. وفي حال توفيره، يتم ربط الرموز المميّزة التي تم عرضها بهذا المفتاح. يجب إنشاء إثبات جديد وفريد لكل طلب، ويجب أن يتضمّن المطالبتَين htm (طريقة HTTP) وhtu (معرّف الموارد المنتظم لبروتوكول HTTP) اللتين تتطابقان مع الطلب.

    يعرض المقتطف التالي نموذجًا لطلب:

    POST /token HTTP/1.1
    Host: oauth2.googleapis.com
    Content-Type: application/x-www-form-urlencoded
    DPoP: DPOP_PROOF_JWT
    
    client_id=your_client_id&
    refresh_token=refresh_token&
    grant_type=refresh_token

    لاستخدام DPoP مع رمز مميّز لإعادة التحميل، يجب إنشاء رمز JWT جديد وفريد لإثبات DPoP للطلب. راجِع إنشاء إثبات DPoP للحصول على تعليمات مفصّلة حول إنشاء مفتاحَي التشفير وإنشاء رمز JWT.

    تتم الإشارة إلى عملية التبادل الناجحة من خلال استجابة 200 OK تحتوي على رمز دخول جديد. عند استخدام DPoP، يكون token_type هو Bearer. ويؤكّد الردّ الناجح قبول إثبات DPoP للرمز المميّز لإعادة التحميل. قد تعرض Google أيضًا عنوان HTTP جديدًا باسم DPoP-Nonce في الاستجابة. وفي حال عرضه، يجب أن يخزّن العميل هذا الرقم الخاص مؤقتًا ويضمّنه في مطالبة nonce لإثبات DPoP في الطلبات اللاحقة.

    يحدث فشل في التبادل إذا كان العنوان DPoP غير متوفّر أو غير صالح أو يستخدم مفتاحًا غير صحيح. للحصول على تفاصيل حول رموز خطأ DPoP المحدّدة ومعالجة الأرقام المستخدمة لمرة واحدة، يُرجى الاطّلاع على تعذُّر التبادل.

    يعرض المقتطف التالي نموذجًا لعناوين الاستجابة والنص الأساسي الناجحَين عند استخدام DPoP:

    HTTP/1.1 200 OK
    Content-Type: application/json; charset=utf-8
    DPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI
    
    {
      "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
      "expires_in": 3920,
      "scope": "https://www.googleapis.com/auth/drive.metadata.readonly",
      "token_type": "Bearer"
    }

    يُرجى العِلم أنّ هناك حدودًا لعدد الرموز المميزة لإعادة التحميل التي سيتم إصدارها، حدّ واحد لكل مجموعة من العميل والمستخدم، وحدّ آخر لكل مستخدم في جميع العملاء. يجب حفظ الرموز المميزة لإعادة التحميل في مساحة تخزين طويلة الأمد ومواصلة استخدامها طالما أنّها صالحة. إذا طلب تطبيقك عددًا كبيرًا جدًا من الرموز المميزة لإعادة التحميل، قد يتجاوز هذه الحدود، وفي هذه الحالة ستتوقف الرموز المميزة لإعادة التحميل القديمة عن العمل.

    إبطال الرمز المميّز

    في بعض الحالات، قد يرغب المستخدم في إبطال إذن الوصول الممنوح لتطبيق معيّن. ويمكن للمستخدم إبطال إذن الوصول من خلال الانتقال إلى إعدادات الحساب. لمزيد من المعلومات، يُرجى الاطّلاع على قسم إزالة إمكانية وصول الموقع الإلكتروني أو التطبيق في مستند الدعم المواقع الإلكترونية والتطبيقات التابعة لجهات خارجية التي يمكنها الوصول إلى حسابك.

    ويمكن أيضًا لأحد التطبيقات إلغاء إذن الوصول الممنوح له آليًا. يُعدّ الإبطال الآلي مهمًا في الحالات التي يلغي فيها المستخدم اشتراكه أو يزيل تطبيقًا أو عندما تتغيّر موارد واجهة برمجة التطبيقات التي يتطلّبها التطبيق بشكل كبير. بعبارة أخرى، يمكن أن يتضمّن جزء من عملية الإزالة طلب بيانات من واجهة برمجة التطبيقات لضمان إزالة الأذونات التي تم منحها للتطبيق في السابق.

    PHP

    لإبطال رمز مميّز آليًا، يمكنك طلب revokeToken():

    $client->revokeToken();

    Python

    لإبطال رمز مميّز آليًا، أرسِل طلبًا إلى https://oauth2.googleapis.com/revoke يتضمّن الرمز المميّز كمَعلمة ويضبط العنوان Content-Type:

    requests.post('https://oauth2.googleapis.com/revoke',
        params={'token': credentials.token},
        headers = {'content-type': 'application/x-www-form-urlencoded'})

    Ruby

    لإبطال رمز مميّز آليًا، أرسِل طلب HTTP إلى نقطة النهاية oauth2.revoke:

    uri = URI('https://oauth2.googleapis.com/revoke')
    response = Net::HTTP.post_form(uri, 'token' => auth_client.access_token)

    يمكن أن يكون الرمز المميز رمز دخول أو رمزًا مميزًا لإعادة التحميل. إذا كان الرمز المميز هو رمز دخول وله رمز مميز لإعادة التحميل، سيتم أيضًا إبطال الرمز المميز لإعادة التحميل.

    إذا تمت معالجة عملية الإبطال بنجاح، يكون رمز حالة الاستجابة هو 200. في حالات الخطأ، يتم عرض رمز الحالة 400 مع رمز الخطأ.

    Node.js

    لإبطال رمز مميّز آليًا، أرسِل طلب HTTPS POST إلى نقطة النهاية /revoke:

    const https = require('https');
    
    // Build the string for the POST request
    let postData = "token=" + userCredential.access_token;
    
    // Options for POST request to Google's OAuth 2.0 server to revoke a token
    let postOptions = {
      host: 'oauth2.googleapis.com',
      port: '443',
      path: '/revoke',
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(postData)
      }
    };
    
    // Set up the request
    const postReq = https.request(postOptions, function (res) {
      res.setEncoding('utf8');
      res.on('data', d => {
        console.log('Response: ' + d);
      });
    });
    
    postReq.on('error', error => {
      console.log(error)
    });
    
    // Post the request with data
    postReq.write(postData);
    postReq.end();

    يمكن أن تكون مَعلمة الرمز المميز رمز دخول أو رمزًا مميزًا لإعادة التحميل. إذا كان الرمز المميز هو رمز دخول وكان لديه رمز مميز لإعادة التحميل، سيتم أيضًا إبطال الرمز المميز لإعادة التحميل.

    إذا تمت معالجة عملية الإبطال بنجاح، يكون رمز حالة الاستجابة هو 200. في حالات الخطأ، يتم عرض رمز الحالة 400 مع رمز الخطأ.

    HTTP/REST

    لإبطال رمز مميّز آليًا، يرسل تطبيقك طلبًا إلى https://oauth2.googleapis.com/revoke ويتضمّن الرمز المميّز كمَعلمة:

    curl -d -X -POST --header "Content-type:application/x-www-form-urlencoded" \
            https://oauth2.googleapis.com/revoke?token={token}

    يمكن أن يكون الرمز المميز رمز دخول أو رمزًا مميزًا لإعادة التحميل. إذا كان الرمز المميز هو رمز دخول وكان لديه رمز مميز لإعادة التحميل، سيتم أيضًا إبطال الرمز المميز لإعادة التحميل.

    إذا تمت معالجة الإبطال بنجاح، يكون رمز حالة HTTP للاستجابة هو 200. في حالات الخطأ، يتم عرض رمز حالة HTTP ‏400 مع رمز خطأ.

    الوصول المستند إلى الوقت

    يسمح الوصول المستنِد إلى الوقت للمستخدم بمنح تطبيقك إذن الوصول إلى بياناته لفترة محدودة لإكمال إجراء معيّن. تتوفّر إمكانية الوصول المستندة إلى الوقت في منتجات Google محدّدة أثناء عملية الموافقة، ما يمنح المستخدمين خيار منح إذن الوصول لفترة زمنية محدودة. على سبيل المثال، تتيح واجهة برمجة التطبيقات لنقل البيانات نقل البيانات لمرة واحدة.

    عندما يمنح أحد المستخدمين تطبيقك إذن الوصول المستند إلى الوقت، ستنتهي صلاحية الرمز المميز لإعادة التحميل بعد المدة المحدّدة. يُرجى العِلم أنّه قد يتم إبطال الرموز المميزة لإعادة التحميل في وقت سابق في ظل ظروف معيّنة. يُرجى الاطّلاع على هذه الحالات لمعرفة التفاصيل. يمثّل الحقل refresh_token_expires_in الذي يتم عرضه في ردّ تبادل رمز التفويض الوقت المتبقي حتى انتهاء صلاحية الرمز المميز لإعادة التحميل في هذه الحالات.

    تنفيذ ميزة "الحماية العابرة للحساب"

    من الخطوات الإضافية التي يجب اتّخاذها لحماية حسابات المستخدمين تنفيذ ميزة "الحماية العابرة للحساب" من خلال الاستفادة من خدمة "الحماية العابرة للحساب" من Google. تتيح لك هذه الخدمة الاشتراك في إشعارات أحداث الأمان التي توفّر معلومات لتطبيقك حول التغييرات الرئيسية في حساب المستخدم. ويمكنك بعد ذلك استخدام المعلومات لاتخاذ الإجراءات بناءً على طريقة استجابتك للأحداث.

    في ما يلي بعض الأمثلة على أنواع الأحداث التي ترسلها خدمة "الحماية العابرة للحساب" من Google إلى تطبيقك:

    • https://schemas.openid.net/secevent/risc/event-type/sessions-revoked
    • https://schemas.openid.net/secevent/oauth/event-type/token-revoked
    • https://schemas.openid.net/secevent/risc/event-type/account-disabled

    يمكنك الاطّلاع على صفحة حماية حسابات المستخدمين من خلال ميزة "الحماية العابرة للحساب" لمزيد من المعلومات حول كيفية تنفيذ ميزة "الحماية العابرة للحساب" والاطّلاع على القائمة الكاملة للأحداث المتاحة.