2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18)
开发版本: devel

32.20. OAuth 支持 #

libpq 实现了一个可选模块,支持 OAuth v2 设备授权客户端流程,该流程记录在 RFC 8628 中。有关如何启用设备授权作为内置流程支持的信息,请参阅 安装文档

当启用支持并安装了可选模块时,如果在认证过程中服务器 请求令牌(bearer token)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 和权限是否符合预期始终是个好主意。不应将权限授予不受信任的第三方。

客户端应用程序可以实现自己的流程来自定义与应用程序的交互和集成。有关如何将自定义流程添加到 libpq 的更多信息,请参阅 第 32.20.1 节

要使 OAuth 客户端流程可用,连接字符串至少必须包含 oauth_issueroauth_client_id。(这些设置由您组织的 OAuth 提供商确定。) 内置流程还需要 OAuth 授权服务器发布设备授权端点。

注意

内置设备授权流程目前在 Windows 上不受支持。自定义客户端流程仍然可以实现。

32.20.1. Authdata 钩子 #

OAuth 流程的行为可以通过客户端使用以下钩子 API 进行修改或替换:

PQsetAuthDataHook #

设置 PGauthDataHook,覆盖 libpq 对其 OAuth 客户端流程的一个或多个方面的处理。

void PQsetAuthDataHook(PQauthDataHook_type hook);

如果 hookNULL,将重新安装默认处理程序。否则,应用程序将传递一个指向具有以下签名的回调函数的指针:

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

当需要应用程序执行操作时,libpq 将调用此函数。type 描述正在进行的请求,conn 是正在进行身份验证的连接句柄,data 指向请求特定的元数据。此指针的内容由 type 确定;有关支持的列表,请参阅 第 32.20.1.1 节

钩子可以链接在一起,以允许协作和/或回退行为。通常,钩子实现应检查传入的 type (以及可能的请求元数据和/或正在使用的特定 conn 的设置),以决定是否处理特定的 authdata。如果不处理,则应将其委托给链中的前一个钩子 (可通过 PQgetAuthDataHook 检索)。

成功通过返回一个大于零的整数来表示。返回负整数表示错误条件并中止连接尝试。(零值保留给默认实现)。

PQgetAuthDataHook #

检索 PGauthDataHook 的当前值。

PQauthDataHook_type PQgetAuthDataHook(void);

在初始化时 (第一次调用 PQsetAuthDataHook 之前),此函数将返回 PQdefaultAuthDataHook

32.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;

可以 包含libpq 中的 OAuth 设备授权流程要求最终用户使用浏览器访问一个 URL,然后输入一个代码,该代码允许 libpq 代表他们连接到服务器。默认提示只是将 verification_uriuser_code 打印到标准错误。替换实现可以使用任何首选方法显示此信息,例如使用 GUI。

仅在内置设备授权流程期间调用此回调。如果应用程序安装了 自定义 OAuth 流程,或者 libpq 未内置支持内置流程,则不会使用此 authdata 类型。

如果提供了非 NULL 的 verification_uri_complete,则可以选择将其用于非文本验证 (例如,通过显示二维码)。在这种情况下,仍应将 URL 和用户名代码显示给最终用户,因为代码将由提供商手动确认,并且 URL 允许用户继续,即使他们无法使用非文本方法。有关更多信息,请参阅 RFC 8628 的第 3.3.1 节。

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 包含描述授权服务器支持的流程的 OAuth 发现文档的 URL,scope 包含访问服务器所需的 OAuth 范围的空格分隔列表 (可能为空)。两者都可能为 NULL,表示信息无法发现。(在这种情况下,实现可能能够使用其他预配置的知识来建立需求,或者它们可以选择失败)。

钩子的最终输出是 token,它必须指向一个有效的 Bearer 令牌,以便在连接中使用。(此令牌应由 oauth_issuer 发行,并包含请求的范围,否则连接将被服务器的验证器模块拒绝。) 分配的令牌字符串在 libpq 完成连接之前必须保持有效;钩子应设置一个 cleanup 回调,当 libpq 不再需要它时将被调用。

如果实现无法在钩子首次调用期间立即生成 token,它应该设置 async 回调来处理与授权服务器的非阻塞通信。[16] 当钩子返回后,将调用此回调来立即启动流程。当回调无法在不阻塞的情况下取得进一步进展时,它应该在设置 *pgsocket 为将标记为可读/可写以实现再次进度文件描述符之后,返回 PGRES_POLLING_READINGPGRES_POLLING_WRITING。(然后通过 PQsocket() 将此描述符提供给顶级轮询循环)。当流程完成且 token 已设置时,返回 PGRES_POLLING_OK,或返回 PGRES_POLLING_FAILED 来指示失败。

实现可能希望存储额外的数据以在 asynccleanup 回调之间进行管理。user 指针就是为此目的提供的;libpq 不会触及其内容,并且应用程序可以根据需要使用它。(记住在令牌清理期间释放任何分配)。

32.20.2. 调试和开发者设置 #

通过设置环境变量 PGOAUTHDEBUG=UNSAFE 可以启用“危险调试模式”。此功能仅为方便本地开发和测试而提供。它执行了许多您不希望生产系统执行的操作:

  • 允许在 OAuth 提供商交换期间使用未加密的 HTTP。

  • 允许使用 PGOAUTHCAFILE 环境变量完全替换系统的受信任 CA 列表。

  • 在 OAuth 流程期间将 HTTP 流量 (包含多个关键秘密) 打印到标准错误。

  • 允许使用零秒重试间隔,这可能导致客户端忙等待并浪费 CPU。

警告

请勿与第三方共享 OAuth 流程流量的输出。它包含可用于攻击您的客户端和服务器的秘密。



[16]PQAUTHDATA_OAUTH_BEARER_TOKEN 钩子回调期间执行阻塞操作会干扰非阻塞连接 API (如 PQconnectPoll),并阻止并发连接取得进展。仅使用同步连接原语 (如 PQconnectdb) 的应用程序可以在钩子期间同步检索令牌,而不是实现 async 回调,但它们将仅限于一次一个连接。

提交更正

如果您在文档中发现任何不正确、与您使用该功能的实际经验不符或需要进一步说明的内容,请使用 此表单 报告文档问题。