الربط بخدمات غير تابعة لـ Google من إضافة Google Workspace

يمكن لمشروع إضافة Google Workspace الاتصال مباشرةً بالعديد من منتجات Google من خلال خدمات برمجة التطبيقات المدمجة والمتقدمة.

يمكنك أيضًا الوصول إلى الخدمات والخدمات غير التابعة لـ Google APIs. إذا كانت الخدمة لا تتطلب تفويضًا، يمكنك عادةً تقديم طلب UrlFetch مناسب، ثم اطلب من الأداة الإضافية تفسير الاستجابة.

ولكن إذا كانت الخدمة بخلاف Google تتطلب تفويضًا، فيجب تهيئة OAuth لهذه الخدمة. ويمكنك تسهيل هذه العملية باستخدام مكتبة OAuth2 لبرمجة التطبيقات (هناك أيضًا إصدار OAuth1).

استخدام خدمة OAuth

عند استخدام كائن خدمة OAuth للاتصال بخدمة غير تابعة لـ Google، يجب أن تكتشف "إضافة Google Workspace" متى يكون التفويض مطلوبًا، واستدعاء تدفق التفويض عندما يكون ذلك مطلوبًا.

وتتألف عملية التفويض من:

  1. لتنبيه المستخدم بأن المصادقة مطلوبة وتوفير رابط لبدء العملية.
  2. جارٍ الحصول على تفويض من خدمة غير تابعة لشركة Google.
  3. تجري إعادة تحميل الإضافة لإعادة محاولة الوصول إلى المورد المحمي.

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

اكتشاف أن التفويض مطلوب

قد لا يكون للطلب إذن للدخول إلى مورد محمي لعدة أسباب، مثل:

  • لم يتم إنشاء رمز الدخول بعد أو انتهت صلاحيته.
  • لا يغطي رمز الدخول المورد المطلوب.
  • لا يغطي رمز الدخول رموز النطاقات المطلوبة للطلب.

يجب أن يكتشف رمز الإضافة هذه الحالات. تخبرك وظيفة مكتبة OAuth hasAccess() إذا كنت تتمتع حاليًا بإمكانية الوصول إلى خدمة. بدلاً من ذلك، عند استخدام طلبات UrlFetchApp fetch()، يمكنك ضبط مَعلمة muteHttpExceptions على true. يمنع هذا الإجراء الطلب من الحصول على استثناء عند تعذُّر الطلب ويسمح لك بفحص رمز الاستجابة للطلب والمحتوى في كائن HttpResponse المعروض.

عندما تكتشف الإضافة أن التفويض مطلوب، من المفترض أن يتم تشغيل تدفق التفويض.

استدعاء تدفق التفويض

يمكنك استدعاء تدفق التفويض باستخدام خدمة البطاقة لإنشاء كائن AuthorizationException، وتحديد خصائصه، ثم استدعاء الوظيفة throwException(). قبل تقديم الاستثناء، يجب تقديم ما يلي:

  1. مطلوبة. عنوان URL للتفويض. ويتم تحديد ذلك بواسطة الخدمة التي لا تتبع Google وهي الموقع الذي يتم نقل المستخدم إليه عند بدء تدفق التفويض. يمكنك تعيين عنوان URL هذا باستخدام الدالة setAuthorizationUrl().
  2. مطلوبة. سلسلة اسم عرض مورد. يحدد المورد للمستخدم عند طلب التفويض. ويمكنك تحديد هذا الاسم باستخدام الدالة setResourceDisplayName().
  3. اسم دالة رد الاتصال التي تنشئ طلب تفويض مخصص. تعرض معاودة الاتصال هذه مصفوفة من كائنات Card المُنشأة والتي تنشئ واجهة مستخدم لمعالجة التفويض. هذا الإجراء اختياري، إذا لم يتم تعيينه، فسيتم استخدام بطاقة التفويض الافتراضية. ويمكنك ضبط دالة رد الاتصال باستخدام الدالة setCustomUiCallback().

مثال على التهيئة التي لا تتبع Google OAuth

يوضح نموذج الشفرة هذا كيفية تهيئة إحدى الإضافات لاستخدام واجهة برمجة تطبيقات بخلاف Google API تتطلب OAuth. وتستفيد من OAuth2 لبرمجة التطبيقات لإنشاء خدمة للوصول إلى واجهة برمجة التطبيقات.

