Google Workspace eklentisinden Google dışı hizmetlere bağlanma

Google Workspace Eklenti projeniz, Apps Komut Dosyası'nın yerleşik ve gelişmiş hizmetleriyle birçok Google ürününe doğrudan bağlanabilir.

Google dışı API'lere ve hizmetlere de erişebilirsiniz. Hizmet yetkilendirme gerektirmiyorsa genellikle yalnızca uygun bir UrlFetch isteğinde bulunabilir ve eklentinizin yanıtı yorumlamasını sağlayabilirsiniz.

Ancak Google dışı hizmet için yetkilendirme gerekiyorsa OAuth'u bu hizmet için yapılandırmanız gerekir. OAuth2 for Apps Komut Dosyası kitaplığını (ayrıca bir OAuth1 sürümü de mevcuttur) kullanarak bu işlemi kolaylaştırabilirsiniz.

OAuth hizmeti kullanma

Google dışı bir hizmete bağlanmak için OAuth hizmet nesnesi kullanırken Google Workspace Eklentinizin, yetkilendirmenin ne zaman gerekli olduğunu algılaması ve gerekli olduğunda yetkilendirme akışını çağırması gerekir.

Yetkilendirme akışı şunlardan oluşur:

  1. Yetkilendirmenin gerekli olduğu konusunda kullanıcıyı uyarır ve işlemi başlatmak için bir bağlantı sağlar.
  2. Google dışı hizmetten yetkilendirme alınıyor.
  3. Korunan kaynağa erişmeyi yeniden denemek için eklenti yenileniyor.

Google dışı yetkilendirme gerektiğinde Google Workspace eklentisi altyapısı bu ayrıntıları yönetir. Eklentinizin yalnızca yetkilendirme gerektiğinde bunu algılaması ve gerektiğinde yetkilendirme akışını çağırması gerekir.

Yetkilendirmenin gerekli olduğunu belirleme

Bir istek, aşağıdakiler gibi çeşitli nedenlerden dolayı korunan bir kaynağa erişim yetkisine sahip olmayabilir:

  • Erişim jetonu henüz oluşturulmadı veya jetonun süresi dolmuş.
  • Erişim jetonu, istenen kaynağı kapsamıyor.
  • Erişim jetonu, isteğin gerekli kapsamlarını kapsamıyor.

Eklenti kodunuz bu durumları algılar. OAuth kitaplığı hasAccess() işlevi, bir hizmete şu anda erişiminiz olup olmadığını bildirebilir. Alternatif olarak, UrlFetchApp fetch() isteklerini kullanırken muteHttpExceptions parametresini true olarak ayarlayabilirsiniz. Bu, isteğin istek hatası durumunda istisna oluşturmasını önler ve döndürülen HttpResponse nesnesindeki istek yanıt kodunu ve içeriği incelemenize olanak tanır.

Eklenti, yetkilendirme gerektiğini tespit ettiğinde yetkilendirme akışını tetiklemelidir.

Yetkilendirme akışını çağırma

Yetkilendirme akışını, Kart hizmetini kullanarak bir AuthorizationException nesnesi oluşturmak, özelliklerini ayarlamak ve throwException() işlevini çağırarak çağırırsınız. İstisnayı oluşturmadan önce aşağıdakileri sağlamanız gerekir:

  1. Zorunludur. Bir yetkilendirme URL'si. Bu, Google dışı hizmet tarafından belirtilir ve yetkilendirme akışı başladığında kullanıcının yönlendirildiği konumdur. Bu URL'yi setAuthorizationUrl() işlevini kullanarak ayarlarsınız.
  2. Zorunludur. Kaynak görünen ad dizesi. Yetkilendirme istendiğinde kaynağı kullanıcıya tanımlar. Bu adı, setResourceDisplayName() işlevini kullanarak ayarlarsınız.
  3. Özel yetkilendirme istemi oluşturan bir geri çağırma işlevinin adı. Bu geri çağırma, yetkilendirmeyi işlemek için bir kullanıcı arayüzü oluşturan bir derlenmiş Card nesneleri dizisi döndürür. Bu isteğe bağlıdır. Ayarlanmazsa varsayılan yetkilendirme kartı kullanılır. Geri çağırma işlevini setCustomUiCallback() işlevini kullanarak ayarlayabilirsiniz.

Google dışı OAuth yapılandırması örneği

