Hello!

> 
> Right -- I hadn't meant that you should remove the PGOAUTHCAFILE
> envvar from your patch, just that an oauth_ca_file parameter should
> be
> added as well.
> 
> 

I'm attached a v2 of this patch I'm not really sure if this is what you
mean.

I want to add some test for this option that I think it could be really
useful, what do you think?

-- 
Jonathan Gonzalez V. <[email protected]>
From ed6b0146d45ded6f0970f9bb8eb9a34d78960f2d Mon Sep 17 00:00:00 2001
From: "Jonathan Gonzalez V." <[email protected]>
Date: Wed, 29 Oct 2025 16:54:42 +0100
Subject: [PATCH v2 1/1] libpq-oauth: allow changing the CA when not in debug
 mode

Allowing to set a CA enables users environment like companies with
internal CA or developers working on their own local system while
using a self-signed CA and don't need to see all the debug messages
while testing inside an internal environment.

Signed-off-by: Jonathan Gonzalez V. <[email protected]>
---
 doc/src/sgml/libpq.sgml                  | 23 ++++++++++++++++------
 src/interfaces/libpq-oauth/oauth-curl.c  | 25 ++++++++++--------------
 src/interfaces/libpq-oauth/oauth-utils.c |  3 +++
 src/interfaces/libpq-oauth/oauth-utils.h |  2 ++
 src/interfaces/libpq/fe-auth-oauth.c     |  3 +++
 src/interfaces/libpq/fe-connect.c        |  4 ++++
 src/interfaces/libpq/libpq-int.h         |  1 +
 src/tools/pgindent/typedefs.list         |  1 +
 8 files changed, 41 insertions(+), 21 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 21e1ba34a4e..f28871d402a 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -10519,12 +10519,6 @@ typedef struct PGoauthBearerRequest
        permits the use of unencrypted HTTP during the OAuth provider exchange
       </para>
      </listitem>
-     <listitem>
-      <para>
-       allows the system's trusted CA list to be completely replaced using the
-       <envar>PGOAUTHCAFILE</envar> environment variable
-      </para>
-     </listitem>
      <listitem>
       <para>
        prints HTTP traffic (containing several critical secrets) to standard
@@ -10546,6 +10540,23 @@ typedef struct PGoauthBearerRequest
     </para>
    </warning>
   </sect2>
+  <sect2 id="libpq-oauth-environment">
+   <title>Environment variables</title>
+   <para>
+    The behavior of the OAuth calls may be affected by the following variables:
+    <variablelist>
+     <varlistentry>
+      <term><envar>PGOAUTHCAFILE</envar></term>
+      <listitem>
+       <para>
+        Allows to specify the path to a CA file that will be used by the client
+        to verify the certificate from the OAuth server side.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+  </sect2>
  </sect1>
 
 
diff --git a/src/interfaces/libpq-oauth/oauth-curl.c b/src/interfaces/libpq-oauth/oauth-curl.c
index 691e7ec1d9f..0460cce65bb 100644
--- a/src/interfaces/libpq-oauth/oauth-curl.c
+++ b/src/interfaces/libpq-oauth/oauth-curl.c
@@ -56,6 +56,7 @@
 #define conn_oauth_discovery_uri(CONN) (CONN->oauth_discovery_uri)
 #define conn_oauth_issuer_id(CONN) (CONN->oauth_issuer_id)
 #define conn_oauth_scope(CONN) (CONN->oauth_scope)
+#define conn_oauth_ca_file(CONN) (CONN->oauth_ca_file)
 #define conn_sasl_state(CONN) (CONN->sasl_state)
 
 #define set_conn_altsock(CONN, VAL) do { CONN->altsock = VAL; } while (0)
