استخدام نموذج الرمز المميز

تساعدك مكتبة JavaScript google.accounts.oauth2 في طلب موافقة المستخدم والحصول على رمز دخول للتعامل مع بيانات المستخدم. وهي تستند إلى مسار التفويض الضمني في OAuth 2.0، وهي مصمَّمة للسماح لك إما بطلب بيانات من Google APIs مباشرةً باستخدام REST وCORS، أو باستخدام مكتبة برامج Google APIs للغة JavaScript (المعروفة أيضًا باسم gapi.client) للوصول بسهولة ومرونة إلى واجهات برمجة التطبيقات الأكثر تعقيدًا.

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

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

ننصحك باتّباع الأسلوب الموضّح هنا بدلاً من التقنيات التي يغطيها الدليل القديم OAuth 2.0 لتطبيقات الويب من جهة العميل.

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

اتّبِع الخطوات الموضّحة في الإعداد لضبط شاشة طلب الموافقة المتعلّقة ببروتوكول OAuth والحصول على معرّف عميل وتحميل مكتبة العميل.

إعداد برنامج الرمز المميز

استخدِم الدالة initTokenClient() لتهيئة عميل رمز مميّز جديد باستخدام معرّف العميل لتطبيق الويب، ويجب تضمين قائمة بنطاق واحد أو أكثر من النطاقات التي يحتاج المستخدم إلى الوصول إليها:

const client = google.accounts.oauth2.initTokenClient({
  client_id: 'YOUR_GOOGLE_CLIENT_ID',
  scope: 'https://www.googleapis.com/auth/calendar.readonly',
  callback: (response) => {
    ...
  },
});

بدء مسار رمز OAuth 2.0 المميز

استخدِم طريقة requestAccessToken() لتفعيل سير عمل تجربة المستخدم الخاصة بالرمز المميز والحصول على رمز مميز للوصول. تطلب Google من المستخدم ما يلي:

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

يؤدي إجراء من المستخدم إلى بدء تدفق الرمز المميّز: <button onclick="client.requestAccessToken();">Authorize me</button>

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

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

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

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

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

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

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

تفويض تدريجي

بالنسبة إلى تطبيقات الويب، يوضّح السيناريوهان التاليان على المستوى العالي عملية منح الأذونات بشكل تدريجي باستخدام:

  • تطبيق Ajax من صفحة واحدة، وغالبًا ما يستخدم XMLHttpRequest مع إمكانية الوصول الديناميكي إلى الموارد.
  • صفحات ويب متعددة، ويتم فصل الموارد وإدارتها على أساس كل صفحة.

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

Ajax

أضِف إمكانية الحصول على تفويض تدريجي إلى تطبيقك من خلال إجراء طلبات متعددة إلى requestAccessToken() واستخدام المَعلمة scope الخاصة بالكائن OverridableTokenClientConfig لطلب نطاقات فردية عند الحاجة إليها وفقط عند الضرورة. في هذا المثال، سيتم طلب الموارد وعرضها فقط بعد أن يوسّع المستخدم قسم المحتوى المصغّر من خلال إجراء.

تطبيق Ajax
إعداد عميل الرمز المميّز عند تحميل الصفحة:
        const client = google.accounts.oauth2.initTokenClient({
          client_id: 'YOUR_GOOGLE_CLIENT_ID',
          callback: "onTokenResponse",
        });
      
طلب الموافقة والحصول على رموز الدخول من خلال إيماءات المستخدم، انقر على علامة `+` لفتح:

المستندات التي يجب قراءتها

عرض المستندات الحديثة

          client.requestAccessToken(
            overrideConfig = ({
               scope = 'https://www.googleapis.com/auth/documents.readonly'
             })
           );
        

الأحداث القادمة

عرض معلومات التقويم

          client.requestAccessToken(
            overrideConfig = ({
               scope = 'https://www.googleapis.com/auth/calendar.readonly'
             })
           );
        

عرض الصور

          client.requestAccessToken(
            overrideConfig = ({
               scope = 'https://www.googleapis.com/auth/photoslibrary.readonly'
             })
           );
        

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

صفحات ويب متعدّدة

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

تطبيق من صفحات متعددة
صفحة ويب الرمز
الصفحة 1 المستندات المطلوب قراءتها
  const client = google.accounts.oauth2.initTokenClient({
    client_id: 'YOUR_GOOGLE_CLIENT_ID',
    callback: "onTokenResponse",
    scope: 'https://www.googleapis.com/auth/documents.readonly',
  });
  client.requestAccessToken();
          
الصفحة 2. الفعاليات القادمة
  const client = google.accounts.oauth2.initTokenClient({
    client_id: 'YOUR_GOOGLE_CLIENT_ID',
    callback: "onTokenResponse",
    scope: 'https://www.googleapis.com/auth/calendar.readonly',
  });
  client.requestAccessToken();
          
الصفحة 3 مجموعة صور
  const client = google.accounts.oauth2.initTokenClient({
    client_id: 'YOUR_GOOGLE_CLIENT_ID',
    callback: "onTokenResponse",
    scope: 'https://www.googleapis.com/auth/photoslibrary.readonly',
  });
  client.requestAccessToken();
          

تطلب كل صفحة النطاق اللازم وتحصل على رمز مميّز للوصول من خلال استدعاء initTokenClient() وrequestAccessToken() عند وقت التحميل. في هذا السيناريو، يتم استخدام صفحات ويب فردية لفصل وظائف المستخدمين والموارد حسب النطاق بشكل واضح. في سيناريو واقعي، قد تطلب الصفحات الفردية نطاقات متعدّدة ذات صلة.

الأذونات الدقيقة

يتم التعامل مع الأذونات الدقيقة بالطريقة نفسها في جميع السيناريوهات. بعد أن تستدعي requestAccessToken() دالة معاودة الاتصال ويتم عرض رمز دخول، تأكَّد من أنّ المستخدم وافق على النطاقات المطلوبة باستخدام hasGrantedAllScopes() أو hasGrantedAnyScope(). على سبيل المثال:

const client = google.accounts.oauth2.initTokenClient({
  client_id: 'YOUR_GOOGLE_CLIENT_ID',
  scope: 'https://www.googleapis.com/auth/calendar.readonly \
          https://www.googleapis.com/auth/documents.readonly \
          https://www.googleapis.com/auth/photoslibrary.readonly',
  callback: (tokenResponse) => {
    if (tokenResponse && tokenResponse.access_token) {
      if (google.accounts.oauth2.hasGrantedAnyScope(tokenResponse,
          'https://www.googleapis.com/auth/photoslibrary.readonly')) {
        // Look at pictures
        ...
      }
      if (google.accounts.oauth2.hasGrantedAllScopes(tokenResponse,
          'https://www.googleapis.com/auth/calendar.readonly',
          'https://www.googleapis.com/auth/documents.readonly')) {
        // Meeting planning and review documents
        ...
      }
    }
  },
});

سيتم أيضًا تضمين أي أذونات تم قبولها سابقًا من جلسات أو طلبات سابقة في الرد. يتم الاحتفاظ بسجلّ لموافقة المستخدِم لكل مستخدِم ورقم تعريف العميل، ويظلّ متاحًا على مستوى طلبات متعدّدة إلى initTokenClient() أو requestAccessToken(). بشكلٍ تلقائي، لا تكون موافقة المستخدم ضرورية إلا في المرة الأولى التي يزور فيها المستخدم موقعك الإلكتروني ويطلب نطاقًا جديدًا، ولكن يمكن طلبها عند كل عملية تحميل للصفحة باستخدام prompt=consent في عناصر إعداد Token Client.

العمل باستخدام الرموز المميزة

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

استخدام REST وCORS مع Google APIs

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

في هذا المثال، يمكنك عرض أحداث التقويم القادمة للمستخدمين الذين سجّلوا الدخول باستخدام رمز الدخول الذي تعرضه tokenRequest():

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.googleapis.com/calendar/v3/calendars/primary/events');
xhr.setRequestHeader('Authorization', 'Bearer ' + tokenResponse.access_token);
xhr.send();

تتيح واجهات Google API استخدام سياسة مشاركة الموارد المتعددة المصادر (CORS) تلقائيًا، ويؤدي تضمين رمز مميّز للوصول في طلب XMLHttpRequest أو fetch إلى بدء عملية فحص مسبق لسياسة مشاركة الموارد المتعددة المصادر (CORS)، أي طلب OPTIONS قبل طلب GET أو POST.

يتناول القسم التالي كيفية الدمج بسهولة مع واجهات برمجة التطبيقات الأكثر تعقيدًا.

العمل باستخدام مكتبة JavaScript الخاصة بواجهات Google API

يعمل عميل الرمز المميز مع مكتبة برامج Google API للغة JavaScript. اطّلِع على مقتطف الرمز البرمجي أدناه.

const client = google.accounts.oauth2.initTokenClient({
  client_id: 'YOUR_GOOGLE_CLIENT_ID',
  scope: 'https://www.googleapis.com/auth/calendar.readonly',
  callback: (tokenResponse) => {
    if (tokenResponse && tokenResponse.access_token) {
      gapi.client.setApiKey('YOUR_API_KEY');
      gapi.client.load('calendar', 'v3', listUpcomingEvents);
    }
  },
});

function listUpcomingEvents() {
  gapi.client.calendar.events.list(...);
}

انتهاء صلاحية الرمز المميز

تم تصميم رموز الدخول ليكون لها مدة صلاحية قصيرة. إذا انتهت صلاحية رمز الدخول قبل نهاية جلسة المستخدم، احصل على رمز جديد من خلال استدعاء requestAccessToken() من حدث ناتج عن إجراء من المستخدم، مثل الضغط على زر.

استدعِ طريقة google.accounts.oauth2.revoke لإزالة موافقة المستخدم وإمكانية الوصول إلى الموارد لجميع النطاقات الممنوحة لتطبيقك. يجب توفُّر رمز دخول صالح لإبطال هذا الإذن:

google.accounts.oauth2.revoke('414a76cb127a7ece7ee4bf287602ca2b56f8fcbf7fcecc2cd4e0509268120bd7', done => {
    console.log(done);
    console.log(done.successful);
    console.log(done.error);
    console.log(done.error_description);
  });