OAuth2 — это стандартный протокол делегированной авторизации, позволяющий приложениям получать ограниченный доступ к ресурсам пользователя на другом сервисе без необходимости получать его учетные данные (логин и пароль). В контексте Google Apps Script (GAS), OAuth2 становится незаменимым инструментом для безопасного взаимодействия с API Google и сторонними сервисами.
Что такое OAuth2 и зачем он нужен в Google Apps Script
Представьте, что вы пишете скрипт для Google Sheets, который должен автоматически загружать данные о расходах из рекламного кабинета сторонней платформы. Передавать логин и пароль от этой платформы скрипту — крайне небезопасно. OAuth2 решает эту проблему, предоставляя механизм, при котором пользователь разрешает вашему скрипту доступ только к определенным данным (например, к данным о кампаниях) на ограниченное время, не раскрывая своих полных учетных данных.
Использование OAuth2 в GAS позволяет:
- Безопасно авторизоваться в API Google (Sheets, Drive, Calendar, Gmail, Ads, Analytics и т.д.).
- Интегрироваться со сторонними сервисами, поддерживающими OAuth2 (CRM, маркетинговые платформы, социальные сети).
- Управлять уровнем доступа через scopes (области разрешений), запрашивая только необходимые права.
- Избежать хранения чувствительных пользовательских паролей в коде скрипта.
Проблемы аутентификации без OAuth2 в Google Apps Script
Попытки аутентификации без использования стандартного протокола OAuth2 часто приводят к проблемам:
- Небезопасное хранение учетных данных: Использование логина/пароля или API-ключей, жестко закодированных в скрипте или даже в
PropertiesService, повышает риск их компрометации. - Ограниченная поддержка API: Многие современные API требуют именно OAuth2 и не поддерживают устаревшие методы аутентификации (например, basic auth) или простые API-ключи для доступа к пользовательским данным.
- Сложность реализации: Ручная реализация всего потока OAuth2 (получение кода авторизации, обмен его на токен, обработка обновления токена) в GAS является трудоемкой и подверженной ошибкам задачей.
Обзор OAuth2 библиотеки для Google Apps Script
К счастью, сообщество Google Apps Script разработало и поддерживает удобную библиотеку OAuth2 (Script ID: 1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF). Эта библиотека значительно упрощает интеграцию OAuth2 в ваши скрипты, абстрагируя сложные детали протокола.
Основные возможности библиотеки:
- Упрощение процесса получения URL авторизации.
- Автоматическая обработка callback-запросов.
- Получение и безопасное хранение access и refresh токенов (используя
PropertiesService). - Автоматическое обновление access токена с помощью refresh токена.
- Предоставление удобных методов для проверки наличия доступа и получения токена.
Настройка OAuth2 для вашего проекта Google Apps Script
Перед использованием библиотеки необходимо настроить OAuth2 в Google Cloud Platform (GCP) и сохранить учетные данные в вашем проекте GAS.
Создание и настройка проекта в Google Cloud Platform (GCP)
- Перейдите в Google Cloud Console.
- Создайте новый проект или выберите существующий, к которому будет привязан ваш скрипт.
- Убедитесь, что для проекта включен биллинг (некоторые API требуют этого, хотя сама настройка OAuth2 бесплатна).
Включение API и получение учетных данных OAuth 2.0
- В меню навигации выберите APIs & Services > Library.
- Найдите и включите API, к которым ваш скрипт будет обращаться (например, Google Sheets API, Google Drive API).
- Перейдите в APIs & Services > Credentials.
- Нажмите Create Credentials > OAuth client ID.
- Если потребуется, настройте OAuth consent screen (экран запроса разрешений). Укажите:
- User Type:
Internal(если скрипт будет использоваться только внутри вашей Google Workspace организации) илиExternal(для общего доступа). - App name: Название вашего приложения (будет показано пользователям).
- User support email: Ваш email.
- Authorized domains: Домен вашего приложения (обычно не требуется для GAS).
- Developer contact information: Ваш email.
- User Type:
- При создании OAuth client ID выберите Application type:
Web application. - Укажите Name для вашего клиента (например,
My GAS Script Client).
Настройка URI перенаправления (redirect URI)
Это критически важный шаг. После того как пользователь предоставит разрешение на экране согласия Google, он будет перенаправлен обратно в ваш скрипт через специальный URL.
- В секции Authorized redirect URIs нажмите Add URI.
- Введите URI в следующем формате:
https://script.google.com/macros/d/{SCRIPT ID}/usercallback
Замените{SCRIPT ID}на Script ID вашего проекта Google Apps Script (его можно найти в Project Settings > Script ID). - Нажмите Create.
Сохранение учетных данных в Google Apps Script
После создания OAuth client ID вы получите Client ID и Client Secret. Их необходимо безопасно сохранить в свойствах вашего скрипта, а не в коде.
/**
* Сохраняет Client ID и Client Secret в свойствах скрипта.
* Запускать эту функцию нужно один раз вручную из редактора.
*/
function saveCredentials(): void {
const scriptProperties: GoogleAppsScript.Properties.Properties = PropertiesService.getScriptProperties();
// Замените значения ниже вашими реальными Client ID и Client Secret
const clientId: string = 'YOUR_CLIENT_ID';
const clientSecret: string = 'YOUR_CLIENT_SECRET';
if (!clientId || clientId === 'YOUR_CLIENT_ID' || !clientSecret || clientSecret === 'YOUR_CLIENT_SECRET') {
Logger.log('Пожалуйста, укажите реальные Client ID и Client Secret в функции saveCredentials.');
return;
}
scriptProperties.setProperty('OAUTH2_CLIENT_ID', clientId);
scriptProperties.setProperty('OAUTH2_CLIENT_SECRET', clientSecret);
Logger.log('Учетные данные OAuth2 сохранены в ScriptProperties.');
}
Важно: Замените 'YOUR_CLIENT_ID' и 'YOUR_CLIENT_SECRET' на реальные значения, полученные из GCP, и запустите эту функцию один раз вручную из редактора скриптов.
Использование OAuth2 библиотеки в Google Apps Script
Теперь, когда настройка завершена, можно использовать библиотеку для управления процессом авторизации.
Установка и импорт OAuth2 библиотеки
- В редакторе скриптов откройте Resources > Libraries (в старом редакторе) или Libraries > + (в новом редакторе).
- В поле Find a Library или Script ID введите ID библиотеки:
1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF - Нажмите Search.
- Выберите последнюю версию библиотеки и оставьте идентификатор по умолчанию (
OAuth2). - Нажмите Add.
Создание и настройка сервиса OAuth2
Сервис OAuth2 — это основной объект библиотеки, который управляет всем процессом аутентификации для конкретного API.
/**
* Создает и возвращает сконфигурированный сервис OAuth2 для Google Sheets API.
* @returns {OAuth2.Service} Сконфигурированный сервис OAuth2.
*/
function getSheetsService_(): OAuth2.Service {
const scriptProperties: GoogleAppsScript.Properties.Properties = PropertiesService.getScriptProperties();
const clientId: string | null = scriptProperties.getProperty('OAUTH2_CLIENT_ID');
const clientSecret: string | null = scriptProperties.getProperty('OAUTH2_CLIENT_SECRET');
if (!clientId || !clientSecret) {
throw new Error('Client ID или Client Secret не найдены в ScriptProperties. Запустите saveCredentials().');
}
// Создание сервиса OAuth2
return OAuth2.createService('GoogleSheets')
// Данные клиента из GCP
.setClientId(clientId)
.setClientSecret(clientSecret)
// URL для авторизации и получения токена Google
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
.setTokenUrl('https://oauth2.googleapis.com/token')
// Имя функции обратного вызова (callback function)
.setCallbackFunction('authCallback')
// Хранилище для токенов (рекомендуется использовать scriptProperties)
.setPropertyStore(PropertiesService.getScriptProperties())
// Области разрешений (scopes), необходимые для работы с Sheets API
.setScope('https://www.googleapis.com/auth/spreadsheets')
// Дополнительные параметры (необязательно)
.setParam('access_type', 'offline') // Запрос refresh_token для длительного доступа
.setParam('prompt', 'consent'); // Всегда показывать экран согласия (для отладки)
}
/**
* Функция обратного вызова (callback), обрабатывающая перенаправление от Google.
* @param {GoogleAppsScript.Events.DoGet} request Параметры запроса, переданные Google.
* @returns {GoogleAppsScript.HTML.HtmlOutput} Результат для отображения пользователю.
*/
function authCallback(request: GoogleAppsScript.Events.DoGet): GoogleAppsScript.HTML.HtmlOutput {
const sheetsService: OAuth2.Service = getSheetsService_(); // Получаем тот же сервис
const isAuthorized: boolean = sheetsService.handleCallback(request);
if (isAuthorized) {
return HtmlService.createHtmlOutput('Авторизация прошла успешно! Можете закрыть эту вкладку.');
} else {
return HtmlService.createHtmlOutput('Ошибка авторизации. Попробуйте снова.');
}
}
Получение URL-адреса авторизации
Если скрипт обнаруживает, что у него нет доступа (токен отсутствует или истек), он должен перенаправить пользователя на URL авторизации.
/**
* Инициирует процесс авторизации, если требуется.
*/
function authorizeIfNeeded_(): void {
const sheetsService: OAuth2.Service = getSheetsService_();
if (!sheetsService.hasAccess()) {
const authorizationUrl: string = sheetsService.getAuthorizationUrl();
// Показываем URL пользователю (например, в диалоговом окне или логе)
// В реальном приложении это может быть кнопка или ссылка.
Logger.log(`Пожалуйста, перейдите по ссылке для авторизации: ${authorizationUrl}`);
// Важно: после перехода по ссылке и авторизации будет вызвана функция authCallback.
// Текущее выполнение скрипта здесь обычно прерывается или ожидает действия пользователя.
} else {
Logger.log('Скрипт уже авторизован.');
}
}
Обработка ответа авторизации и получение токена доступа
Это происходит внутри функции authCallback, которую мы определили ранее. Метод sheetsService.handleCallback(request) обрабатывает параметры, переданные Google после авторизации, получает и сохраняет токен доступа (и refresh token, если был запрошен access_type=offline).
Использование токена доступа для доступа к API Google или другим сервисам
После успешной авторизации (sheetsService.hasAccess() возвращает true), можно получить токен доступа и использовать его для выполнения запросов к API.
/**
* Пример функции, использующей авторизованный доступ к Google Sheets API.
*/
function readSheetData(): void {
const sheetsService: OAuth2.Service = getSheetsService_();
if (!sheetsService.hasAccess()) {
Logger.log('Необходима авторизация. Запустите authorizeIfNeeded_() или предоставьте ссылку пользователю.');
authorizeIfNeeded_(); // Попытка показать ссылку
return;
}
// Получаем токен доступа
const accessToken: string = sheetsService.getAccessToken();
// Формируем запрос к Google Sheets API
const spreadsheetId: string = 'YOUR_SPREADSHEET_ID'; // Замените на ID вашей таблицы
const range: string = 'Sheet1!A1:B2'; // Замените на нужный диапазон
const apiUrl: string = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${range}`;
const options: GoogleAppsScript.URL_Fetch.URLFetchRequestOptions = {
method: 'get',
headers: {
'Authorization': `Bearer ${accessToken}` // Передаем токен в заголовке
},
muteHttpExceptions: true // Позволяет обрабатывать ошибки API вручную
};
try {
const response: GoogleAppsScript.URL_Fetch.HTTPResponse = UrlFetchApp.fetch(apiUrl, options);
const responseCode: number = response.getResponseCode();
const responseBody: string = response.getContentText();
if (responseCode === 200) {
const data: { values: any[][] } = JSON.parse(responseBody);
Logger.log('Данные из таблицы:');
Logger.log(data.values);
} else {
Logger.log(`Ошибка API: Код ${responseCode}, Тело: ${responseBody}`);
// Попытка сбросить токен, если он мог стать невалидным
if (responseCode === 401 || responseCode === 403) {
sheetsService.reset();
Logger.log('Токен доступа сброшен из-за ошибки авторизации. Попробуйте авторизоваться снова.');
}
}
} catch (error) {
Logger.log(`Ошибка выполнения запроса: ${error}`);
}
}
Примеры использования OAuth2 библиотеки
Авторизация доступа к Google Sheets API
Пример выше (readSheetData) демонстрирует базовый сценарий чтения данных из Google Sheets с использованием OAuth2.
Авторизация доступа к Google Drive API
Аналогично Sheets, для Drive API нужно создать отдельный сервис (или модифицировать существующий), указав соответствующие scopes.
/**
* Создает и возвращает сервис OAuth2 для Google Drive API.
* @returns {OAuth2.Service} Сконфигурированный сервис OAuth2.
*/
function getDriveService_(): OAuth2.Service {
// ... (код получения clientId, clientSecret аналогичен getSheetsService_) ...
const scriptProperties: GoogleAppsScript.Properties.Properties = PropertiesService.getScriptProperties();
const clientId: string | null = scriptProperties.getProperty('OAUTH2_CLIENT_ID');
const clientSecret: string | null = scriptProperties.getProperty('OAUTH2_CLIENT_SECRET');
if (!clientId || !clientSecret) {
throw new Error('Client ID или Client Secret не найдены.');
}
return OAuth2.createService('GoogleDrive')
.setClientId(clientId)
.setClientSecret(clientSecret)
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
.setTokenUrl('https://oauth2.googleapis.com/token')
.setCallbackFunction('authCallback') // Можно использовать ту же callback функцию
.setPropertyStore(PropertiesService.getScriptProperties())
// Scope для чтения метаданных файлов
.setScope('https://www.googleapis.com/auth/drive.readonly.metadata')
.setParam('access_type', 'offline')
.setParam('prompt', 'consent');
}
/**
* Пример функции для получения списка файлов из Google Drive.
*/
function listDriveFiles(): void {
const driveService: OAuth2.Service = getDriveService_();
if (!driveService.hasAccess()) {
Logger.log(`Требуется авторизация для Drive. URL: ${driveService.getAuthorizationUrl()}`);
return;
}
const accessToken: string = driveService.getAccessToken();
const apiUrl: string = 'https://www.googleapis.com/drive/v3/files?pageSize=10&fields=files(id,name)';
const options: GoogleAppsScript.URL_Fetch.URLFetchRequestOptions = {
method: 'get',
headers: { 'Authorization': `Bearer ${accessToken}` },
muteHttpExceptions: true
};
try {
const response: GoogleAppsScript.URL_Fetch.HTTPResponse = UrlFetchApp.fetch(apiUrl, options);
// ... (обработка ответа аналогична readSheetData) ...
Logger.log(response.getContentText());
} catch (error) {
Logger.log(`Ошибка выполнения запроса к Drive API: ${error}`);
}
}
Авторизация доступа к сторонним API
Принцип тот же, но нужно использовать URL авторизации, URL токена и scopes, специфичные для стороннего сервиса. Предположим, есть гипотетический сервис аналитики маркетинга.
/**
* Создает сервис OAuth2 для гипотетического Marketing Analytics API.
* @returns {OAuth2.Service} Сконфигурированный сервис OAuth2.
*/
function getMarketingApiService_(): OAuth2.Service {
// ... (получение clientId, clientSecret) ...
const scriptProperties: GoogleAppsScript.Properties.Properties = PropertiesService.getScriptProperties();
const clientId: string | null = scriptProperties.getProperty('MARKETING_API_CLIENT_ID'); // Используйте другие ключи свойств!
const clientSecret: string | null = scriptProperties.getProperty('MARKETING_API_CLIENT_SECRET');
if (!clientId || !clientSecret) {
throw new Error('Client ID или Client Secret для Marketing API не найдены.');
}
return OAuth2.createService('MarketingApi')
.setClientId(clientId)
.setClientSecret(clientSecret)
// URL конкретного стороннего сервиса
.setAuthorizationBaseUrl('https://api.example-marketing.com/oauth/authorize')
.setTokenUrl('https://api.example-marketing.com/oauth/token')
.setCallbackFunction('authCallbackMarketing') // Нужна своя callback функция!
.setPropertyStore(PropertiesService.getScriptProperties())
// Scopes конкретного стороннего сервиса
.setScope('read_campaigns write_reports')
.setParam('access_type', 'offline'); // Если поддерживается
}
/**
* Callback функция для Marketing API.
* @param {GoogleAppsScript.Events.DoGet} request Параметры запроса.
* @returns {GoogleAppsScript.HTML.HtmlOutput} Результат.
*/
function authCallbackMarketing(request: GoogleAppsScript.Events.DoGet): GoogleAppsScript.HTML.HtmlOutput {
const marketingService: OAuth2.Service = getMarketingApiService_();
const isAuthorized: boolean = marketingService.handleCallback(request);
// ... (обработка результата) ...
return HtmlService.createHtmlOutput(isAuthorized ? 'Marketing API авторизован.' : 'Ошибка авторизации Marketing API.');
}
// Функция использования Marketing API будет аналогична Google API примерам
Важно: Для каждого стороннего сервиса (или даже разных API Google с разными настройками) рекомендуется создавать свой экземпляр OAuth2.Service и, при необходимости, свою callback-функцию.
Продвинутые темы и лучшие практики
Обновление токенов доступа
Access токены имеют ограниченный срок жизни (обычно 1 час для Google). Если вы запросили access_type=offline, OAuth2 библиотека автоматически получит refresh token и будет использовать его для получения нового access токена, когда старый истечет. Вам не нужно вручную управлять этим процессом, если setPropertyStore настроен — библиотека сохранит refresh token и будет его использовать при вызове getAccessToken().
Безопасное хранение и управление учетными данными
- Никогда не храните Client ID и Client Secret непосредственно в коде.
- Используйте
PropertiesService.getScriptProperties()для хранения этих данных. Доступ к Script Properties имеют только редакторы скрипта. - Для более сложных сценариев или повышенных требований безопасности в рамках Google Workspace можно рассмотреть использование Google Secret Manager, но это потребует вызова Cloud Functions из Apps Script, что усложняет архитектуру.
- Регулярно проверяйте и отзывайте неиспользуемые разрешения в настройках вашего Google Аккаунта и GCP.
Обработка ошибок и отладка
- Оборачивайте вызовы методов библиотеки OAuth2 и запросы
UrlFetchAppв блокиtry...catch. - Проверяйте ответ
UrlFetchApp(getResponseCode(),getContentText()) на наличие ошибок API (коды 4xx, 5xx). - При ошибках авторизации (401, 403) используйте
service.reset()для сброса сохраненных токенов и инициируйте процесс авторизации заново. - Используйте
Logger.log()или продвинутые методы логирования для отслеживания потока выполнения и значений переменных. - Метод
service.getLastError()может предоставить дополнительную информацию об ошибках внутри библиотеки OAuth2.
Советы по оптимизации OAuth2 интеграции в Google Apps Script
- Запрашивайте минимально необходимые scopes: Не запрашивайте доступ ко всему Drive API, если вам нужно только читать один файл. Это повышает безопасность и доверие пользователей.
- Кэширование: Библиотека OAuth2 кэширует токены в
PropertiesService. Избегайте создания нового экземпляраOAuth2.Serviceпри каждом вызове функции; создавайте его один раз и передавайте или получайте с помощьюget...Service_()функции. - Понятный UI/UX: Предоставьте пользователю четкие инструкции по процессу авторизации. Объясните, зачем скрипту нужен доступ.
- Обработка отзыва разрешений: Пользователь может отозвать доступ к вашему скрипту в настройках своего Google Аккаунта. Ваш скрипт должен корректно обрабатывать ошибки 401/403, возникающие в этом случае, и предлагать пользователю пройти авторизацию заново.