/**
 * Attempts to access a non-Google API using a constructed service
 * object.
 *
 * If your add-on needs access to non-Google APIs that require OAuth,
 * you need to implement this method. You can use the OAuth1 and
 * OAuth2 Apps Script libraries to help implement it.
 *
 * @param {String} url         The URL to access.
 * @param {String} method_opt  The HTTP method. Defaults to GET.
 * @param {Object} headers_opt The HTTP headers. Defaults to an empty
 *                             object. The Authorization field is added
 *                             to the headers in this method.
 * @return {HttpResponse} the result from the UrlFetchApp.fetch() call.
 */
function accessProtectedResource(url, method_opt, headers_opt) {
  var service = getOAuthService();
  var maybeAuthorized = service.hasAccess();
  if (maybeAuthorized) {
    // A token is present, but it may be expired or invalid. Make a
    // request and check the response code to be sure.

    // Make the UrlFetch request and return the result.
    var accessToken = service.getAccessToken();
    var method = method_opt || 'get';
    var headers = headers_opt || {};
    headers['Authorization'] =
        Utilities.formatString('Bearer %s', accessToken);
    var resp = UrlFetchApp.fetch(url, {
      'headers': headers,
      'method' : method,
      'muteHttpExceptions': true, // Prevents thrown HTTP exceptions.
    });

    var code = resp.getResponseCode();
    if (code >= 200 && code < 300) {
      return resp.getContentText("utf-8"); // Success
    } else if (code == 401 || code == 403) {
       // Not fully authorized for this action.
       maybeAuthorized = false;
    } else {
       // Handle other response codes by logging them and throwing an
       // exception.
       console.error("Backend server error (%s): %s", code.toString(),
                     resp.getContentText("utf-8"));
       throw ("Backend server error: " + code);
    }
  }

  if (!maybeAuthorized) {
    // Invoke the authorization flow using the default authorization
    // prompt card.
    CardService.newAuthorizationException()
        .setAuthorizationUrl(service.getAuthorizationUrl())
        .setResourceDisplayName("Display name to show to the user")
        .throwException();
  }
}

/**
 * Create a new OAuth service to facilitate accessing an API.
 * This example assumes there is a single service that the add-on needs to
 * access. Its name is used when persisting the authorized token, so ensure
 * it is unique within the scope of the property store. You must set the
 * client secret and client ID, which are obtained when registering your
 * add-on with the API.
 *
 * See the Apps Script OAuth2 Library documentation for more
 * information:
 *   https://github.com/googlesamples/apps-script-oauth2#1-create-the-oauth2-service
 *
 *  @return A configured OAuth2 service object.
 */
function getOAuthService() {
  return OAuth2.createService('SERVICE_NAME')
      .setAuthorizationBaseUrl('SERVICE_AUTH_URL')
      .setTokenUrl('SERVICE_AUTH_TOKEN_URL')
      .setClientId('CLIENT_ID')
      .setClientSecret('CLIENT_SECRET')
      .setScope('SERVICE_SCOPE_REQUESTS')
      .setCallbackFunction('authCallback')
      .setCache(CacheService.getUserCache())
      .setPropertyStore(PropertiesService.getUserProperties());
}

/**
 * Boilerplate code to determine if a request is authorized and returns
 * a corresponding HTML message. When the user completes the OAuth2 flow
 * on the service provider's website, this function is invoked from the
 * service. In order for authorization to succeed you must make sure that
 * the service knows how to call this function by setting the correct
 * redirect URL.
 *
 * The redirect URL to enter is:
 * https://script.google.com/macros/d/<Apps Script ID>/usercallback
 *
 * See the Apps Script OAuth2 Library documentation for more
 * information:
 *   https://github.com/googlesamples/apps-script-oauth2#1-create-the-oauth2-service
 *
 *  @param {Object} callbackRequest The request data received from the
 *                  callback function. Pass it to the service's
 *                  handleCallback() method to complete the
 *                  authorization process.
 *  @return {HtmlOutput} a success or denied HTML message to display to
 *          the user. Also sets a timer to close the window
 *          automatically.
 */
