в веб-приложении Google Apps Script, получая redirect_uri_mismatch, но я, возможно, даже не на правильном пути - PullRequest
0 голосов
/ 20 июня 2019

Я пытаюсь создать скрипт приложений Google, который обрабатывает две таблицы одновременно - одна содержит личную информацию для пользователей; другой - публичный.

Поскольку в нем хранится личная информация, я должен убедиться, что только пользователь имеет к ней доступ. В конечном итоге я решил (что, возможно, не самый лучший способ справиться с этим), чтобы создать папку и файл на собственном диске Google пользователя для хранения данных. Я обращаюсь с этим внутри моего code.gs, и он отлично работает.

Проблема возникает, когда я пытаюсь получить доступ к этим данным через мое веб-приложение (которое использует тот же файл code.gs). Несмотря на то, что пользователям приходилось авторизовывать мое приложение для доступа к своим данным, когда оно впервые появлялось и пыталось создать файл, авторизации driveapp и spreadsheetapp, похоже, не учитываются для веб-приложения. Поэтому, хотя у меня есть fileId и url, я не могу получить к ним доступ. Я получаю следующую ошибку:

ACCESS_DENIED Эта электронная таблица недоступна для публичного просмотра и требует учетные данные OAuth

Правильно ли я предположил, что если я войду и создаю учетные данные OAuth с использованием clientID, то это будет работать только на МОИХ электронных таблицах, а не на электронных таблицах моего ПОЛЬЗОВАТЕЛЯ? Когда я позаимствовал у собственной страницы Google информацию об авторизации электронных таблиц https://developers.google.com/chart/interactive/docs/spreadsheets#Authorization, и создал свое приложение только с этим (меняя местами URL-адреса и идентификаторы клиентов), я получил эту ошибку:

  1. Это ошибка.

Ошибка: redirect_uri_mismatch

Происхождение JavaScript в запросе, https://[gobblydygook1] -script.googleusercontent.com , не соответствует авторизованные для клиента OAuth. Визит https://console.developers.google.com/apis/credentials/oauthclient/[validClientID].apps.googleusercontent.com?project=901868773794 обновить авторизованные источники JavaScript.

Узнать больше Подробнее

response_type=permission id_token
scope=https://www.googleapis.com/auth/spreadsheets
openid.realm=
redirect_uri=storagerelay://https/[gobblydygook1]-script.googleusercontent.com?id=auth590482
client_id=[validClientID].apps.googleusercontent.com
ss_domain=https://[gobblydygook1]-script.googleusercontent.com
gsiwebsdk=shim

Это все, что мы знаем.

К сожалению, у меня нет своего собственного домена ... когда я пытаюсь вставить действительный веб-сайт моего скрипта или ссылки, которые мне дает сообщение об ошибке, на странице Credentials, это не кажется взять их (или, может быть, так и не показывает их мне?).

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

Может ли мой code.gs каким-то образом передать авторизацию, которую он использовал для входа в этот файл, браузеру?

Я мог бы сделать так, чтобы code.gs собирал мои данные, но ... кажется, что это на несколько порядков медленнее, чем запросы визуализации.

Вот код, на случай, если вы хотите его увидеть:

index.html:

<!DOCTYPE html>
<html>
  <head>
  <?var userSheetURL = getUserSheetUrl();?>
  </head>
  <body>
    <h1>Hello, World!</h1>
    <h3>User sheet URL is <?=userSheetURL?></h3>
    <div id="user_sheet_url" data-url="<?=userSheetURL?>"></div>
    <?!= include('demo'); ?>
    <button id="authorize-button" style="visibility: hidden">Authorize</button>
    <script src="https://apis.google.com/js/auth.js?onload=init"></script>  
  </body>
</html>

demo.html:

<link href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jquerymobile/1.4.5/jquery.mobile.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquerymobile/1.4.5/jquery.mobile.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">

  var clientId = '[validClientId].apps.googleusercontent.com';
  var scopes = 'https://www.googleapis.com/auth/spreadsheets';

  function init() {
    gapi.auth.authorize(
        {client_id: clientId, scope: scopes, immediate: false},
        handleAuthResult);
  }

  function handleAuthResult(authResult) {
    var authorizeButton = document.getElementById('authorize-button');
    if (authResult && !authResult.error) {
      authorizeButton.style.visibility = 'hidden';
      makeApiCall();
    } else {
      authorizeButton.style.visibility = '';
      authorizeButton.onclick = handleAuthClick;
    }
  }

  function handleAuthClick(event) {
    gapi.auth.authorize(
        {client_id: clientId, scope: scopes, immediate: false},
        handleAuthResult);
    return false;
  }

  function makeApiCall() {
    var userUrl = $('#user_sheet_url').attr('data-url');
    //the following alert is never triggered
    alert(userUrl);
    var tqUrl = userUrl + 'gviz/tq' +
        '?tqx=responseHandler:handleTqResponse' +
        '&access_token=' + encodeURIComponent(gapi.auth.getToken().access_token);
    var slashScript = "/script"
    document.write('<script src="' + tqUrl +'" type="text/javascript"><' + slashScript + '>');
  }

  function handleTqResponse(resp) {
    document.write(JSON.stringify(resp));
  }
