Re: [oauth] Stabilize the libpq-oauth ABI (and allow alternative implementations?)
Jacob Champion <jacob.champion@enterprisedb.com>
From: Jacob Champion <jacob.champion@enterprisedb.com>
To: PostgreSQL Hackers <pgsql-hackers@postgresql.org>
Cc: Chao Li <li.evan.chao@gmail.com>,
Zsolt Parragi <zsolt.parragi@percona.com>
Date: 2026-02-24T22:51:16Z
Lists: pgsql-hackers
Attachments
- v3-0001-oauth-Report-cleanup-errors-as-warnings-on-stderr.patch (application/x-patch) patch v3-0001
- v3-0002-libpq-Add-PQgetThreadLock-to-mirror-PQregisterThr.patch (application/x-patch) patch v3-0002
- v3-0003-libpq-Introduce-PQAUTHDATA_OAUTH_BEARER_TOKEN_V2.patch (application/x-patch) patch v3-0003
- v3-0004-libpq-oauth-Use-the-PGoauthBearerRequestV2-API.patch (application/x-patch) patch v3-0004
- v3-0005-libpq-oauth-Never-link-against-libpq-s-encoding-f.patch (application/x-patch) patch v3-0005
- v3-0006-WIP-Introduce-third-party-OAuth-flow-plugins.patch (application/x-patch) patch v3-0006
On Tue, Dec 16, 2025 at 9:40 AM Jacob Champion <jacob.champion@enterprisedb.com> wrote: > Open questions remain: > 1) 0004: Any objections to putting PQExpBuffer into libpq-fe.h? Tom sounded lukewarm to this on the Discord, so I looked into replacing the new usage with a simple char*. That actually exposed a bug: appending error data during cleanup doesn't help us at all, because we've already stopped using the error buffer. In fact my whole idea of adding things to conn->errorMessage during a teardown operation has been nearly useless from the beginning. Elsewhere in libpq, we handle cases like these (which should ideally just not happen) by printing warnings to stderr, so v3-0001 does that instead. Besides exposing a bug, switching to char* means anyone who's not programming in C and already has code that handles pointer ownership across the boundary can continue to make use of that, instead of adapting to a completely new API for this one particular use case. We already have the cleanup-callback requirement for the token anyway; it's fine. > 2) 0004: Thoughts on the v2 inheritance struct style as opposed to > relying on implementations to double-check the struct length? Still feels half-dozen or the other to me, so I'm planning to move ahead with the inheritance model. I'll look into VALGRIND_MAKE_MEM_NOACCESS (and/or other ways to catch people scribbling where they shouldn't) for v4. > 3) 0005: Should I add the thread lock to an init() API, or expose a > new PQgetThreadLock() that other code can use? v3-0002 implements PQgetThreadLock(). The conversation with Nico Williams at [1] cemented this for me; it's entirely possible that implementations will need the lock at other times besides initialization, say if they're mixing OAuth with Kerberos. Adding a way to retrieve it doesn't actually expose new functionality -- applications could always get at our internal implementation by calling PQregisterThreadLock(NULL) -- but libraries can't use that API safely. Also, clients can probably make use of some of the newer ways of doing this kind of initialization (pthread_once, etc.) that weren't in wide use back when the init-function design showed up. We may not ever actually need a separate init function, and if I'm wrong we can always add one. I think I'll put this in its own top-level thread for comment. > (In other words, a plugin architecture causes the compile-time > and run-time dependency arrows to point in opposite directions, so > plugins won't be able to rely on the LIBPQ_HAS_* macros to determine > what APIs are available to them.) > > (TODO: Are there implications for our use of RTLD_NOW at dlopen() time? To answer my own question, yes: any future libpq-oauth plugin that needs PG20+ APIs will have to lazy-load them (weak symbol declarations, etc.) or else accept that long-running applications may fail OAuth connections until they restart. A more general plugin system would need to solve this. For example, we could load RTLD_LAZY, check for a minimum libpq version declared by the plugin, and then either upgrade the bindings with RTLD_NOW, or dlclose() and bail. But I don't think I need to be solving a nonexistent problem now. --Jacob [1] https://postgr.es/m/aSSp03wmNMngi/Oe%40ubby
Commits
Same data as JSON:
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
libpq: Allow developers to reimplement libpq-oauth
- 09532b4040ed 19 (unreleased) landed
-
libpq: Poison the v2 part of a v1 Bearer request
- 0af4d402cb90 19 (unreleased) landed
-
libpq-oauth: Never link against libpq's encoding functions
- dba35604485f 19 (unreleased) landed
-
libpq-oauth: Use the PGoauthBearerRequestV2 API
- 6225403f2783 19 (unreleased) landed
-
libpq: Introduce PQAUTHDATA_OAUTH_BEARER_TOKEN_V2
- e982331b5208 19 (unreleased) landed
-
libpq: Add PQgetThreadLock() to mirror PQregisterThreadLock()
- b8d76858353e 19 (unreleased) landed
-
oauth: Report cleanup errors as warnings on stderr
- f8c0b91a6063 19 (unreleased) landed
-
oauth_validator: Avoid races in log_check()
- c3df85756ceb 18.2 landed
- ab8af1db4303 19 (unreleased) landed
-
libpq-oauth: use correct c_args in meson.build
- 023a3c786b81 18.2 landed
- 781ca72139d6 19 (unreleased) landed
-
libpq-fe.h: Don't claim SOCKTYPE in the global namespace
- cc824482a3c0 18.2 landed
- 8b217c96ea2d 19 (unreleased) landed