@@ -1708,8 +1709,10 @@ debug_callback(CURL *handle, curl_infotype type, char *data, size_t size,
  * start_request().
  */
 static bool
-setup_curl_handles(struct async_ctx *actx)
+setup_curl_handles(struct async_ctx *actx, PGconn *conn)
 {
+	const char *ca_path = conn_oauth_ca_file(conn);
+
 	/*
 	 * Create our multi handle. This encapsulates the entire conversation with
 	 * libcurl for this connection.
@@ -1798,20 +1801,12 @@ setup_curl_handles(struct async_ctx *actx)
 	}
 
 	/*
-	 * If we're in debug mode, allow the developer to change the trusted CA
-	 * list. For now, this is not something we expose outside of the UNSAFE
-	 * mode, because it's not clear that it's useful in production: both libpq
-	 * and the user's browser must trust the same authorization servers for
-	 * the flow to work at all, so any changes to the roots are likely to be
-	 * done system-wide.
+	 * Allow to set the CA even if we're not in debug mode, this would make it easy
+	 * to work on environments were the CA could be internal and available on every
+	 * system, like big companies with airgap systems.
 	 */
-	if (actx->debugging)
-	{
-		const char *env;
-
-		if ((env = getenv("PGOAUTHCAFILE")) != NULL)
-			CHECK_SETOPT(actx, CURLOPT_CAINFO, env, return false);
-	}
+	if (ca_path != NULL)
+		CHECK_SETOPT(actx, CURLOPT_CAINFO, ca_path, return false);
 
 	/*
 	 * Suppress the Accept header to make our request as minimal as possible.
@@ -2804,7 +2799,7 @@ pg_fe_run_oauth_flow_impl(PGconn *conn)
 		if (!setup_multiplexer(actx))
 			goto error_return;
 
-		if (!setup_curl_handles(actx))
+		if (!setup_curl_handles(actx, conn))
 			goto error_return;
 	}
 
diff --git a/src/interfaces/libpq-oauth/oauth-utils.c b/src/interfaces/libpq-oauth/oauth-utils.c
index 4ebe7d0948c..52a8599e15d 100644
--- a/src/interfaces/libpq-oauth/oauth-utils.c
+++ b/src/interfaces/libpq-oauth/oauth-utils.c
@@ -41,6 +41,7 @@ conn_oauth_client_secret_func conn_oauth_client_secret;
 conn_oauth_discovery_uri_func conn_oauth_discovery_uri;
 conn_oauth_issuer_id_func conn_oauth_issuer_id;
 conn_oauth_scope_func conn_oauth_scope;
+conn_oauth_ca_file_func conn_oauth_ca_file;
 conn_sasl_state_func conn_sasl_state;
 
 set_conn_altsock_func set_conn_altsock;
@@ -70,6 +71,7 @@ libpq_oauth_init(pgthreadlock_t threadlock_impl,
 				 conn_oauth_discovery_uri_func discoveryuri_impl,
 				 conn_oauth_issuer_id_func issuerid_impl,
 				 conn_oauth_scope_func scope_impl,
+				 conn_oauth_ca_file_func cafile_impl,
 				 conn_sasl_state_func saslstate_impl,
 				 set_conn_altsock_func setaltsock_impl,
 				 set_conn_oauth_token_func settoken_impl)
@@ -82,6 +84,7 @@ libpq_oauth_init(pgthreadlock_t threadlock_impl,
 	conn_oauth_discovery_uri = discoveryuri_impl;
 	conn_oauth_issuer_id = issuerid_impl;
 	conn_oauth_scope = scope_impl;
+	conn_oauth_ca_file = cafile_impl;
 	conn_sasl_state = saslstate_impl;
 	set_conn_altsock = setaltsock_impl;
 	set_conn_oauth_token = settoken_impl;
diff --git a/src/interfaces/libpq-oauth/oauth-utils.h b/src/interfaces/libpq-oauth/oauth-utils.h
index 9f4d5b692d2..22183f21c6a 100644
--- a/src/interfaces/libpq-oauth/oauth-utils.h
+++ b/src/interfaces/libpq-oauth/oauth-utils.h
@@ -40,6 +40,7 @@ DECLARE_GETTER(char *, oauth_client_secret);
 DECLARE_GETTER(char *, oauth_discovery_uri);
 DECLARE_GETTER(char *, oauth_issuer_id);
 DECLARE_GETTER(char *, oauth_scope);
+DECLARE_GETTER(char *, oauth_ca_file);
 DECLARE_GETTER(fe_oauth_state *, sasl_state);
 
 DECLARE_SETTER(pgsocket, altsock);
@@ -59,6 +60,7 @@ extern PGDLLEXPORT void libpq_oauth_init(pgthreadlock_t threadlock,
 										 conn_oauth_discovery_uri_func discoveryuri_impl,
 										 conn_oauth_issuer_id_func issuerid_impl,
 										 conn_oauth_scope_func scope_impl,
+										 conn_oauth_ca_file_func cafile_impl,
 										 conn_sasl_state_func saslstate_impl,
 										 set_conn_altsock_func setaltsock_impl,
 										 set_conn_oauth_token_func settoken_impl);
diff --git a/src/interfaces/libpq/fe-auth-oauth.c b/src/interfaces/libpq/fe-auth-oauth.c
index 67879d64b39..74fa737e5d3 100644
--- a/src/interfaces/libpq/fe-auth-oauth.c
+++ b/src/interfaces/libpq/fe-auth-oauth.c
@@ -816,6 +816,7 @@ DEFINE_GETTER(char *, oauth_client_secret);
 DEFINE_GETTER(char *, oauth_discovery_uri);
 DEFINE_GETTER(char *, oauth_issuer_id);
 DEFINE_GETTER(char *, oauth_scope);
+DEFINE_GETTER(char *, oauth_ca_file);
 DEFINE_GETTER(fe_oauth_state *, sasl_state);
 
 DEFINE_SETTER(pgsocket, altsock);
@@ -845,6 +846,7 @@ use_builtin_flow(PGconn *conn, fe_oauth_state *state)
 						 conn_oauth_discovery_uri_func discoveryuri_impl,
 						 conn_oauth_issuer_id_func issuerid_impl,
 						 conn_oauth_scope_func scope_impl,
+						 conn_oauth_ca_file_func cafile_impl,
 						 conn_sasl_state_func saslstate_impl,
 						 set_conn_altsock_func setaltsock_impl,
 						 set_conn_oauth_token_func settoken_impl);
@@ -932,6 +934,7 @@ use_builtin_flow(PGconn *conn, fe_oauth_state *state)
 			 conn_oauth_discovery_uri,
 			 conn_oauth_issuer_id,
 			 conn_oauth_scope,
+			 conn_oauth_ca_file,
 			 conn_sasl_state,
 			 set_conn_altsock,
 			 set_conn_oauth_token);
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index a0d2f749811..6f5a1006206 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -412,6 +412,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"OAuth-Scope", "", 15,
 	offsetof(struct pg_conn, oauth_scope)},
 
+	{"oauth_ca_file", "PGOAUTHCAFILE", NULL, NULL,
+	 "Oauth-CA-File", "", 64,
+	 offsetof(struct pg_conn, oauth_ca_file)},
+
 	{"sslkeylogfile", NULL, NULL, NULL,
 		"SSL-Key-Log-File", "D", 64,
 	offsetof(struct pg_conn, sslkeylogfile)},
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index fb6a7cbf15d..3f799e9b34d 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -444,6 +444,7 @@ struct pg_conn
 	char	   *oauth_client_secret;	/* client secret */
 	char	   *oauth_scope;	/* access token scope */
 	char	   *oauth_token;	/* access token */
+	char       *oauth_ca_file;	/* CA file path  */
 	bool		oauth_want_retry;	/* should we retry on failure? */
 
 	/* Optional file to write trace info to */
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 241945734ec..e09858263e7 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3587,6 +3587,7 @@ conn_oauth_client_secret_func
 conn_oauth_discovery_uri_func
 conn_oauth_issuer_id_func
 conn_oauth_scope_func
+conn_oauth_ca_file_func
 conn_sasl_state_func
 contain_aggs_of_level_context
 contain_placeholder_references_context
-- 
2.51.0

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to