</script>

(вызов getUserSheetUrl вызывает функцию в code.gs, которая возвращает URL файла пользователя, который я создал. Я вижу, что он успешно возвращает нужную мне строку в окне браузера)

1 Ответ

1 голос
/ 25 июня 2019

Хотя я все еще уверен, что есть способ навигации по аутентификации более прямым способом, я нашел следующий обходной путь:

В моем code.gs у меня уже был следующий сегмент дляinitialize:

function initializeUserFile() {
  var folders = DriveApp.getFoldersByName(userFolderName);
  if(!folders.hasNext()) {
    //Folder does not exist in user's drive, so create it
    userFolder = DriveApp.createFolder(userFolderName);
  } else {
    //Folder does exist, so get it
    userFolder = folders.next();
  }
  var files = userFolder.getFilesByName(userFileName);
  var file;
  var fileId;
  if(files.hasNext()) {
    //File exists in user's folder, so get it
    file = files.next();
  } else {
    //File does not exist in user's folder.  Check root.
    files = DriveApp.getFilesByName(userFileName);
    if(!files.hasNext()) {
      //file does not exist
      fileId = SpreadsheetApp.create(userFileName).getId();
      file = DriveApp.getFileById(fileId);
    } else {
      //file is in root
      file = files.next();
    }
    //file is in root, whether because we found it or because we had to create it.  
    //so move it to the folder we need it in
    userFolder.addFile(file);
    DriveApp.getRootFolder().removeFile(file);
  }
  userFileId = file.getId();
  userSpreadsheet = SpreadsheetApp.openById(userFileId);
  initializeSheet(userInfoSheet, userInfoSheetName, userSpreadsheet);  
  initializeSheet(userCrewSheet, userCrewSheetName, userSpreadsheet);
  initializeSheet(userBonusSheet, userBonusSheetName, userSpreadsheet);
  removeSheet(userSpreadsheet, "Sheet1");
  Logger.log("spreadsheet = " + file.getName());
  Logger.log("folder = " + userFolder.getName());
}

function initializeSheet(sheet,sheetName,targetSheet) {
  if(!targetSheet) {
    targetSheet = activeSpreadsheet;
  }
  sheet = targetSheet.getSheetByName(sheetName);
  //The following only fires if the sheet doesn't exist
  if(sheet == null) { 
    sheet = targetSheet.insertSheet();
    sheet.setName(sheetName);
    setHeaders(sheet,sheetName);
  }
}

Я пропущу перечисление setHeaders, поскольку он длинный ... в основном просто решает, на какой странице он находится, и создает соответствующую первую строку (заголовки) для этой страницы.

Таким образом, когда новый пользователь получает доступ к приложению, эта процедура запускается и создает файл и папку.Если существующий пользователь обращается к нему, он в основном просто инициализирует важные переменные, такие как userFolder, userFileId и userSpreadsheet.

Единственной важной для следующей части является userSpreadsheet:

function getUserCrewTable(sheetName) {
  initializeUserFile();
  var sheet = userSpreadsheet.getSheetByName(sheetName);
  var range = sheet.getRange(1,1,sheet.getLastRow(),sheet.getLastColumn());
  return range.getValues();
}

ИзнутриВ моем веб-приложении я могу вызвать google.script.run.getUserCrewTable () и получить массив, содержащий все на странице.Я снова вызываю InitializeUserFile, потому что он будет забывать переменные в то же время ... что дает мне userSpreadsheet, ориентированный на электронную таблицу пользователя.Я могу передать конкретный лист, который я ищу (так как у меня есть два), и вернуть стол.Я называю это следующим образом:

google.script.run
            .withSuccessHandler(handleUserQueryResponse)
            .withFailureHandler(failureHandler)
            .getUserCrewTable("my_crew");

function handleUserQueryResponse(response) {
  userData = google.visualization.arrayToDataTable(response);
}

, что userData теперь является dataTable и может использоваться везде, где может использоваться dataTable.

...