Google OAuth и мобильное приложение





Об аутентификации для работы с сервисами google из мобильного или настольного приложения

Вступление

В интернете есть много публикаций на эту тему и все из них подразумевают аутентификацию через встроенный в приложение браузер. В мобильном приложении данный способ не сработает, так как 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"];

Если ЗначениеЗаполнено(стрТокенОбновления) Тогда
УстановитьТокенОбновления(стрТокенОбновления);
КонецЕсли;
КонецЕсли;

стрРезультат = стрОтвет;

Итог

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

Инициируем процедуру входа.

Открывается браузер, пользователь выбирает аккаунт и разрешает или отклоняет запрос.

Далее пользователя перенаправляет на страницу шлюза, где в случае успеха выводится "Код получен", а в случае ошибки — описание ошибки. Страницу можно красиво оформить, для учебного примера этого достаточно.

Приложение всё это время опрашивает шлюз и обменивает код на токены.

Проверяем запросом данных пользователя.

В архиве конфигурация и файлы шлюза.

3 Comments

  1. user958823

    Добрый день!

    А конечный запрос на получение самих данных для каждой области доступа (scope) свой ? Имею ввиду, если например в первом запросе в scope указать https://www.googleapis.com/auth/calendar то куда делать запрос в последнем запросе ?

    Reply
  2. user958823

    Помогите пожалуйста.

    Можно где-то еще скачать эти файлы ?

    Reply
  3. stveans@gmail.com

    (1)если ещё актуально: смотря что вам нужно. У календаря много методов. Подробнее можно узнать здесь.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *