31.20. Поддержка OAuth#

31.20. Поддержка OAuth

31.20. Поддержка OAuth #

libpq реализует поддержку клиентского потока авторизации устройства OAuth v2, задокументированного в RFC 8628, в качестве дополнительного модуля

Когда поддержка включена и установлен дополнительный модуль, libpq будет использовать встроенный поток по умолчанию, если сервер запрашивает токен-носитель во время аутентификации. Этот поток может быть использован даже если система, на которой работает клиентское приложение, не имеет доступного веб-браузера, например, при запуске клиента через SSH.

Встроенный поток по умолчанию выведет URL для посещения и код пользователя для ввода:

$ psql 'dbname=postgres oauth_issuer=https://example.com oauth_client_id=...'
Visit https://example.com/device and enter the code: ABCD-EFGH

(Этот запрос может быть настроен.) Затем пользователь войдет в свою учетную запись OAuth, которая спросит, разрешить ли libpq и серверу выполнять действия от их имени. Всегда полезно внимательно проверять отображаемый URL и разрешения, чтобы убедиться, что они соответствуют ожиданиям, прежде чем продолжить. Разрешения не должны предоставляться ненадежным третьим сторонам.

Клиентские приложения могут реализовывать собственные потоки для настройки взаимодействия и интеграции с приложениями. См. Раздел 31.20.1 для получения дополнительной информации о том, как добавить пользовательский поток в libpq.

Чтобы поток клиента OAuth был пригоден для использования, строка подключения должна как минимум содержать oauth_issuer и oauth_client_id. (Эти настройки определяются поставщиком OAuth вашей организации.) Встроенный поток дополнительно требует, чтобы сервер авторизации OAuth публиковал конечную точку авторизации устройства.

Примечание

Пользовательские потоки клиентов все еще могут быть реализованы.

31.20.1. Хуки Authdata #

Поведение потока OAuth может быть изменено или заменено клиентом с использованием следующего API-хука:

PQsetAuthDataHook #

Устанавливает PGauthDataHook, переопределяя обработку libpq одного или нескольких аспектов его клиентского потока OAuth.

void PQsetAuthDataHook(PQauthDataHook_type hook);

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

int hook_fn(PGauthData type, PGconn *conn, void *data);

который libpq вызовет, когда от приложения требуется действие. type описывает запрашиваемое действие, conn — это дескриптор соединения, который проходит аутентификацию, а data указывает на метаданные, специфичные для запроса. Содержимое этого указателя определяется type; см. Раздел 31.20.1.1 для поддерживаемого списка.

Хуки могут быть объединены в цепочку, чтобы обеспечить кооперативное и/или резервное поведение. В общем случае, реализация хука должна проверять входящий type (и, возможно, метаданные запроса и/или настройки для конкретного conn в использовании), чтобы решить, обрабатывать ли конкретный фрагмент authdata. Если нет, она должна делегировать предыдущему хуку в цепочке (доступному через PQgetAuthDataHook).

Успех обозначается возвращением целого числа, большего нуля. Возвращение отрицательного целого числа сигнализирует об ошибке и прекращает попытку подключения. (Значение ноль зарезервировано для реализации по умолчанию.)

PQgetAuthDataHook #

Извлекает текущее значение PGauthDataHook.

PQauthDataHook_type PQgetAuthDataHook(void);

Во время инициализации (до первого вызова PQsetAuthDataHook), эта функция вернет PQdefaultAuthDataHook.

31.20.1.1. Типы хуков #

Следующие типы PGauthData и их соответствующие структуры data определены:

PQAUTHDATA_PROMPT_OAUTH_DEVICE #

Заменяет приглашение пользователя по умолчанию во время встроенного клиентского потока авторизации устройства. data указывает на экземпляр PGpromptOAuthDevice:

typedef struct _PGpromptOAuthDevice
{
    const char *verification_uri;   /* verification URI to visit */
    const char *user_code;          /* user code to enter */
    const char *verification_uri_complete;  /* optional combination of URI and
                                             * code, or NULL */
    int         expires_in;         /* seconds until user code expires */
} PGpromptOAuthDevice;

Подсказка по умолчанию просто выводит verification_uri и user_code в стандартный поток ошибок. Альтернативные реализации могут отображать эту информацию любым предпочтительным способом, например, с помощью графического интерфейса.

Этот обратный вызов вызывается только во время встроенного процесса авторизации устройства. Если приложение устанавливает пользовательский процесс OAuth , или libpq не был собран с поддержкой встроенного процесса, этот тип authdata не будет использоваться.

