Вступление
В интернете есть много публикаций на эту тему и все из них подразумевают аутентификацию через встроенный в приложение браузер. В мобильном приложении данный способ не сработает, так как google с 20 апреля 2024 года запретил его и аутентификацию можно пройти только через системный браузер.
Здесь описано много разных способов и для устанавливаемых приложений рекомендуется использовать библиотеки, но, к сожалению, не в нашем случае. Возможно, есть способ как-то срастить библиотеку и 1С с помощью внешней компоненты, но у меня не хватило терпения осилить его. Также имеется ручной способ аутентификации — это когда пользователь разрешает доступ и копирует-вставляет токен обновления (refresh_token) из браузера в приложение. Этот способ самый простой и на нём можно было бы остановиться, но мы не ищем простых путей и в данной публикации речь пойдет о способе аутентификации через промежуточный шлюз, подкрепленный примерами. Способ универсален и будет работать в настольной версии.
Подготовка
Для начала в консоли необходимо создать проект и учетные данные "Идентификатор клиента OAuth" (веб-приложение). В поле "Разрешенные URI перенаправления" указать адреса шлюза (например, localhost/addcode/auth) на которые будет отвечать сервер google (конечный запрос будет иметь вид: localhost/addcode/auth?code=1&state=2&scope=3 или localhost/addcode/auth?state=1&error=2). Этого будет достаточно, но для работы с каким-либо сервисом необходимо включить соответствующий api, создать ключ приложения… Об этом не буду писать, так как в интернете всё подробно расписано. В конце необходимо скачать из консоли json файл с учетными данными.
Далее потребуется веб-сервер (шлюз), где будут реализованы http-сервисы. Их можно сделать как на 1С, так и на другом, более универсальном, языке. Также шлюз физически может располагаться как в локальной сети, так и на хостинге в интернете. Я же выбрал вариант с бесплатным хостингом, где поддерживается php и mysql.
В общий макет УчетныеДанныеGoogle необходимо загрузить скачанный ранее json файл.
Реализация
Процедура аутентификации начинается с клиентской части:
&НаКлиенте
Процедура Войти(Команда)
стрКлюч = Строка(Новый УникальныйИдентификатор);
ДанныеДляВхода = СервисыGoogleКлиентСервер.ДанныеВходаВАккаунт();
УчетныеДанные = ДанныеДляВхода.УчетныеДанные["web"];
ШаблонЗапроса = ДанныеДляВхода.ШаблонЗапроса;
ТекстЗапроса = СтрШаблон(ШаблонЗапроса,
УчетныеДанные["auth_uri"],
УчетныеДанные["client_id"],
УчетныеДанные["redirect_uris"][0],
ДанныеДляВхода.ОбластиДоступа, стрКлюч);
ЗапуститьПриложение(ТекстЗапроса);
ПодключитьОбработчикОжидания("ПолучитьКод", 3, Ложь);
КонецПроцедуры
Здесь формируется текст http-запроса, запускается браузер и подключается обработчик ожидания, который опрашивает шлюз. Переменная стрКлюч необходима для идентификации, по ней будем находить кому что принадлежит. В redirect_uris адреса, которые были указаны при создании учетных данных OAuth в консоли. По этим адресам будет перенаправлять сервер google после ответа пользователя. Нам же необходимо обработать ответ и вывести пользователю результат (успешно…).
Файл index.php
<?php
$servername = "";
$username = "";
$password = "";
$dbname = "";
$setData = false;
if(isset($_GET["error"]) && !empty($_GET["error"])){
$state = $_GET["state"];
$code = "";
$error = $_GET["error"];
$scope = "";
$setData = true;
}elseif(!empty($_GET)){
$state = $_GET["state"];
$code = $_GET["code"];
$error = "";
$scope = $_GET["scope"];
$setData = true;
}
if($setData){
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "INSERT INTO tCodes (state, code, error, scope) VALUES ('$state', '$code', '$error', '$scope')";
if ($conn->query($sql) === TRUE) {
echo "Код получен";
} else {
echo "Error: " . $sql . "<br>" . $conn->error;
}
$conn->close();
}
?>
Код получает значения параметров из запроса и записывает их в базу данных. При использовании такого же способа предварительно нужно создать базу данных и таблицу. Значение параметра state — это стрКлюч, code — код для получения токенов, scope — область доступа. В php не силен, но код рабочий, собирал копипастой из интернета как черновик, а для рабочих систем можно сделать лучше.
Всё это время 1С опрашивает шлюз с ключом и как только ответ будет получен обработчик отключается.
Обработчик ожидания ПолучитьКод:
&НаКлиенте
Процедура ПолучитьКод() Экспорт
АдресШлюза = "";
ТекстЗапроса = "?state=" + стрКлюч;
Запрос = Новый HTTPЗапрос(ТекстЗапроса);
Соединение = Новый HTTPСоединение(АдресШлюза,,,,,, Новый ЗащищенноеСоединениеOpenSSL);
Ответ = Соединение.Получить(Запрос);
стрОтвет = Ответ.ПолучитьТелоКакСтроку();
РезультатЧтения = ОбщегоНазначенияКлиентСервер.ПеревестиJsonВОбъект(стрОтвет);
Если РезультатЧтения["state"] = Неопределено Тогда
Возврат;
КонецЕсли;
ОтключитьОбработчикОжидания("ПолучитьКод");
// Далее механизм обмена кода на токен обновления и токен доступа
КонецПроцедуры
В процедуре нужно указать адрес своего шлюза (например, localhost/addcode/auth/res.php), где реализована обработка и передача данных.
Файл res.php
<?php
$servername = "";
$username = "";
$password = "";
$dbname = "";
$state= $_GET['state'];
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT state, code, error, scope FROM tCodes WHERE state = '$state'";
$result = $conn->query($sql);
if ($result->num_rows == 1) {
$row = $result->fetch_assoc();
$data = [ 'state' => $row["state"], 'code' => $row["code"], 'scope' => $row["scope"], 'error' => $row["error"]];
echo json_encode( $data );
$sql = "DELETE FROM tCodes WHERE state = '$state'";
$conn->query($sql);
} else {
$data = [ 'error' => 'not_found' ];
http_response_code(404);
echo json_encode( $data );
}
$conn->close();
?>
Запрос получает данные по ключу и возвращает в виде json. Этот код тоже рабочий черновик.
Далее значение параметра code нужно обменять на токены (продолжение процедуры ПолучитьКод).
Код = РезультатЧтения["code"];
ДанныеОбменаКодаНаТокен = СервисыGoogleКлиентСервер.ДанныеОбменаКодаНаТокен(стрКлюч);
УчетныеДанные = ДанныеОбменаКодаНаТокен.УчетныеДанные["web"];
ШаблонЗапроса = ДанныеОбменаКодаНаТокен.ШаблонЗапроса;
ТекстЗапроса = СтрШаблон(ШаблонЗапроса, Код, УчетныеДанные["client_id"], УчетныеДанные["client_secret"], УчетныеДанные["redirect_uris"][0]);
Запрос = Новый HTTPЗапрос(ДанныеОбменаКодаНаТокен.АдресРесурса);
Запрос.Заголовки = ДанныеОбменаКодаНаТокен.Заголовки;
Запрос.УстановитьТелоИзСтроки(ТекстЗапроса);
Соединение = Новый HTTPСоединение(ДанныеОбменаКодаНаТокен.АдресСервера,,,,, 10, Новый ЗащищенноеСоединениеOpenSSL);
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
стрОтвет = Ответ.ПолучитьТелоКакСтроку();
Если Ответ.КодСостояния = 200 Тогда
РезультатЧтения = ОбщегоНазначенияКлиентСервер.ПеревестиJsonВОбъект(стрОтвет);
стрТокенОбновления = РезультатЧтения["refresh_token"];
стрТокенДоступа = РезультатЧтения["access_token"];
Если ЗначениеЗаполнено(стрТокенОбновления) Тогда
УстановитьТокенОбновления(стрТокенОбновления);
КонецЕсли;
КонецЕсли;
стрРезультат = стрОтвет;
Итог
Данный способ довольно прост, позволяет получить код без участия пользователя и кроссплатформенный. Напоследок приведу несколько скриншотов приложения:
Инициируем процедуру входа.
Открывается браузер, пользователь выбирает аккаунт и разрешает или отклоняет запрос.
Далее пользователя перенаправляет на страницу шлюза, где в случае успеха выводится "Код получен", а в случае ошибки — описание ошибки. Страницу можно красиво оформить, для учебного примера этого достаточно.
Приложение всё это время опрашивает шлюз и обменивает код на токены.
Проверяем запросом данных пользователя.
В архиве конфигурация и файлы шлюза.
Добрый день!
https://www.googleapis.com/auth/calendar то куда делать запрос в последнем запросе ?
А конечный запрос на получение самих данных для каждой области доступа (scope) свой ? Имею ввиду, если например в первом запросе в scope указать
Помогите пожалуйста.
Можно где-то еще скачать эти файлы ?
(1)если ещё актуально: смотря что вам нужно. У календаря много методов. Подробнее можно узнатьздесь .