Bu kod örneğinde, bir eklentinin OAuth gerektiren Google dışı bir API'yi kullanacak şekilde nasıl yapılandırılacağı gösterilmektedir. API'ye erişmek amacıyla bir hizmet oluşturmak için Apps Komut Dosyası için OAuth2'den yararlanır.

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

Özel yetkilendirme istemi oluşturma

Google dışı hizmet yetkilendirme kartı

Yetkilendirme isteminde varsayılan olarak herhangi bir marka bulunmaz ve yalnızca eklentinin hangi kaynağa erişmeye çalıştığını belirtmek için görünen ad dizesini kullanır. Ancak eklentiniz, aynı amaca hizmet eden ve ek bilgiler ile marka bilinci oluşturma öğelerini içerebilen, özelleştirilmiş bir yetkilendirme kartı tanımlayabilir.

Özel bir istem, oluşturulmuş Card nesneleri dizisi döndüren bir özel kullanıcı arayüzü geri çağırma işlevi uygulayarak tanımlarsınız. Bu dizi yalnızca tek bir kart içermelidir. Daha fazla üst bilgi sağlanırsa bunların başlıkları bir listede görüntülenir ve bu da kafa karıştırıcı bir kullanıcı deneyimine neden olabilir.

İade edilen kart şunları yapmalıdır:

  • Kullanıcıya, eklentinin kendi adına Google dışı bir hizmete erişim izni istediğini açıkça belirtin.
  • İzin verilirse eklentinin neler yapabileceğini açıkça belirtin.
  • Kullanıcıyı hizmetin yetkilendirme URL'sine yönlendiren bir düğme veya benzer bir widget içermelidir. Bu widget'ın işlevinin kullanıcı tarafından açıkça görülebildiğinden emin olun.
  • Yetkilendirme alındıktan sonra eklentinin yeniden yüklenmesini sağlamak için, yukarıdaki widget'ın OpenLink nesnesinde OnClose.RELOAD_ADD_ON ayarını kullanması gerekir.
  • Yetkilendirme isteminden açılan tüm bağlantılar HTTPS kullanmalıdır.

AuthorizationException nesnenizde setCustomUiCallback() işlevini çağırarak yetkilendirme akışını kartınızı kullanacak şekilde yönlendirirsiniz.

Aşağıdaki örnekte bir özel yetkilendirme istemi geri çağırma işlevi gösterilmektedir:

/**
 * 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 uygulamalarında üçüncü taraf girişlerini yönetme

Google Workspace Eklentileri için yaygın olarak kullanılan uygulamalardan biri, Google Workspace barındırma uygulaması içinden üçüncü taraf sistemlerle etkileşim için arayüz sunmaktır. Apps Komut Dosyası için OAuth2 kitaplığı, üçüncü taraf hizmetleriyle bağlantı oluşturmanıza ve bunları yönetmenize yardımcı olabilir.

Üçüncü taraf sistemler genellikle kullanıcının bir kullanıcı kimliği, şifre veya başka kimlik bilgileri kullanarak oturum açmasını gerektirir. Bir kullanıcı bir Google Workspace ana makinesini kullanırken üçüncü taraf hizmetinizde oturum açtığında, başka bir Google Workspace ana makinesine geçtiğinde tekrar oturum açmak zorunda kalmayacağından emin olmanız gerekir. Tekrarlanan giriş isteklerini önlemek için kullanıcı özelliklerini veya kimlik jetonlarını kullanın. Bunlar aşağıdaki bölümlerde açıklanmıştır.

Kullanıcı özellikleri

Bir kullanıcının oturum açma verilerini Apps Komut Dosyası'nın kullanıcı özelliklerinde depolayabilirsiniz. Örneğin, bu kişilerin giriş hizmetlerinden kendi JWT'nizi oluşturup bunu bir kullanıcı özelliğine kaydedebilir veya bu hizmetin kullanıcı adı ve şifresini kaydedebilirsiniz.

Kullanıcı özellikleri, yalnızca eklentinizin komut dosyası içinde bu kullanıcı tarafından erişilebilecek şekilde ayarlanır. Diğer kullanıcılar ve diğer komut dosyaları bu özelliklere erişemez. Ayrıntılı bilgi için PropertiesService sayfasını inceleyin.

Kimlik jetonları

Hizmetiniz için giriş kimlik bilgisi olarak bir Google kimliği jetonu kullanabilirsiniz. Bu, tek oturum açma özelliğini sağlamanın bir yoludur. Kullanıcılar bir Google barındırma uygulamasında oldukları için zaten Google'a giriş yapmışlardır.