توضّح هذه الصفحة كيفية استخدام تطبيقات خادم الويب لمكتبات عملاء 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 Live Streaming API استخدام حساب الخدمة. بما أنّه لا يمكن ربط حساب خدمة بحساب على YouTube، ستؤدي محاولات تفويض الطلبات باستخدام هذا المسار إلى ظهور الخطأ NoLinkedYouTubeAccount.
مكتبات العملاء
تستخدم الأمثلة الخاصة باللغة في هذه الصفحة مكتبات عملاء Google API لتنفيذ تفويض OAuth 2.0. لتشغيل عينات التعليمات البرمجية، عليك أولاً تثبيت مكتبة البرامج للغتك.
عند استخدام مكتبة عميل Google API لمعالجة مسار OAuth 2.0 في تطبيقك، تنفّذ مكتبة العميل العديد من الإجراءات التي يحتاج التطبيق إلى معالجتها بنفسه في الحالات الأخرى. على سبيل المثال، يحدّد هذا الإعداد متى يمكن للتطبيق استخدام رموز الدخول المخزَّنة أو إعادة تحميلها، ومتى يجب أن يحصل التطبيق على الموافقة مرة أخرى. تنشئ مكتبة العميل أيضًا عناوين URL صحيحة لإعادة التوجيه وتساعد في تنفيذ معالِجات إعادة التوجيه التي تستبدل رموز التفويض برموز الدخول.
تتوفّر مكتبات عملاء Google API للتطبيقات من جهة الخادم باللغات التالية:
المتطلبات الأساسية
تفعيل واجهات برمجة التطبيقات لمشروعك
يجب أن يفعّل أي تطبيق يطلب بيانات من Google APIs واجهات برمجة التطبيقات هذه في "وحدة تحكّم واجهة برمجة التطبيقات".
لتفعيل واجهة برمجة تطبيقات لمشروعك، اتّبِع الخطوات التالية:
- افتح مكتبة واجهات برمجة التطبيقات في Google API Console.
- اختَر مشروعًا أو أنشِئ مشروعًا جديدًا إذا طُلب منك ذلك.
- استخدِم صفحة "المكتبة" (Library) للعثور على YouTube Data API وتفعيلها. ابحث عن أي واجهات برمجة تطبيقات أخرى سيستخدمها تطبيقك وفعِّلها أيضًا.
إنشاء بيانات اعتماد التفويض
يجب أن يتوفّر لأي تطبيق يستخدم OAuth 2.0 للوصول إلى Google APIs بيانات اعتماد تفويض تعرّف التطبيق لخادم OAuth 2.0 من Google. توضّح الخطوات التالية كيفية إنشاء بيانات اعتماد لمشروعك. يمكن لتطبيقاتك بعد ذلك استخدام بيانات الاعتماد للوصول إلى واجهات برمجة التطبيقات التي فعّلتها لهذا المشروع.
- انتقِل إلى صفحة "العملاء".
- انقر على إنشاء عميل (Create Client).
- اختَر نوع التطبيق تطبيق ويب.
- املأ النموذج وانقر على إنشاء. يجب أن تحدّد التطبيقات التي تستخدم لغات وأُطر عمل مثل 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. |
إدارة حسابك في YouTube |
https://www. |
الاطّلاع على قائمة بأعضاء القناة النشطين حاليًا ومستواهم الحالي وتاريخ انضمامهم |
https://www. |
الاطّلاع على فيديوهاتك على YouTube وتقييماتها وتعليقاتها وترجماتها وكذلك تعديلها وحذفها نهائيًا |
https://www. |
عرض حسابك في YouTube |
https://www. |
إدارة فيديوهات YouTube |
https://www. |
عرض وإدارة أصولك والمحتوى المرتبط بها على YouTube |
https://www. |
عرض معلومات خاصة عن قناتك على 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
requestspip install --upgrade requests
راجِع ملاحظات الإصدار لمكتبة برامج Google API للغة Python إذا لم تتمكّن من ترقية Python ودليل نقل البيانات المرتبط بها.
Ruby
لتشغيل عينات تعليمات برمجية Ruby في هذا المستند، ستحتاج إلى:
- Ruby 2.6 أو إصدار أحدث
-
مكتبة Google Auth للغة Ruby:
gem install googleauth
-
مكتبات برامج Google APIs في Drive و"تقويم Google":
gem install google-apis-drive_v3 google-apis-calendar_v3
-
إطار عمل تطبيق الويب Sinatra Ruby
gem install sinatra
Node.js
لتشغيل عيّنات التعليمات البرمجية Node.js في هذا المستند، ستحتاج إلى ما يلي:
- إصدار الصيانة الطويل الأمد (LTS) أو الإصدار الطويل الأمد (LTS) النشط أو الإصدار الحالي من Node.js
-
عميل Google APIs Node.js:
npm install googleapis crypto express express-session
HTTP/REST
لا تحتاج إلى تثبيت أي مكتبات لتتمكّن من طلب نقاط نهاية OAuth 2.0 مباشرةً.
الحصول على رموز الدخول عبر OAuth 2.0
توضّح الخطوات التالية كيفية تفاعل تطبيقك مع خادم OAuth 2.0 من Google للحصول على موافقة المستخدم على تنفيذ طلب بيانات من واجهة برمجة التطبيقات نيابةً عنه. ويجب أن يحصل تطبيقك على هذه الموافقة قبل أن يتمكّن من تنفيذ طلب بيانات من واجهة برمجة تطبيقات Google يتطلّب تفويضًا من المستخدم.
تلخّص القائمة التالية هذه الخطوات بسرعة:
- يحدّد تطبيقك الأذونات التي يحتاج إليها.
- يعيد تطبيقك توجيه المستخدم إلى Google مع قائمة الأذونات المطلوبة.
- يقرّر المستخدم ما إذا كان سيمنح الأذونات لتطبيقك.
- يعرف تطبيقك القرار الذي اتخذه المستخدم.
- إذا منح المستخدم الأذونات المطلوبة، يسترد تطبيقك الرموز المميّزة اللازمة لإرسال طلبات بيانات من واجهة برمجة التطبيقات نيابةً عن المستخدم.
الخطوة 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', 'https://www.googleapis.com/auth/calendar.readonly']) # Required, indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. flow.redirect_uri = 'https://www.example.com/oauth2callback' # Generate URL for request to Google's OAuth 2.0 server. # Use kwargs to set optional request parameters. authorization_url, state = flow.authorization_url( # Recommended, enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Optional, enable incremental authorization. Recommended as a best practice. include_granted_scopes='true', # Optional, if your application knows which user is trying to authenticate, it can use this # parameter to provide a hint to the Google Authentication Server. login_hint='hint@example.com', # Optional, set prompt to 'consent' will prompt the user for consent prompt='consent')
Ruby
استخدِم ملف client_secrets.json الذي أنشأته لضبط عنصر عميل في تطبيقك. عند ضبط عنصر عميل، عليك تحديد النطاقات التي يحتاج تطبيقك إلى الوصول إليها، بالإضافة إلى عنوان URL لنقطة نهاية المصادقة في تطبيقك، والتي ستتعامل مع الاستجابة من خادم OAuth 2.0.
على سبيل المثال، لطلب التشغيل بلا إنترنت لاسترداد بيانات مستخدم على YouTube:
require 'googleauth' require 'googleauth/web_user_authorizer' require 'googleauth/stores/redis_token_store' require 'google/apis/youtube_v3' require 'google/apis/calendar_v3' # Required, call the from_file method to retrieve the client ID from a # client_secret.json file. client_id = Google::Auth::ClientId.from_file('/path/to/client_secret.json') # Required, scope value # Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. scope = ['Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY', 'Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY'] # Required, Authorizers require a storage instance to manage long term persistence of # access and refresh tokens. token_store = Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new) # Required, indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. callback_uri = '/oauth2callback' # To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI # from the client_secret.json file. To get these credentials for your application, visit # https://console.cloud.google.com/apis/credentials. authorizer = Google::Auth::WebUserAuthorizer.new(client_id, scope, token_store, callback_uri)
يستخدم تطبيقك عنصر العميل لتنفيذ عمليات OAuth 2.0، مثل إنشاء عناوين URL لطلبات التفويض وتطبيق رموز الدخول على طلبات HTTP.
Node.js
ينشئ مقتطف الرمز التالي عنصر google.auth.OAuth2 يحدّد المَعلمات في طلب التفويض.
يستخدم هذا العنصر معلومات من ملف client_secret.json لتحديد تطبيقك. لطلب أذونات من مستخدم لاسترداد رمز دخول، عليك إعادة توجيهه إلى صفحة موافقة. لإنشاء عنوان URL لصفحة الموافقة، اتّبِع الخطوات التالية:
const {google} = require('googleapis'); const crypto = require('crypto'); const express = require('express'); const session = require('express-session'); /** * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI * from the client_secret.json file. To get these credentials for your application, visit * https://console.cloud.google.com/apis/credentials. */ const oauth2Client = new google.auth.OAuth2( YOUR_CLIENT_ID, YOUR_CLIENT_SECRET, YOUR_REDIRECT_URL ); // Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. const scopes = [ 'https://www.googleapis.com/auth/youtube.force-ssl', 'https://www.googleapis.com/auth/calendar.readonly' ]; // Generate a secure random state value. const state = crypto.randomBytes(32).toString('hex'); // Store state in the session req.session.state = state; // Generate a url that asks permissions for the Drive activity and Google Calendar scope const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true, // Include the state parameter to reduce the risk of CSRF attacks. state: state });
ملاحظة مهمة: لا يتم عرض refresh_token إلا في أول عملية تفويض.
مزيد من التفاصيل
HTTP/REST
تتوفّر نقطة نهاية OAuth 2.0 من Google على الرابط https://accounts.google.com/o/oauth2/v2/auth. لا يمكن الوصول إلى نقطة النهاية هذه إلا عبر بروتوكول HTTPS. يتم رفض اتصالات HTTP العادية.
يتيح خادم التفويض في Google استخدام مَعلمات سلسلة طلبات البحث التالية لتطبيقات خادم الويب:
| المعلمات | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
client_id |
مطلوب
معرّف العميل لتطبيقك يمكنك العثور على هذه القيمة في صفحة "العملاء" ضمن Cloud Console. |
||||||||||||||||
redirect_uri |
مطلوب
يحدّد هذا المَعلمة المكان الذي يعيد خادم واجهة برمجة التطبيقات توجيه المستخدم إليه بعد أن يكمل المستخدم
عملية التفويض. يجب أن تتطابق القيمة تمامًا مع أحد عناوين URI لإعادة التوجيه المصرّح بها لعميل OAuth 2.0، والتي تم ضبطها في صفحة العملاء في Cloud Console الخاصة بالعميل. إذا لم تتطابق هذه القيمة مع معرّف موارد منتظم (URI) معتمَد لإعادة التوجيه لـ يُرجى العِلم أنّ المخطط |
||||||||||||||||
response_type |
مطلوب
يحدّد هذا الإعداد ما إذا كانت نقطة نهاية Google OAuth 2.0 ستعرض رمز تفويض. اضبط قيمة المَعلمة على |
||||||||||||||||
scope |
مطلوب
قائمة مفصولة بمسافات تتضمّن النطاقات التي تحدّد الموارد التي يمكن لتطبيقك الوصول إليها نيابةً عن المستخدم. تُعلم هذه القيم شاشة طلب الموافقة التي تعرضها Google للمستخدم. تتيح النطاقات لتطبيقك طلب الوصول إلى الموارد التي يحتاج إليها فقط، كما تتيح للمستخدمين التحكّم في مقدار الوصول الذي يمنحونه لتطبيقك. وبالتالي، هناك علاقة عكسية بين عدد النطاقات المطلوبة واحتمال الحصول على موافقة المستخدم. يستخدم الإصدار الثالث من YouTube Data API النطاقات التالية:
يوفّر مستند نطاقات OAuth 2.0 API قائمة كاملة بالنطاقات التي يمكنك استخدامها للوصول إلى Google APIs. ننصحك بأن يطلب تطبيقك الوصول إلى نطاقات التفويض في السياق كلما أمكن ذلك. من خلال طلب الوصول إلى بيانات المستخدم في السياق، باستخدام التفويض المتزايد، يمكنك مساعدة المستخدمين على فهم سبب احتياج تطبيقك إلى الوصول الذي يطلبه. |
||||||||||||||||
access_type |
مقترَح
يشير هذا الحقل إلى ما إذا كان بإمكان تطبيقك إعادة تحميل رموز الدخول عندما لا يكون المستخدم متصلاً
بالمتصفّح. القيم الصالحة للمَعلمة هي اضبط القيمة على |
||||||||||||||||
state |
مقترَح
تحدّد أي قيمة سلسلة يستخدمها تطبيقك للحفاظ على الحالة بين طلب التفويض واستجابة خادم التفويض.
يعرض الخادم القيمة الدقيقة التي ترسلها كزوج يمكنك استخدام هذه المَعلمة لأغراض متعددة، مثل توجيه المستخدم إلى المورد الصحيح في تطبيقك وإرسال أرقام تُستخدم لمرة واحدة والحدّ من تزوير الطلبات من مواقع إلكترونية مختلفة. بما أنّه يمكن تخمين |
||||||||||||||||
include_granted_scopes |
اختياريّ
تتيح هذه الميزة للتطبيقات استخدام التفويض المتزايد لطلب الوصول إلى نطاقات إضافية في السياق. إذا ضبطت قيمة هذه المَعلمة على |
||||||||||||||||
enable_granular_consent |
اختياريّ
القيمة التلقائية هي عندما تفعّل Google الأذونات الدقيقة لأحد التطبيقات، لن يكون لهذه المَعلمة أي تأثير. |
||||||||||||||||
login_hint |
اختياريّ
إذا كان تطبيقك يعرف المستخدم الذي يحاول المصادقة، يمكنه استخدام هذه المَعلمة لتقديم تلميح إلى خادم مصادقة Google. يستخدم الخادم التلميح لتبسيط عملية تسجيل الدخول، إما من خلال ملء حقل البريد الإلكتروني مسبقًا في نموذج تسجيل الدخول أو من خلال اختيار جلسة تسجيل الدخول المتعددة المناسبة. اضبط قيمة المَعلمة على عنوان بريد إلكتروني أو معرّف |
||||||||||||||||
prompt |
اختياريّ
قائمة بالطلبات التي يتم عرضها للمستخدم، وتكون حسّاسة لحالة الأحرف ومفصولة بمسافات إذا لم تحدّد هذه المَعلمة، سيُطلب من المستخدم منح الإذن في المرة الأولى فقط التي يطلب فيها مشروعك الوصول إلى البيانات. لمزيد من المعلومات، يُرجى الاطّلاع على طلب إعادة الموافقة. القيم المحتمَلة هي:
|
||||||||||||||||
الخطوة 2: إعادة التوجيه إلى خادم OAuth 2.0 من Google
إعادة توجيه المستخدم إلى خادم OAuth 2.0 من Google لبدء عملية المصادقة والتفويض يحدث هذا عادةً عندما يحتاج تطبيقك إلى الوصول إلى بيانات المستخدم لأول مرة. في حالة التفويض المتزايد، تحدث هذه الخطوة أيضًا عندما يحتاج تطبيقك لأول مرة إلى الوصول إلى موارد إضافية ليس لديه إذن بالوصول إليها.
PHP
- أنشئ عنوان URL لطلب الوصول من خادم OAuth 2.0 في Google:
$auth_url = $client->createAuthUrl(); - إعادة توجيه المستخدم إلى
$auth_url:header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
Python
يوضّح هذا المثال كيفية إعادة توجيه المستخدم إلى عنوان URL الخاص بالتفويض باستخدام إطار عمل تطبيق الويب Flask:
return flask.redirect(authorization_url)
Ruby
- أنشئ عنوان URL لطلب الوصول من خادم OAuth 2.0 في Google:
auth_uri = authorizer.get_authorization_url(request: request)
- أعِد توجيه المستخدم إلى
auth_uri.
Node.js
-
استخدِم عنوان URL الذي تم إنشاؤه
authorizationUrlمن طريقة الخطوة 1generateAuthUrlلطلب الوصول من خادم OAuth 2.0 من Google. -
أعِد توجيه المستخدم إلى
authorizationUrl.res.redirect(authorizationUrl);
HTTP/REST
نموذج إعادة التوجيه إلى خادم التفويض في Google
يظهر مثال على عنوان URL أدناه، مع فواصل أسطر ومسافات لسهولة القراءة. يطلب عنوان URL
الوصول إلى نطاق يسمح بالوصول لاسترداد بيانات المستخدم على YouTube. ويستخدم التفويض
التدريجي (include_granted_scopes=true) لضمان أن رمز الدخول الجديد
يغطي أي نطاقات سبق للمستخدم منح التطبيق إذن الوصول إليها. يتم أيضًا ضبط العديد من المَعلمات الأخرى في المثال.
https://accounts.google.com/o/oauth2/v2/auth? scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.force-ssl& access_type=offline& include_granted_scopes=true& response_type=code& state=state_parameter_passthrough_value& redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback& 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.force-ssl& access_type=offline& include_granted_scopes=true& response_type=code& state=state_parameter_passthrough_value& redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback& 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 من سطر الأوامر:
- إنشاء مفتاحَي تشفير EC P-256:
openssl ecparam -name prime256v1 -genkey -noout -out dpop_private.pem openssl ec -in dpop_private.pem -pubout -out dpop_public.pem
- إنشاء عنوان 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" } } - إنشاء حمولة 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".
- بالنسبة إلى عمليات تبادل رموز التفويض، يجب أن تكون قيمة
- التوقيع على الإثبات:
ادمج العنوان المرمّز والحمولة مع نقطة (
.)، ثم وقِّع النتيجة باستخدام مفتاحك الخاص باستخدام 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 https://www.googleapis.com/auth/calendar.readonly",
"refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}الأخطاء
عند استبدال رمز التفويض برمز دخول، قد يظهر لك الخطأ التالي بدلاً من الاستجابة المتوقعة. يتم في هذا القسم إدراج رموز الأخطاء الشائعة والحلول المقترَحة.
invalid_grant
رمز التفويض المقدَّم غير صالح أو تنسيقه غير صحيح. اطلب رمزًا جديدًا من خلال إعادة بدء عملية OAuth لمطالبة المستخدم بالموافقة مرة أخرى.
الخطوة 6: التحقّق من النطاقات التي منحها المستخدمون
عند طلب أذونات (نطاقات) متعددة، قد لا يمنح المستخدمون تطبيقك إذن الوصول إليها جميعًا. يجب أن يتحقّق تطبيقك من النطاقات التي تم منحها فعليًا وأن يتعامل بسلاسة مع الحالات التي يتم فيها رفض بعض الأذونات، وذلك عادةً من خلال إيقاف الميزات التي تعتمد على تلك النطاقات المرفوضة.
ولكن هناك استثناءات في هذا الصدد. تتجاوز تطبيقات Google Workspace Enterprise التي تتضمّن تفويضًا على مستوى النطاق، أو التطبيقات التي تم وضع علامة عليها على أنّها موثوق بها، شاشة طلب الموافقة على الأذونات الدقيقة. بالنسبة إلى هذه التطبيقات، لن تظهر للمستخدمين شاشة طلب الموافقة على الأذونات الدقيقة. بدلاً من ذلك، سيتلقّى تطبيقك إما جميع النطاقات المطلوبة أو لن يتلقّى أيًا منها.
لمزيد من المعلومات التفصيلية، يُرجى الاطّلاع على كيفية التعامل مع الأذونات الدقيقة.
PHP
للتحقّق من النطاقات التي منحها المستخدم، استخدِم طريقة getGrantedScope():
// Space-separated string of granted scopes if it exists, otherwise null. $granted_scopes = $client->getOAuth2Service()->getGrantedScope(); // Determine which scopes user granted and build a dictionary $granted_scopes_dict = [ 'Drive' => str_contains($granted_scopes, Google\Service\Drive::DRIVE_METADATA_READONLY), 'Calendar' => str_contains($granted_scopes, Google\Service\Calendar::CALENDAR_READONLY) ];
Python
يحتوي عنصر credentials الذي تم عرضه على سمة granted_scopes، وهي قائمة بالنطاقات التي منحها المستخدم لتطبيقك.
credentials = flow.credentials flask.session['credentials'] = { 'token': credentials.token, 'refresh_token': credentials.refresh_token, 'granted_scopes': credentials.granted_scopes}
تتحقّق الدالة التالية من النطاقات التي منحها المستخدم لتطبيقك.
def check_granted_scopes(credentials): features = {} if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['granted_scopes']: features['drive'] = True else: features['drive'] = False if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['granted_scopes']: features['calendar'] = True else: features['calendar'] = False return features
Ruby
عند طلب نطاقات متعددة في آنٍ واحد، تحقَّق من النطاقات التي تم منحها من خلال السمة scope في الكائن credentials.
# User authorized the request. Now, check which scopes were granted. if credentials.scope.include?(Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY) # User authorized read-only Drive activity permission. # Calling the APIs, etc else # User didn't authorize read-only Drive activity permission. # Update UX and application accordingly end # Check if user authorized Calendar read permission. if credentials.scope.include?(Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY) # User authorized Calendar read permission. # Calling the APIs, etc. else # User didn't authorize Calendar read permission. # Update UX and application accordingly end
Node.js
عند طلب نطاقات متعددة في آنٍ واحد، تحقَّق من النطاقات التي تم منحها من خلال السمة scope في الكائن tokens.
// User authorized the request. Now, check which scopes were granted. if (tokens.scope.includes('https://www.googleapis.com/auth/youtube.force-ssl')) { // User authorized read-only Drive activity permission. // Calling the APIs, etc. } else { // User didn't authorize read-only Drive activity permission. // Update UX and application accordingly } // Check if user authorized Calendar read permission. if (tokens.scope.includes('https://www.googleapis.com/auth/calendar.readonly')) { // User authorized Calendar read permission. // Calling the APIs, etc. } else { // User didn't authorize Calendar read permission. // Update UX and application accordingly }
HTTP/REST
للتحقّق مما إذا كان المستخدم قد منح تطبيقك إذن الوصول إلى نطاق معيّن،
افحص الحقل scope في استجابة رمز الدخول. نطاقات الوصول التي يمنحها رمز الدخول، ويتم التعبير عنها كقائمة من السلاسل الحسّاسة لحالة الأحرف والمفصولة بمسافات.
على سبيل المثال، يشير نموذج استجابة رمز الدخول التالي إلى أنّ المستخدم قد منح تطبيقك إذن الوصول إلى نشاط Drive للقراءة فقط وأذونات أحداث "تقويم Google":
{ "access_token": "1/fFAGRNJru1FTz70BzhT3Zg", "expires_in": 3920, "token_type": "Bearer", "scope": "https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/calendar.readonly", "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI" }
استدعاء Google APIs
PHP
استخدِم رمز الدخول لطلب بيانات من Google APIs من خلال إكمال الخطوات التالية:
- إذا كنت بحاجة إلى تطبيق رمز مميّز للوصول إلى عنصر
Google\Clientجديد، على سبيل المثال، إذا خزّنت الرمز المميّز للوصول في جلسة مستخدم، استخدِم طريقةsetAccessToken:$client->setAccessToken($access_token); - أنشئ عنصر خدمة لواجهة برمجة التطبيقات التي تريد طلبها. يمكنك إنشاء كائن خدمة من خلال
تقديم كائن
Google\Clientمعتمَد إلى الدالة الإنشائية لواجهة برمجة التطبيقات التي تريد طلبها. على سبيل المثال، لطلب بيانات من YouTube Data API:$youtube = new Google_Service_YouTube($client); - أرسِل طلبات إلى خدمة واجهة برمجة التطبيقات باستخدام
الواجهة التي يوفّرها عنصر الخدمة.
على سبيل المثال، لاستخدام YouTube Data API من أجل استرداد قائمة بأحداث البث المباشر لقناة المستخدم الذي تمت مصادقته:
$broadcasts = $youtube->liveBroadcasts->listLiveBroadcasts('id,snippet', [ 'mine' => true ]);
Python
بعد الحصول على رمز الدخول، يمكن لتطبيقك استخدام هذا الرمز لتفويض طلبات البيانات من واجهة برمجة التطبيقات نيابةً عن حساب مستخدم أو حساب خدمة معيّن. استخدِم بيانات اعتماد التفويض الخاصة بالمستخدم لإنشاء عنصر خدمة لواجهة برمجة التطبيقات التي تريد طلبها، ثم استخدِم هذا العنصر لإرسال طلبات بيانات من واجهة برمجة التطبيقات المصرّح بها.
- أنشئ عنصر خدمة لواجهة برمجة التطبيقات التي تريد طلبها. يمكنك إنشاء عنصر خدمة من خلال
استدعاء طريقة
buildفي مكتبةgoogleapiclient.discoveryمع اسم واجهة برمجة التطبيقات وإصدارها وبيانات اعتماد المستخدم: على سبيل المثال، لاستدعاء الإصدار 3 من YouTube Data API:from googleapiclient.discovery import build youtube = build('youtube', 'v3', credentials=credentials)
- أرسِل طلبات إلى خدمة واجهة برمجة التطبيقات باستخدام
الواجهة التي يوفّرها عنصر الخدمة.
على سبيل المثال، لاستخدام YouTube Data API من أجل استرداد قائمة بأحداث البث المباشر لقناة المستخدم الذي تمت مصادقته:
broadcasts = youtube.liveBroadcasts().list(part='id,snippet', mine=True).execute()
Ruby
بعد الحصول على رمز مميّز للوصول، يمكن لتطبيقك استخدام هذا الرمز لإرسال طلبات بيانات من واجهة برمجة التطبيقات نيابةً عن حساب مستخدم أو حساب خدمة معيّن. استخدِم بيانات اعتماد التفويض الخاصة بالمستخدم لإنشاء عنصر خدمة لواجهة برمجة التطبيقات التي تريد طلبها، ثم استخدِم هذا العنصر لإرسال طلبات بيانات من واجهة برمجة التطبيقات المصرّح بها.
- أنشئ عنصر خدمة لواجهة برمجة التطبيقات التي تريد طلبها.
على سبيل المثال، لطلب الإصدار 3 من YouTube Data API:
youtube = Google::Apis::YoutubeV3::YouTubeService.new
- اضبط بيانات الاعتماد في الخدمة:
youtube.authorization = credentials
- أرسِل طلبات إلى خدمة واجهة برمجة التطبيقات باستخدام
الواجهة
التي يوفّرها عنصر الخدمة.
على سبيل المثال، لاستخدام YouTube Data API من أجل استرداد قائمة بأحداث البث المباشر لقناة المستخدم الذي تمت مصادقته:
broadcasts = youtube.list_liveBroadcasts('id,snippet', mine: true)
ويمكن بدلاً من ذلك تقديم التفويض لكل طريقة على حدة من خلال توفير المَعلمة options للطريقة:
broadcasts = youtube.list_liveBroadcasts('id,snippet', mine: true)
Node.js
بعد الحصول على رمز الدخول وضبطه على العنصر OAuth2، استخدِم العنصر
لطلب بيانات من Google APIs. يمكن لتطبيقك استخدام هذا الرمز المميز لتفويض طلبات البيانات من واجهة برمجة التطبيقات نيابةً عن
حساب مستخدم أو حساب خدمة معيّن. أنشئ عنصر خدمة لواجهة برمجة التطبيقات التي تريد طلبها.
على سبيل المثال، يستخدم الرمز التالي Google Drive API لعرض أسماء الملفات في Drive الخاص بالمستخدم.
const { google } = require('googleapis'); // Example of using Google Drive API to list filenames in user's Drive. const drive = google.drive('v3'); drive.files.list({ auth: oauth2Client, pageSize: 10, fields: 'nextPageToken, files(id, name)', }, (err1, res1) => { if (err1) return console.log('The API returned an error: ' + err1); const files = res1.data.files; if (files.length) { console.log('Files:'); files.map((file) => { console.log(`${file.name} (${file.id})`); }); } else { console.log('No files found.'); } });
HTTP/REST
بعد حصول تطبيقك على رمز مميّز للوصول، يمكنك استخدام الرمز لإجراء طلبات إلى واجهة برمجة تطبيقات Google نيابةً عن حساب مستخدم معيّن إذا تم منح نطاقات الوصول التي تتطلّبها واجهة برمجة التطبيقات. لإجراء ذلك، يجب تضمين رمز الدخول في طلب موجّه إلى واجهة برمجة التطبيقات من خلال تضمين إما مَعلمة طلب البحث access_token أو قيمة عنوان HTTP Authorization Bearer. ويُفضَّل استخدام عنوان HTTP كلما أمكن ذلك، لأنّ سلاسل طلبات البحث تكون عادةً مرئية في سجلّات الخادم. في معظم الحالات، يمكنك استخدام مكتبة برامج لإعداد طلباتك إلى Google APIs (على سبيل المثال، عند طلب YouTube Live Streaming API).
يُرجى العِلم أنّ YouTube Live Streaming API لا تتيح استخدام حساب الخدمة. بما أنّه لا يمكن ربط حساب خدمة بحساب على YouTube، ستؤدي محاولات تفويض الطلبات باستخدام هذا المسار إلى ظهور الخطأ NoLinkedYouTubeAccount.
يمكنك تجربة جميع واجهات Google APIs والاطّلاع على نطاقاتها في مساحة بروتوكول OAuth 2.0.
أمثلة على طلبات HTTP GET
قد يبدو طلب البيانات من نقطة النهاية
liveBroadcasts.list (واجهة برمجة التطبيقات YouTube Live Streaming API) باستخدام عنوان HTTP Authorization: Bearer
على النحو التالي. يُرجى العِلم أنّه عليك تحديد رمز الدخول الخاص بك:
GET /youtube/v3/liveBroadcasts?part=id%2Csnippet&mine=true HTTP/1.1 Host: www.googleapis.com Authorization: Bearer access_token
في ما يلي طلب إلى واجهة برمجة التطبيقات نفسها للمستخدم الذي تمت مصادقته باستخدام مَعلمة سلسلة طلب البحث access_token:
GET https://www.googleapis.com/youtube/v3/liveBroadcasts?access_token=access_token&part=id%2Csnippet&mine=true
أمثلة على curl
يمكنك اختبار هذه الأوامر باستخدام تطبيق سطر الأوامر curl. في ما يلي مثال يستخدم خيار عنوان HTTP (المفضّل):
curl -H "Authorization: Bearer access_token" https://www.googleapis.com/youtube/v3/liveBroadcasts?part=id%2Csnippet&mine=true
أو يمكنك بدلاً من ذلك استخدام خيار مَعلمة سلسلة طلب البحث:
curl https://www.googleapis.com/youtube/v3/liveBroadcasts?access_token=access_token&part=id%2Csnippet&mine=true
مثال مُكتمل
يطبع المثال التالي كائنًا بتنسيق JSON يعرض أحداث البث المباشر لقناة المستخدم الذي تمّت مصادقته على YouTube بعد أن يمنح المستخدم الإذن للتطبيق باسترداد هذه البيانات.
PHP
لتشغيل هذا المثال:
- في وحدة تحكّم واجهة برمجة التطبيقات، أضِف عنوان URL للجهاز المحلي إلى قائمة عناوين URL لإعادة التوجيه، مثل
http://localhost:8080. - أنشئ دليلاً جديدًا وانتقل إليه. على سبيل المثال:
mkdir ~/php-oauth2-example cd ~/php-oauth2-example
- ثبِّت مكتبة برامج Google API للغة PHP باستخدام Composer:
composer require google/apiclient:^2.15.0
- أنشِئ الملفَّين
index.phpوoauth2callback.phpبالمحتوى التالي. - شغِّل المثال باستخدام خادم الويب المضمّن للاختبار في PHP:
php -S localhost:8080 ~/php-oauth2-example
index.php
<?php require_once __DIR__.'/vendor/autoload.php'; session_start(); $client = new Google\Client(); $client->setAuthConfig('client_secret.json'); // User granted permission as an access token is in the session. if (isset($_SESSION['access_token']) && $_SESSION['access_token']) { $client->setAccessToken($_SESSION['access_token']); // Check if user granted Drive permission if ($_SESSION['granted_scopes_dict']['Drive']) { echo "Drive feature is enabled."; echo "</br>"; $drive = new Drive($client); $files = array(); $response = $drive->files->listFiles(array()); foreach ($response->files as $file) { echo "File: " . $file->name . " (" . $file->id . ")"; echo "</br>"; } } else { echo "Drive feature is NOT enabled."; echo "</br>"; } // Check if user granted Calendar permission if ($_SESSION['granted_scopes_dict']['Calendar']) { echo "Calendar feature is enabled."; echo "</br>"; } else { echo "Calendar feature is NOT enabled."; echo "</br>"; } } else { // Redirect users to outh2call.php which redirects users to Google OAuth 2.0 $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php'; header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL)); } ?>
oauth2callback.php
<?php require_once __DIR__.'/vendor/autoload.php'; session_start(); $client = new Google\Client(); // Required, call the setAuthConfig function to load authorization credentials from // client_secret.json file. $client->setAuthConfigFile('client_secret.json'); $client->setRedirectUri('http://' . $_SERVER['HTTP_HOST']. $_SERVER['PHP_SELF']); // Required, to set the scope value, call the addScope function. $client->addScope(Google_Service_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(); // Space-separated string of granted scopes if it exists, otherwise null. $granted_scopes = $client->getOAuth2Service()->getGrantedScope(); // Determine which scopes user granted and build a dictionary $granted_scopes_dict = [ 'Drive' => str_contains($granted_scopes, Google\Service\Drive::DRIVE_METADATA_READONLY), 'Calendar' => str_contains($granted_scopes, Google\Service\Calendar::CALENDAR_READONLY) ]; $_SESSION['granted_scopes_dict'] = $granted_scopes_dict; $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/'; header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL)); } // An error response e.g. error=access_denied if (isset($_GET['error'])) { echo "Error: ". $_GET['error']; } ?>
Python
يستخدم هذا المثال إطار عمل Flask. ويشغّل تطبيق ويب على http://localhost:8080 يتيح لك اختبار مسار OAuth 2.0. إذا انتقلت إلى عنوان URL هذا، من المفترض أن تظهر لك خمسة روابط:
- طلب بيانات من Drive API: يشير هذا الرابط إلى صفحة تحاول تنفيذ نموذج طلب بيانات من واجهة برمجة التطبيقات إذا منح المستخدمون الإذن. وإذا لزم الأمر، يبدأ مسار التفويض. في حال نجاح الطلب، ستعرض الصفحة الردّ من واجهة برمجة التطبيقات.
- صفحة وهمية لطلب بيانات من واجهة برمجة تطبيقات التقويم: يشير هذا الرابط إلى صفحة وهمية تحاول تنفيذ طلب بيانات نموذجي من واجهة برمجة تطبيقات التقويم إذا منح المستخدمون الإذن. وتبدأ عملية التفويض إذا لزم الأمر. في حال نجاح الطلب، ستعرض الصفحة الردّ من واجهة برمجة التطبيقات.
- اختبار مسار المصادقة مباشرةً: يشير هذا الرابط إلى صفحة تحاول إرسال المستخدم من خلال مسار التفويض. يطلب التطبيق إذنًا لإرسال طلبات بيانات من واجهة برمجة التطبيقات معتمَدة نيابةً عن المستخدم.
- إبطال بيانات الاعتماد الحالية: يشير هذا الرابط إلى صفحة تُبطل الأذونات التي منحها المستخدم للتطبيق.
- محو بيانات اعتماد جلسة 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', 'https://www.googleapis.com/auth/calendar.readonly'] 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('/drive') def drive_api_request(): if 'credentials' not in flask.session: return flask.redirect('authorize') features = flask.session['features'] if features['drive']: # 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 browser session storage. 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')) youtube = googleapiclient.discovery.build( API_SERVICE_NAME, API_VERSION, credentials=credentials) broadcasts = youtube.liveBroadcasts().list(part='id,snippet', mine=True).execute() # Save credentials back to session in case access token was refreshed. flask.session['credentials'] = credentials_to_dict(credentials) return flask.jsonify(**broadcasts) else: # User didn't authorize read-only Drive activity permission. return '<p>Drive feature is not enabled.</p>' @app.route('/calendar') def calendar_api_request(): if 'credentials' not in flask.session: return flask.redirect('authorize') features = flask.session['features'] if features['calendar']: # User authorized Calendar read permission. # Calling the APIs, etc. return ('<p>User granted the Google Calendar read permission. '+ 'This sample code does not include code to call Calendar</p>') else: # User didn't authorize Calendar read permission. # Update UX and application accordingly return '<p>Calendar feature is not enabled.</p>' @app.route('/authorize') def authorize(): # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps. flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( CLIENT_SECRETS_FILE, scopes=SCOPES) # The URI created here must exactly match one of the authorized redirect URIs # for the OAuth 2.0 client, which you configured in the API Console. If this # value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch' # error. flow.redirect_uri = flask.url_for('oauth2callback', _external=True) authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Enable incremental authorization. Recommended as a best practice. include_granted_scopes='true') # Store the state so the callback can verify the auth server response. flask.session['state'] = state return flask.redirect(authorization_url) @app.route('/oauth2callback') def oauth2callback(): # Specify the state when creating the flow in the callback so that it can # verified in the authorization server response. state = flask.session['state'] flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( CLIENT_SECRETS_FILE, scopes=SCOPES, state=state) flow.redirect_uri = flask.url_for('oauth2callback', _external=True) # Use the authorization server's response to fetch the OAuth 2.0 tokens. authorization_response = flask.request.url flow.fetch_token(authorization_response=authorization_response) # Store credentials in the session. # ACTION ITEM: In a production app, you likely want to save these # credentials in a persistent database instead. credentials = flow.credentials credentials = credentials_to_dict(credentials) flask.session['credentials'] = credentials # Check which scopes user granted features = check_granted_scopes(credentials) flask.session['features'] = features return flask.redirect('/') @app.route('/revoke') def revoke(): if 'credentials' not in flask.session: return ('You need to <a href="/authorize">authorize</a> before ' + 'testing the code to revoke credentials.') # 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 check_granted_scopes(credentials): features = {} if 'https://www.googleapis.com/auth/youtube.force-ssl' in credentials['granted_scopes']: features['drive'] = True else: features['drive'] = False if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['granted_scopes']: features['calendar'] = True else: features['calendar'] = False return features def print_index_table(): return ('<table>' + '<tr><td><a href="/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="/drive">Call Drive API directly</a></td>' + '<td> Use stored credentials to call the API, you still might not be prompted to reauthorize ' + ' the application.</td></tr>' + '<tr><td><a href="/calendar">Call Calendar API directly</a></td>' + '<td> Use stored credentials to call the API, 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="/authorize">authorize</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 'google/apis/calendar_v3' require 'sinatra' configure do enable :sessions # Required, call the from_file method to retrieve the client ID from a # client_secret.json file. set :client_id, Google::Auth::ClientId.from_file('/path/to/client_secret.json') # Required, scope value # Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. scope = ['Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY', 'Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY'] # Required, Authorizers require a storage instance to manage long term persistence of # access and refresh tokens. set :token_store, Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new) # Required, indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. set :callback_uri, '/oauth2callback' # To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI # from the client_secret.json file. To get these credentials for your application, visit # https://console.cloud.google.com/apis/credentials. set :authorizer, Google::Auth::WebUserAuthorizer.new(settings.client_id, settings.scope, settings.token_store, callback_uri: settings.callback_uri) end get '/' do # NOTE: Assumes the user is already authenticated to the app user_id = request.session['user_id'] # Fetch stored credentials for the user from the given request session. # nil if none present credentials = settings.authorizer.get_credentials(user_id, request) if credentials.nil? # Generate a url that asks the user to authorize requested scope(s). # Then, redirect user to the url. redirect settings.authorizer.get_authorization_url(request: request) end # User authorized the request. Now, check which scopes were granted. if credentials.scope.include?(Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY) # User authorized read-only Drive activity permission. # Example of using Google Drive API to list filenames in user's Drive. youtube = Google::Apis::YoutubeV3::YouTubeService.new broadcasts = youtube.list_liveBroadcasts('id,snippet', mine: true) "<pre>#{JSON.pretty_generate(broadcasts.to_h)}</pre>" else # User didn't authorize read-only Drive activity permission. # Update UX and application accordingly end # Check if user authorized Calendar read permission. if credentials.scope.include?(Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY) # User authorized Calendar read permission. # Calling the APIs, etc. else # User didn't authorize Calendar read permission. # Update UX and application accordingly end end # Receive the callback from Google's OAuth 2.0 server. get '/oauth2callback' do # Handle the result of the oauth callback. Defers the exchange of the code by # temporarily stashing the results in the user's session. target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request) redirect target_url end
Node.js
لتشغيل هذا المثال:
-
في وحدة تحكّم واجهة برمجة التطبيقات، أضِف عنوان URL للجهاز المحلي إلى قائمة عناوين URL لإعادة التوجيه، مثل
http://localhost. - تأكَّد من تثبيت إصدار الصيانة الطويل الأمد (LTS) أو الإصدار الطويل الأمد (LTS) النشط أو الإصدار الحالي من Node.js.
-
أنشئ دليلاً جديدًا وانتقل إليه. على سبيل المثال:
mkdir ~/nodejs-oauth2-example cd ~/nodejs-oauth2-example
-
ثبِّت
مكتبة برامج Google API
لنظام Node.js باستخدام npm:
npm install googleapis
-
أنشِئ الملفات
main.jsبالمحتوى التالي. -
شغِّل المثال:
node .\main.js
main.js
const http = require('http'); const https = require('https'); const url = require('url'); const { google } = require('googleapis'); const crypto = require('crypto'); const express = require('express'); const session = require('express-session'); /** * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI. * To get these credentials for your application, visit * https://console.cloud.google.com/apis/credentials. */ const oauth2Client = new google.auth.OAuth2( YOUR_CLIENT_ID, YOUR_CLIENT_SECRET, YOUR_REDIRECT_URL ); // Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. const scopes = [ 'https://www.googleapis.com/auth/youtube.force-ssl', 'https://www.googleapis.com/auth/calendar.readonly' ]; /* Global variable that stores user credential in this code example. * ACTION ITEM for developers: * Store user's refresh token in your data store if * incorporating this code into your real app. * For more information on handling refresh tokens, * see https://github.com/googleapis/google-api-nodejs-client#handling-refresh-tokens */ let userCredential = null; async function main() { const app = express(); app.use(session({ secret: 'your_secure_secret_key', // Replace with a strong secret resave: false, saveUninitialized: false, })); // Example on redirecting user to Google's OAuth 2.0 server. app.get('/', async (req, res) => { // Generate a secure random state value. const state = crypto.randomBytes(32).toString('hex'); // Store state in the session req.session.state = state; // Generate a url that asks permissions for the Drive activity and Google Calendar scope const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true, // Include the state parameter to reduce the risk of CSRF attacks. state: state }); res.redirect(authorizationUrl); }); // Receive the callback from Google's OAuth 2.0 server. app.get('/oauth2callback', async (req, res) => { // Handle the OAuth 2.0 server response let q = url.parse(req.url, true).query; if (q.error) { // An error response e.g. error=access_denied console.log('Error:' + q.error); } else if (q.state !== req.session.state) { //check state value console.log('State mismatch. Possible CSRF attack'); res.end('State mismatch. Possible CSRF attack'); } else { // Get access and refresh tokens (if access_type is offline) let { tokens } = await oauth2Client.getToken(q.code); oauth2Client.setCredentials(tokens); /** Save credential to the global variable in case access token was refreshed. * ACTION ITEM: In a production app, you likely want to save the refresh token * in a secure persistent database instead. */ userCredential = tokens; // User authorized the request. Now, check which scopes were granted. if (tokens.scope.includes('https://www.googleapis.com/auth/youtube.force-ssl')) { // User authorized read-only Drive activity permission. // Example of using Google Drive API to list filenames in user's Drive. const drive = google.drive('v3'); drive.files.list({ auth: oauth2Client, pageSize: 10, fields: 'nextPageToken, files(id, name)', }, (err1, res1) => { if (err1) return console.log('The API returned an error: ' + err1); const files = res1.data.files; if (files.length) { console.log('Files:'); files.map((file) => { console.log(`${file.name} (${file.id})`); }); } else { console.log('No files found.'); } }); } else { // User didn't authorize read-only Drive activity permission. // Update UX and application accordingly } // Check if user authorized Calendar read permission. if (tokens.scope.includes('https://www.googleapis.com/auth/calendar.readonly')) { // User authorized Calendar read permission. // Calling the APIs, etc. } else { // User didn't authorize Calendar read permission. // Update UX and application accordingly } } }); // Example on revoking a token app.get('/revoke', async (req, res) => { // Build the string for the POST request let postData = "token=" + userCredential.access_token; // Options for POST request to Google's OAuth 2.0 server to revoke a token let postOptions = { host: 'oauth2.googleapis.com', port: '443', path: '/revoke', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(postData) } }; // Set up the request const postReq = https.request(postOptions, function (res) { res.setEncoding('utf8'); res.on('data', d => { console.log('Response: ' + d); }); }); postReq.on('error', error => { console.log(error) }); // Post the request with data postReq.write(postData); postReq.end(); }); const server = http.createServer(app); server.listen(8080); } main().catch(console.error);
HTTP/REST
يستخدم مثال Python هذا إطار عمل Flask ومكتبة Requests لتوضيح مسار الويب لبروتوكول OAuth 2.0. ننصح باستخدام مكتبة عملاء Google API للغة Python في هذا المسار. (يستخدم المثال في علامة التبويب Python مكتبة البرامج).
import json import flask import requests app = flask.Flask(__name__) # To get these credentials (CLIENT_ID CLIENT_SECRET) and for your application, visit # https://console.cloud.google.com/apis/credentials. CLIENT_ID = '123456789.apps.googleusercontent.com' CLIENT_SECRET = 'abc123' # Read from a file or environmental variable in a real app # Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. SCOPE = 'https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/calendar.readonly' # Indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. REDIRECT_URI = 'http://example.com/oauth2callback' @app.route('/') def index(): if 'credentials' not in flask.session: return flask.redirect(flask.url_for('oauth2callback')) credentials = json.loads(flask.session['credentials']) if credentials['expires_in'] <= 0: return flask.redirect(flask.url_for('oauth2callback')) else: # User authorized the request. Now, check which scopes were granted. if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['scope']: # User authorized read-only Drive activity permission. # Example of using Google Drive API to list filenames in user's Drive. headers = {'Authorization': 'Bearer {}'.format(credentials['access_token'])} req_uri = 'https://youtube.googleapis.com/youtube/v3/liveBroadcasts' r = requests.get(req_uri, headers=headers).text else: # User didn't authorize read-only Drive activity permission. # Update UX and application accordingly r = 'User did not authorize Drive permission.' # Check if user authorized Calendar read permission. if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['scope']: # User authorized Calendar read permission. # Calling the APIs, etc. r += 'User authorized Calendar permission.' else: # User didn't authorize Calendar read permission. # Update UX and application accordingly r += 'User did not authorize Calendar permission.' return r @app.route('/oauth2callback') def oauth2callback(): if 'code' not in flask.request.args: state = str(uuid.uuid4()) flask.session['state'] = state # Generate a url that asks permissions for the Drive activity # and Google Calendar scope. Then, redirect user to the url. auth_uri = ('https://accounts.google.com/o/oauth2/v2/auth?response_type=code' '&client_id={}&redirect_uri={}&scope={}&state={}').format(CLIENT_ID, REDIRECT_URI, SCOPE, state) return flask.redirect(auth_uri) else: if 'state' not in flask.request.args or flask.request.args['state'] != flask.session['state']: return 'State mismatch. Possible CSRF attack.', 400 auth_code = flask.request.args.get('code') data = {'code': auth_code, 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET, 'redirect_uri': REDIRECT_URI, 'grant_type': 'authorization_code'} # Exchange authorization code for access and refresh tokens (if access_type is offline) r = requests.post('https://oauth2.googleapis.com/token', data=data) flask.session['credentials'] = r.text return flask.redirect(flask.url_for('index')) if __name__ == '__main__': import uuid app.secret_key = str(uuid.uuid4()) app.debug = False app.run()
قواعد التحقّق من صحة معرّف الموارد المنتظم (URI) الخاص بإعادة التوجيه
تطبّق Google قواعد التحقّق التالية على معرّفات الموارد المنتظمة (URI) لإعادة التوجيه من أجل مساعدة المطوّرين في الحفاظ على أمان تطبيقاتهم. يجب أن تلتزم عناوين URI لإعادة التوجيه بهذه القواعد. يُرجى الاطّلاع على القسم 3 من RFC 3986 لمعرفة تعريف النطاق والمضيف والمسار وطلب البحث والمخطط ومعلومات المستخدم، والمستخدمة في هذه القواعد.
| قواعد التحقّق من الصحة | |
|---|---|
| Scheme |
يجب أن تستخدم معرّفات الموارد المنتظمة (URI) لإعادة التوجيه نظام HTTPS، وليس HTTP العادي. يُستثنى من هذه القاعدة معرّفات الموارد المنتظمة (URI) الخاصة بالمضيف المحلي (بما في ذلك معرّفات الموارد المنتظمة الخاصة بعنوان IP للمضيف المحلي). |
| المضيف |
لا يمكن أن تكون المضيفات عناوين IP أولية. يتم استثناء عناوين IP الخاصة بالمضيف المحلي من هذه القاعدة. |
| النطاق |
“googleusercontent.com”.goo.gl) ما لم
يكن التطبيق يملك النطاق. بالإضافة إلى ذلك، إذا اختار تطبيق يملك نطاقًا لخدمة اختصار عناوين URL
إعادة التوجيه إلى ذلك النطاق، يجب أن يحتوي معرّف الموارد المنتظم (URI) لإعادة التوجيه على
“/google-callback/” في مساره أو ينتهي بـ
“/google-callback”. |
| Userinfo |
لا يمكن أن تحتوي عناوين URI لإعادة التوجيه على المكوّن الفرعي userinfo. |
| Path |
لا يمكن أن تحتوي عناوين URI لإعادة التوجيه على ثغرة path traversal (تُعرف أيضًا باسم تتبُّع الدليل)،
والتي يتم تمثيلها بـ |
| طلب بحث |
لا يمكن أن تحتوي معرّفات الموارد المنتظمة (URI) لإعادة التوجيه على عمليات إعادة توجيه مفتوحة. |
| Fragment |
لا يمكن أن تحتوي معرّفات الموارد المنتظمة (URI) لإعادة التوجيه على مكوّن التجزئة. |
| الأحرف |
لا يمكن أن تحتوي عناوين URI لإعادة التوجيه على أحرف معيّنة، بما في ذلك:
|
التفويض المتزايد
في بروتوكول OAuth 2.0، يطلب تطبيقك الإذن بالوصول إلى الموارد التي يتم تحديدها من خلال النطاقات. من أفضل الممارسات المتعلّقة بتجربة المستخدم طلب الإذن بالوصول إلى الموارد في الوقت الذي تحتاج إليها فيه. ولإتاحة هذه الممارسة، يتيح خادم التفويض في Google التفويض المتزايد. تتيح لك هذه الميزة طلب النطاقات حسب الحاجة، وإذا منح المستخدم الإذن للنطاق الجديد، يتم عرض رمز تفويض يمكن استبداله برمز مميّز يحتوي على جميع النطاقات التي منحها المستخدم للمشروع.
على سبيل المثال، لنفترض أنّ أحد التطبيقات يستردّ بيانات قناة على YouTube لمستخدم مصادَق عليه، ويتيح أيضًا
للمستخدم استرداد بيانات "إحصاءات YouTube" من خلال مسار خاص. في هذه الحالة، قد يطلب التطبيق عند تسجيل الدخول الوصول إلى نطاق https://www.googleapis.com/auth/youtube.force-ssl فقط. ومع ذلك، إذا حاول المستخدم الوصول إلى بيانات "إحصاءات Google" لقناته، يمكن للتطبيق أيضًا
طلب الوصول إلى نطاق https://www.googleapis.com/auth/yt-analytics.readonly.
لتنفيذ التفويض المتزايد، عليك إكمال المسار العادي لطلب رمز الدخول، ولكن تأكَّد من أنّ طلب التفويض يتضمّن النطاقات التي تم منحها سابقًا. يتيح هذا النهج لتطبيقك تجنُّب إدارة رموز الدخول المتعددة.
تنطبق القواعد التالية على رمز الدخول الذي تم الحصول عليه من تفويض تزايدي:
- يمكن استخدام الرمز المميّز للوصول إلى الموارد التي تتوافق مع أي من النطاقات المضمّنة في التفويض الجديد المجمّع.
- عند استخدام الرمز المميز لإعادة التحميل للتفويض المجمّع من أجل الحصول على رمز مميز للوصول، يمثّل الرمز المميز للوصول التفويض المجمّع ويمكن استخدامه لأي من قيم
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
In this example, the calling application requests access to retrieve the user's YouTube data in addition to any other access that the user has already granted to the application.
GET https://accounts.google.com/o/oauth2/v2/auth?
scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.force-ssl&
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_typeHTTP query parameter toofflinewhen 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 https://www.googleapis.com/auth/calendar.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
يمكنك الاطّلاع على صفحة حماية حسابات المستخدمين من خلال ميزة "الحماية العابرة للحساب" لمزيد من المعلومات حول كيفية تنفيذ ميزة "الحماية العابرة للحساب" والاطّلاع على القائمة الكاملة للأحداث المتاحة.