function authCallback(callbackRequest) {
  var authorized = getOAuthService().handleCallback(callbackRequest);
  if (authorized) {
    return HtmlService.createHtmlOutput(
      'Success! <script>setTimeout(function() { top.window.close() }, 1);</script>');
  } else {
    return HtmlService.createHtmlOutput('Denied');
  }
}

/**
 * Unauthorizes the non-Google service. This is useful for OAuth
 * development/testing.  Run this method (Run > resetOAuth in the script
 * editor) to reset OAuth to re-prompt the user for OAuth.
 */
function resetOAuth() {
  getOAuthService().reset();
}

إنشاء إشعار تفويض مخصص

بطاقة تفويض غير تابعة لخدمة Google

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

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

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

  • وضّح للمستخدم أن الإضافة تطلب إذنًا للوصول إلى خدمة غير تابعة لشركة Google نيابةً عنه.
  • وضّح ما تستطيع الإضافة فعله إذا تم السماح بها.
  • تحتوي على زر أو أداة مشابهة تنقل المستخدم إلى عنوان URL لتفويض الخدمة. تأكد من أن وظيفة هذه الأداة واضحة للمستخدم.
  • يجب أن تستخدم الأداة أعلاه الإعداد OnClose.RELOAD_ADD_ON في الكائن OpenLink لضمان إعادة تحميل الإضافة بعد استلام التفويض.
  • يجب أن تستخدم جميع الروابط التي تم فتحها من رسالة المطالبة HTTPS.

يمكنك توجيه مسار التفويض لاستخدام بطاقتك من خلال استدعاء الوظيفة setCustomUiCallback() في الكائن AuthorizationException.

يوضح المثال التالي وظيفة رد اتصال طلب تفويض مخصص:

/**
 * Returns an array of cards that comprise the customized authorization
 * prompt. Includes a button that opens the proper authorization link
 * for a non-Google service.
 *
 * When creating the text button, using the
 * setOnClose(CardService.OnClose.RELOAD_ADD_ON) function forces the add-on
 * to refresh once the authorization flow completes.
 *
 * @return {Card[]} The card representing the custom authorization prompt.
 */
function create3PAuthorizationUi() {
  var service = getOAuthService();
  var authUrl = service.getAuthorizationUrl();
  var authButton = CardService.newTextButton()
      .setText('Begin Authorization')
      .setAuthorizationAction(CardService.newAuthorizationAction()
          .setAuthorizationUrl(authUrl));

  var promptText =
      'To show you information from your 3P account that is relevant' +
      ' to the recipients of the email, this add-on needs authorization' +
      ' to: <ul><li>Read recipients of the email</li>' +
      '         <li>Read contact information from 3P account</li></ul>.';

  var card = CardService.newCardBuilder()
      .setHeader(CardService.newCardHeader()
          .setTitle('Authorization Required'))
      .addSection(CardService.newCardSection()
          .setHeader('This add-on needs access to your 3P account.')
          .addWidget(CardService.newTextParagraph()
              .setText(promptText))
          .addWidget(CardService.newButtonSet()
              .addButton(authButton)))
      .build();
  return [card];
}

/**
 * When connecting to the non-Google service, pass the name of the
 * custom UI callback function to the AuthorizationException object
 */
function accessProtectedResource(url, method_opt, headers_opt) {
  var service = getOAuthService();
  if (service.hasAccess()) {
    // Make the UrlFetch request and return the result.
    // ...
  } else {
    // Invoke the authorization flow using a custom authorization
    // prompt card.
    CardService.newAuthorizationException()
        .setAuthorizationUrl(service.getAuthorizationUrl())
        .setResourceDisplayName("Display name to show to the user")
        .setCustomUiCallback('create3PAuthorizationUi')
        .throwException();
  }
}

إدارة عمليات تسجيل الدخول التابعة لجهات خارجية في جميع تطبيقات Google Workspace

يتمثل أحد التطبيقات الشائعة في إضافات Google Workspace في توفير واجهة للتفاعل مع نظام تابع لجهة خارجية من داخل تطبيق مضيف Google Workspace. يمكن أن تساعدك مكتبة OAuth2 لبرمجة التطبيقات في إنشاء الاتصالات بخدمات الجهات الخارجية وإدارتها.

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

خصائص المستخدمين

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

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

الرموز المميزة للمعرّفات

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