Надстройка Gmail: Oauth не запускается

В приведенном ниже коде кнопка «Тест» запускает функцию, которая вызывает внешнюю конечную точку для загрузки данных. Однако при нажатии кнопки ничего не происходит, и я получаю 400 ошибку в области консоли с надписью Invalid Argument.

Code.gs

function buildAddOn(e) {
  // Create a section for that contains all user Labels.
  var section = CardService.newCardSection()  
  var action = CardService.newAction()
        .setFunctionName("testCall");

  var button = CardService.newTextButton().setText('Test').setOnClickAction(action);
  section.addWidget(CardService.newButtonSet().addButton(button)); 

//  section.addWidget(CardService.newTextParagraph()
//    .setText("This is a text paragraph widget. Multiple lines are allowed if needed.");)

  // Build the main card after adding the section.
  var card = CardService.newCardBuilder()
    .setHeader(CardService.newCardHeader()
    .setTitle('Authentication Card')
    .setImageUrl('https://www.gstatic.com/images/icons/material/system/1x/label_googblue_48dp.png'))
    .addSection(section)
    .build();

  return [card];
}

function testCall(){
  console.log("test");
  var data = accessProtectedResource('https://api.ssdf.io/v1.0/asd/4/174203','get');
  return CardService.newActionResponseBuilder()
      .setNotification(CardService.newNotification()
          .setType(CardService.NotificationType.INFO)
          .setText(data))
      .build();
}

authService.gs

/**
 * 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("Login to ....")
        .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('auth')
      .setAuthorizationBaseUrl('https://app.ss.io/oauth/authorize')
      .setTokenUrl('https://api.ss.io/oauth/token')
      .setClientId('2361c9fbc5ba4b88813a3ef')
      .setClientSecret('f5d3a04f4asda30a52830e230e43727')
      .setScope('1')
      .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();
}

person Shyamal Parikh    schedule 28.03.2019    source источник


Ответы (2)


Все URL-адреса в функции getOAuthService() должны быть исходными URL-адресами Google в примере:

      // Set the endpoint URLs, which are the same for all Google services.
      .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
      .setTokenUrl('https://accounts.google.com/o/oauth2/token')

а также

      // Set the scopes to request (space-separated for Google services).
      .setScope('https://www.googleapis.com/auth/drive')

Возможно, можно будет изменить последний, но только путь, а не домен, и вам нужно посмотреть в API, можно ли и как это изменить. Также этот параметр области видимости отличается в вашем примере, но я не знаю, можно ли принять «1».

Ваше собственное приложение получит обратную связь, если доступ предоставлен, но не участвует в проверке аутентификации. Поэтому вам также необходимо получить токен доступа, я вижу это в вашем коде: var accessToken = service.getAccessToken();, в примере он выглядит немного иначе:

function makeRequest() {
  var driveService = getDriveService();
  var response = UrlFetchApp.fetch('https://www.googleapis.com/drive/v2/files?maxResults=10', {
    headers: {
      Authorization: 'Bearer ' + driveService.getAccessToken()
    }
  });
  // ...
}

см. строку с Authorization: 'Bearer ' + driveService.getAccessToken().

Ваш собственный сервер не настроен (и не должен быть) настроен для обработки запросов аутентификации и поэтому выдает ошибка 400. API создан для использования Javascript на стороне клиента, поэтому я советую не использовать его для аутентификации на собственном сервере. Тем не менее, ниже я перечислил API-интерфейсы для использования на вашем собственном сервере.

Использование собственного сервера для аутентификации
Если вы отклоните мой совет использовать серверы Google для аутентификации, то объем отмеченных проблем становится больше, поскольку конфигурация сервера (apache, nginx, ...) и серверные языки программирования (PHP, Python, ...) могут быть задействованы.

Затем вам нужно отладить заголовки, что именно отправляется на сервер и почему сервер не может с этим справиться. Вы можете отладить запрос и ответ в инструменте разработчика браузера (сетевая панель) и проверить файлы ошибок сервера.

Затем сервер должен получить службу Google сам по себе и делать то, что было бы сделано в интерфейсе только с помощью javascript, если вы следуете пример, который вы указали в своем коде.

В этом примере сделано три запроса к серверу:
- один для аутентификации
- один для получения токена аутентификации
- один для получения защищенной службы
Вы должны помнить, что все Затем ваш собственный сервер должен выполнить три шага, и ваш сервер должен иметь возможность отвечать на все три типа запросов.
Важный вопрос может заключаться в том, какой из трех запросов вызывает ошибку? < / сильный>

Затем вы должны дополнить свой вопрос подробными проблемами, которые вы обнаружите в связи с вашим сервером.

API и примеры для аутентификации OAuth на стороне сервера
PHP: https://developers.google.com/api-client-library/php/auth/web-app
Python: https://разработчики.google.com/api-client-library/python/auth/web-app
Ruby: https://developers.google.com/api-client-library/ruby/auth/web-app
NodeJs: https://developers.google.com/apps-script/api/quickstart/nodejs
Java: https://developers.google.com/api-client-library/java/google-oauth-java-client/
Перейти: https://github.com/googleapis/google-api-go-client
.NET: https://developers.google.com/api-client-library/dotnet/apis/oauth2/v2

person David    schedule 06.04.2019
comment
Я использую собственное приложение для аутентификации. И я почти уверен, что это не имеет ничего общего с URL-адресами. Вы пробовали свой собственный код? - person Shyamal Parikh; 06.04.2019
comment
смотри мои объявления ;-) - person David; 06.04.2019
comment
Добавлены ссылки на API и примеры серверных решений OAuth. - person David; 06.04.2019

Код ошибки 400, кажется, относится к плохой ссылке, я думаю, вам нужно проверить разные URL-адреса. В частности, в getOAuthService вы написали: «https://apP.ss.io/oauth/authorize "в базовом URL-адресе авторизации и" https://apI.ss.io/oauth/token "в URL-адресе токена.

person Albenss    schedule 03.04.2019
comment
Это сделано намеренно, так как я не хотел, чтобы URL-адреса были общедоступными. - person Shyamal Parikh; 04.04.2019