11using System ;
22using System . Collections . Generic ;
33using System . Diagnostics . CodeAnalysis ;
4+ using System . Net . Security ;
45using System . Reflection ;
6+ using System . Security . Cryptography . X509Certificates ;
57using System . Threading ;
68using System . Threading . Tasks ;
79using Microsoft . Extensions . Logging ;
@@ -21,6 +23,9 @@ public class NpgsqlDataSourceBuilder : INpgsqlTypeMapper
2123 ILoggerFactory ? _loggerFactory ;
2224 bool _sensitiveDataLoggingEnabled ;
2325
26+ RemoteCertificateValidationCallback ? _userCertificateValidationCallback ;
27+ Action < X509CertificateCollection > ? _clientCertificatesCallback ;
28+
2429 Func < NpgsqlConnectionStringBuilder , CancellationToken , ValueTask < string > > ? _periodicPasswordProvider ;
2530 TimeSpan _periodicPasswordSuccessRefreshInterval , _periodicPasswordFailureRefreshInterval ;
2631
@@ -77,6 +82,77 @@ public NpgsqlDataSourceBuilder EnableParameterLogging(bool parameterLoggingEnabl
7782 return this ;
7883 }
7984
85+ #region Authentication
86+
87+ /// <summary>
88+ /// When using SSL/TLS, this is a callback that allows customizing how the PostgreSQL-provided certificate is verified. This is an
89+ /// advanced API, consider using <see cref="SslMode.VerifyFull" /> or <see cref="SslMode.VerifyCA" /> instead.
90+ /// </summary>
91+ /// <param name="userCertificateValidationCallback">The callback containing custom callback verification logic.</param>
92+ /// <remarks>
93+ /// <para>
94+ /// Cannot be used in conjunction with <see cref="SslMode.Disable" />, <see cref="SslMode.VerifyCA" /> or
95+ /// <see cref="SslMode.VerifyFull" />.
96+ /// </para>
97+ /// <para>
98+ /// See <see href="https://msdn.microsoft.com/en-us/library/system.net.security.remotecertificatevalidationcallback(v=vs.110).aspx"/>.
99+ /// </para>
100+ /// </remarks>
101+ /// <returns>The same builder instance so that multiple calls can be chained.</returns>
102+ public NpgsqlDataSourceBuilder UseUserCertificateValidationCallback (
103+ RemoteCertificateValidationCallback userCertificateValidationCallback )
104+ {
105+ _userCertificateValidationCallback = userCertificateValidationCallback ;
106+
107+ return this ;
108+ }
109+
110+ /// <summary>
111+ /// Specifies an SSL/TLS certificate which Npgsql will send to PostgreSQL for certificate-based authentication.
112+ /// </summary>
113+ /// <param name="clientCertificate">The client certificate to be sent to PostgreSQL when opening a connection.</param>
114+ /// <returns>The same builder instance so that multiple calls can be chained.</returns>
115+ public NpgsqlDataSourceBuilder UseClientCertificate ( X509Certificate ? clientCertificate )
116+ {
117+ if ( clientCertificate is null )
118+ return UseClientCertificatesCallback ( null ) ;
119+
120+ var clientCertificates = new X509CertificateCollection { clientCertificate } ;
121+ return UseClientCertificates ( clientCertificates ) ;
122+ }
123+
124+ /// <summary>
125+ /// Specifies a collection of SSL/TLS certificates which Npgsql will send to PostgreSQL for certificate-based authentication.
126+ /// </summary>
127+ /// <param name="clientCertificates">The client certificate collection to be sent to PostgreSQL when opening a connection.</param>
128+ /// <returns>The same builder instance so that multiple calls can be chained.</returns>
129+ public NpgsqlDataSourceBuilder UseClientCertificates ( X509CertificateCollection ? clientCertificates )
130+ => UseClientCertificatesCallback ( clientCertificates is null ? null : certs => certs . AddRange ( clientCertificates ) ) ;
131+
132+ /// <summary>
133+ /// Specifies a callback to modify the collection of SSL/TLS client certificates which Npgsql will send to PostgreSQL for
134+ /// certificate-based authentication. This is an advanced API, consider using <see cref="UseClientCertificate" /> or
135+ /// <see cref="UseClientCertificates" /> instead.
136+ /// </summary>
137+ /// <param name="clientCertificatesCallback">The callback to modify the client certificate collection.</param>
138+ /// <remarks>
139+ /// <para>
140+ /// The callback is invoked every time a physical connection is opened, and is therefore suitable for rotating short-lived client
141+ /// certificates. Simply make sure the certificate collection argument has the up-to-date certificate(s).
142+ /// </para>
143+ /// <para>
144+ /// The callback's collection argument already includes any client certificates specified via the connection string or environment
145+ /// variables.
146+ /// </para>
147+ /// </remarks>
148+ /// <returns>The same builder instance so that multiple calls can be chained.</returns>
149+ public NpgsqlDataSourceBuilder UseClientCertificatesCallback ( Action < X509CertificateCollection > ? clientCertificatesCallback )
150+ {
151+ _clientCertificatesCallback = clientCertificatesCallback ;
152+
153+ return this ;
154+ }
155+
80156 /// <summary>
81157 /// Configures a periodic password provider, which is automatically called by the data source at some regular interval. This is the
82158 /// recommended way to fetch a rotating access token.
@@ -116,6 +192,8 @@ public NpgsqlDataSourceBuilder UsePeriodicPasswordProvider(
116192 return this ;
117193 }
118194
195+ #endregion Authentication
196+
119197 #region Type mapping
120198
121199 /// <inheritdoc />
@@ -274,6 +352,8 @@ public NpgsqlDataSource Build()
274352
275353 var config = new NpgsqlDataSourceConfiguration (
276354 loggingConfiguration ,
355+ _userCertificateValidationCallback ,
356+ _clientCertificatesCallback ,
277357 _periodicPasswordProvider ,
278358 _periodicPasswordSuccessRefreshInterval ,
279359 _periodicPasswordFailureRefreshInterval ,
0 commit comments