Если предоставлено ненулевое значение verification_uri_complete, оно может быть использовано для нетекстовой верификации (например, путем отображения QR-кода). В этом случае URL и код пользователя все равно должны быть отображены конечному пользователю, поскольку код будет вручную подтвержден провайдером, а URL позволяет пользователям продолжать, даже если они не могут использовать нетекстовый метод. Для получения дополнительной информации см. раздел 3.3.1 в RFC 8628.

PQAUTHDATA_OAUTH_BEARER_TOKEN #

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

data указывает на экземпляр PGoauthBearerRequest, который должен быть заполнен реализацией:

typedef struct PGoauthBearerRequest
{
    /* Hook inputs (constant across all calls) */
    const char *openid_configuration; /* OIDC discovery URL */
    const char *scope;                /* required scope(s), or NULL */

    /* Hook outputs */

    /* Callback implementing a custom asynchronous OAuth flow. */
    PostgresPollingStatusType (*async) (PGconn *conn,
                                        struct PGoauthBearerRequest *request,
                                        SOCKTYPE *altsock);

    /* Callback to clean up custom allocations. */
    void        (*cleanup) (PGconn *conn, struct PGoauthBearerRequest *request);

    char       *token;   /* acquired Bearer token */
    void       *user;    /* hook-defined allocated data */
} PGoauthBearerRequest;

Два фрагмента информации предоставляются хуку libpq: openid_configuration содержит URL документа обнаружения OAuth, описывающего поддерживаемые потоки сервера авторизации, а scope содержит (возможно, пустой) список разделенных пробелами областей OAuth, которые необходимы для доступа к серверу. Любой из них или оба могут быть NULL, чтобы указать, что информация не была обнаружена. (В этом случае реализации могут быть в состоянии установить требования, используя какую-либо другую предварительно настроенную информацию, или они могут выбрать отказ.)

Окончательный вывод хука — это token, который должен указывать на действительный токен Bearer для использования при подключении. (Этот токен должен быть выдан oauth_issuer и содержать запрашиваемые области, иначе подключение будет отклонено модулем проверки сервера.) Выделенная строка токена должна оставаться действительной до тех пор, пока libpq не завершит подключение; хук должен установить обратный вызов cleanup, который будет вызван, когда libpq больше не будет его требовать.

Если реализация не может сразу создать token во время первоначального вызова хука, она должна установить async обратный вызов для обработки неблокирующей связи с сервером авторизации. [16] Это будет вызвано для немедленного начала потока после возврата из хука. Когда обратный вызов не может продолжать выполнение без блокировки, он должен вернуть либо PGRES_POLLING_READING, либо PGRES_POLLING_WRITING после установки *pgsocket в файловый дескриптор, который будет помечен готовым для чтения/записи, когда выполнение может быть продолжено. (Этот дескриптор затем предоставляется в цикл опроса верхнего уровня через PQsocket().) Верните PGRES_POLLING_OK после установки token, когда поток завершен, или PGRES_POLLING_FAILED для указания на неудачу.

Реализации могут захотеть хранить дополнительные данные для учета между вызовами async и cleanup обратных вызовов. Указатель user предоставляется для этой цели; libpq не будет изменять его содержимое, и приложение может использовать его по своему усмотрению. (Не забудьте освободить любые выделенные ресурсы во время очистки токена.)

31.20.2. Отладка и настройки разработчика #

A "опасный режим отладки" может быть включен путем установки переменной окружения PGOAUTHDEBUG=UNSAFE. Эта функциональность предоставляется только для удобства локальной разработки и тестирования. Она выполняет несколько действий, которые вы не захотите, чтобы производственная система выполняла:

  • разрешает использование незашифрованного HTTP во время обмена с провайдером OAuth

  • позволяет полностью заменить список доверенных УЦ системы, используя переменную окружения PGOAUTHCAFILE

  • выводит HTTP-трафик (содержащий несколько критических секретов) на стандартный поток ошибок во время OAuth-потока

  • разрешает использование интервалов повторной попытки с нулевой секундой, что может привести к зацикливанию клиента и бесполезному потреблению процессора

Предупреждение

Не передавайте данные трафика OAuth третьим сторонам. Они содержат секреты, которые могут быть использованы для атаки на ваших клиентов и серверы.



[16] Выполнение блокирующих операций во время PQAUTHDATA_OAUTH_BEARER_TOKEN обратного вызова хука будет мешать неблокирующим API подключения, таким как PQconnectPoll, и предотвращать прогресс параллельных подключений. Приложения, которые используют только синхронные примитивы подключения, такие как PQconnectdb, могут синхронно получать токен во время хука вместо реализации асинхронного обратного вызова, но они будут ограничены только одним подключением за раз.