Skip to content

Add per connection support for OAuth 2.0 with PostgreSQL 18 - #700

Open
larskanis wants to merge 12 commits into
ged:masterfrom
larskanis:oauth
Open

larskanis wants to merge 12 commits into
ged:masterfrom
larskanis:oauth

Conversation

@larskanis

Copy link
Copy Markdown
Collaborator

This is based on #693, but changed it to a hook which is set as a connection parameter.

I plan to make it based on st_table instead of handling in ruby.
That should make it Ractor compatible.
Let's see what looks better...

Comment thread ext/pg_auth_hooks.c
Comment on lines +354 to +360
rb_cOAuthBearerRequest = rb_define_class_under(rb_mPG, "OAuthBearerRequest", rb_cObject);
rb_undef_alloc_func(rb_cOAuthBearerRequest);

rb_define_method(rb_cOAuthBearerRequest, "openid_configuration", pg_oauth_bearer_request_openid_configuration, 0);
rb_define_method(rb_cOAuthBearerRequest, "scope", pg_oauth_bearer_request_scope, 0);
rb_define_method(rb_cOAuthBearerRequest, "token=", pg_oauth_bearer_request_token_set, 1);
rb_define_method(rb_cOAuthBearerRequest, "token", pg_oauth_bearer_request_token_get, 0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMV, this hook will be used more than the device prompt. The latter requires the additional libpq library, which already has a prompt implementation.

If the device prompt is challenging in any way, I recommend deferring it until needed.

jeltz and others added 6 commits September 19, 2026 14:05
Since the buitt-in OAuth hooks in libpq can return timerfd and not jsut
a socket when you ask for the current file descriptor we are waiting on
we need to make sure to use the right Ruby class to wrap the file
descriptor, if it is not a valid socket we should use IO.
Async is not supported yet,
To avoid overly complicated or duplicated definitions.
This allows to pass the OAuth request to the related PG::Connection object.

The intention of this change is to prepare for a connection local hook.

Another option would be to wrap the PGconn address in the callback into a regular PG::Connection object.
But this is difficult to combine with the garbage collector.
In the end a wrapped object isn't needed, since the PG::Connection is already present after `PG::Connection.connect_start`.
This is before the hook is called.
So in the callback it's enough to compare the PGconn addresses.
The per connection hook works only in async mode, but sync API in ruby-pg is documented as for testing only and they have several flaws already.
So I don't think there is any need to support them with the OAuth hook.
Therefore on sync API the only supported option is non-hooked OAuth.

This moves common code to helpers and hooked OAuth to async specs.

Connecting with a OAuth hook within a Ractor is currently also not possible, but might be changed in future.
This doesn't expose the global function PQsetAuthDataHook to ruby, but only the one per connection.
The conversion of "PGconn *" -> "PG::Connection object" is no longer done per WeakMap, but per st_table.
That should make it easier to get Ractor compatible.
st_table must be protected by a native mutex, to be safely used from multiple ractors.
since the required function is not available.
Because since ged#723 the Webrick thread is blocked by libcurl's blocking HTTP requests within PG.connect .
…a no-op

and the next use raises a
```
  Errno::EADDRINUSE:  Address already in use - bind(2) for 0.0.0.0:23459
```
@larskanis
larskanis force-pushed the oauth branch 4 times, most recently from 27db2a2 to c5d6d0a Compare September 19, 2026 20:43
and work around build issue on mswin and Macos with downloaded PostgreSQL.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants