@@ -65,14 +65,14 @@ public struct NIOHTTPServerConfiguration: Sendable {
6565 certificateChain: [ Certificate ] ,
6666 privateKey: Certificate . PrivateKey ,
6767 trustRoots: [ Certificate ] ? ,
68- certificateVerification: CertificateVerification = . noHostnameVerification,
69- customCertificateVerificationCallback: CustomCertificateVerificationCallback ? = nil
68+ certificateVerification: CertificateVerificationMode = . noHostnameVerification,
69+ customCertificateVerificationCallback: ( @ Sendable ( [ X509 . Certificate ] ) async throws -> CertificateVerificationResult ) ? = nil
7070 )
7171 case reloadingMTLS(
7272 certificateReloader: any CertificateReloader ,
7373 trustRoots: [ Certificate ] ? ,
74- certificateVerification: CertificateVerification = . noHostnameVerification,
75- customCertificateVerificationCallback: CustomCertificateVerificationCallback ? = nil
74+ certificateVerification: CertificateVerificationMode = . noHostnameVerification,
75+ customCertificateVerificationCallback: ( @ Sendable ( [ X509 . Certificate ] ) async throws -> CertificateVerificationResult ) ? = nil
7676 )
7777 }
7878
@@ -103,24 +103,35 @@ public struct NIOHTTPServerConfiguration: Sendable {
103103 Self ( backing: . reloadingTLS( certificateReloader: certificateReloader) )
104104 }
105105
106- /// Enables mTLS. Optionally provide a custom verification callback to override the default verification logic
107- /// used to verify client certificates, and control the derivation of a validated chain of trust from the
108- /// certificates presented by the peer.
106+ /// Enables mTLS.
109107 ///
110108 /// - Parameters:
111109 /// - certificateChain: The certificate chain to present during negotiation.
112110 /// - privateKey: The private key corresponding to the leaf certificate in `certificateChain`.
113111 /// - trustRoots: The root certificates to trust when verifying client certificates.
114112 /// - certificateVerification: Configures the client certificate validation behaviour. Defaults to
115- /// ``CertificateVerification/noHostnameVerification``.
116- /// - customCertificateVerificationCallback: A custom certificate verification callback. This will override
117- /// NIOSSL's default certificate verification logic.
113+ /// ``CertificateVerificationMode/noHostnameVerification``.
114+ /// - customCertificateVerificationCallback: If specified, this callback *overrides* the default NIOSSL client
115+ /// certificate verification logic. The callback receives the certificates presented by the peer. Within the
116+ /// callback, you must validate these certificates against your trust roots and derive a validated chain of
117+ /// trust per [RFC 4158](https://datatracker.ietf.org/doc/html/rfc4158). Return
118+ /// ``CertificateVerificationResult/certificateVerified(_:)`` from the callback if verification succeeds,
119+ /// optionally including the validated certificate chain you derived. Returning the validated certificate
120+ /// chain allows ``NIOHTTPServer`` to provide access to it in the request handler through
121+ /// ``NIOHTTPServer/ConnectionContext/peerCertificateChain``, accessed via the task-local
122+ /// ``NIOHTTPServer/connectionContext`` property. Otherwise, return
123+ /// ``CertificateVerificationResult/failed(_:)`` if verification fails.
124+ ///
125+ /// - Warning: If `customCertificateVerificationCallback` is set, it will **override** NIOSSL's default
126+ /// certificate verification logic.
118127 public static func mTLS(
119128 certificateChain: [ Certificate ] ,
120129 privateKey: Certificate . PrivateKey ,
121130 trustRoots: [ Certificate ] ? ,
122- certificateVerification: CertificateVerification = . noHostnameVerification,
123- customCertificateVerificationCallback: CustomCertificateVerificationCallback ? = nil
131+ certificateVerification: CertificateVerificationMode = . noHostnameVerification,
132+ customCertificateVerificationCallback: (
133+ @Sendable ( [ X509 . Certificate] ) async throws -> CertificateVerificationResult
134+ ) ? = nil
124135 ) -> Self {
125136 Self (
126137 backing: . mTLS(
@@ -132,22 +143,27 @@ public struct NIOHTTPServerConfiguration: Sendable {
132143 )
133144 }
134145
135- /// Enables mTLS with certificate reloading. Optionally provide a custom verification callback to override the default verification logic
136- /// used to verify client certificates, and control the derivation of a validated chain of trust from the
137- /// certificates presented by the peer.
146+ /// Enables mTLS with certificate reloading.
138147 ///
139148 /// - Parameters:
140149 /// - certificateReloader: The certificate reloader instance.
141150 /// - trustRoots: The root certificates to trust when verifying client certificates.
142151 /// - certificateVerification: Configures the client certificate validation behaviour. Defaults to
143- // ``CertificateVerification/noHostnameVerification``.
144- /// - customCertificateVerificationCallback: A custom certificate verification callback. This will override
145- /// NIOSSL's default certificate verification logic.
152+ /// ``CertificateVerification/noHostnameVerification``.
153+ /// - customCertificateVerificationCallback: If specified, this callback *overrides* the default NIOSSL client
154+ /// certificate verification logic. Refer to the documentation for this argument in
155+ /// ``mTLS(certificateChain:privateKey:trustRoots:certificateVerification:customCertificateVerificationCallback:)``
156+ /// for more details.
157+ ///
158+ /// - Warning: If `customCertificateVerificationCallback` is set, it will **override** NIOSSL's default
159+ /// certificate verification logic.
146160 public static func mTLS(
147161 certificateReloader: any CertificateReloader ,
148162 trustRoots: [ Certificate ] ? ,
149- certificateVerification: CertificateVerification = . noHostnameVerification,
150- customCertificateVerificationCallback: CustomCertificateVerificationCallback ? = nil
163+ certificateVerification: CertificateVerificationMode = . noHostnameVerification,
164+ customCertificateVerificationCallback: (
165+ @Sendable ( [ X509 . Certificate] ) async throws -> CertificateVerificationResult
166+ ) ? = nil
151167 ) throws -> Self {
152168 Self (
153169 backing: . reloadingMTLS(
@@ -247,95 +263,78 @@ public struct NIOHTTPServerConfiguration: Sendable {
247263 }
248264}
249265
266+ /// Represents the outcome of certificate verification.
267+ ///
268+ /// Indicates whether certificate verification succeeded or failed, and provides associated metadata when verification
269+ /// is successful.
250270@available ( macOS 26 . 0 , iOS 26 . 0 , watchOS 26 . 0 , tvOS 26 . 0 , visionOS 26 . 0 , * )
251- extension NIOHTTPServerConfiguration . TransportSecurity {
252- /// Represents the outcome of certificate verification.
253- ///
254- /// Indicates whether certificate verification succeeded or failed, and provides associated metadata when
255- /// verification is successful.
256- public enum VerificationResult : Sendable , Hashable {
257- /// Metadata resulting from successful certificate verification.
258- public struct VerificationMetadata : Sendable , Hashable {
259- /// A container for the validated certificate chain: an array of certificates forming a verified and ordered
260- /// chain of trust, starting from the peer's leaf certificate to a trusted root certificate.
261- public var validatedCertificateChain : X509 . ValidatedCertificateChain ?
262-
263- /// Creates an instance with the peer's *validated* certificate chain.
264- ///
265- /// - Parameter validatedCertificateChain: An optional *validated* certificate chain. If provided, it must
266- /// **only** contain the **validated** chain of trust that was built and verified from the certificates
267- /// presented by the peer.
268- public init ( _ validatedCertificateChain: X509 . ValidatedCertificateChain ? ) {
269- self . validatedCertificateChain = validatedCertificateChain
270- }
271+ public enum CertificateVerificationResult : Sendable , Hashable {
272+ /// Metadata resulting from successful certificate verification.
273+ public struct VerificationMetadata : Sendable , Hashable {
274+ /// A container for the validated certificate chain: an array of certificates forming a verified and ordered
275+ /// chain of trust, starting from the peer's leaf certificate to a trusted root certificate.
276+ public var validatedCertificateChain : X509 . ValidatedCertificateChain ?
277+
278+ /// Creates an instance with the peer's *validated* certificate chain.
279+ ///
280+ /// - Parameter validatedCertificateChain: An optional *validated* certificate chain. If provided, it must
281+ /// **only** contain the **validated** chain of trust that was built and verified from the certificates
282+ /// presented by the peer.
283+ public init ( _ validatedCertificateChain: X509 . ValidatedCertificateChain ? ) {
284+ self . validatedCertificateChain = validatedCertificateChain
271285 }
286+ }
272287
273- /// An error representing certificate verification failure.
274- public struct VerificationError : Swift . Error , Hashable {
275- public let reason : String
288+ /// An error representing certificate verification failure.
289+ public struct VerificationError : Swift . Error , Hashable {
290+ public let reason : String
276291
277- /// Creates a verification error with the reason why verification failed.
278- /// - Parameter reason: The reason of why certificate verification failed.
279- public init ( reason: String ) {
280- self . reason = reason
281- }
292+ /// Creates a verification error with the reason why verification failed.
293+ /// - Parameter reason: The reason of why certificate verification failed.
294+ public init ( reason: String ) {
295+ self . reason = reason
282296 }
283-
284- /// Certificate verification succeeded.
285- ///
286- /// The associated metadata contains information captured during verification.
287- case certificateVerified( VerificationMetadata )
288-
289- /// Certificate verification failed.
290- case failed( VerificationError )
291297 }
292298
293- /// A callback for implementing custom certificate verification logic.
294- ///
295- /// Use this callback to perform custom certificate verification. The callback receives the certificates presented
296- /// by the peer as `[X509.Certificate]`. Within the callback, you must validate these certificates against your
297- /// trust roots and derive a validated chain of trust per [RFC 4158](https://datatracker.ietf.org/doc/html/rfc4158).
299+ /// Certificate verification succeeded.
298300 ///
299- /// Return ``VerificationResult/certificateVerified(_:)`` if verification succeeds, optionally including
300- /// the validated certificate chain you derived. Returning the validated certificate chain allows ``NIOHTTPServer``
301- /// to provide access to it in the request handler through ``NIOHTTPServer/ConnectionContext/peerCertificateChain``,
302- /// accessed via the task-local ``NIOHTTPServer/connectionContext`` property. Otherwise, return
303- /// ``VerificationResult/failed(_:)`` if verification fails.
304- public typealias CustomCertificateVerificationCallback = @Sendable ( [ Certificate] ) async throws -> VerificationResult
301+ /// The associated metadata contains information captured during verification.
302+ case certificateVerified( VerificationMetadata )
303+
304+ /// Certificate verification failed.
305+ case failed( VerificationError )
305306}
306307
307- @available ( macOS 26 . 0 , iOS 26 . 0 , watchOS 26 . 0 , tvOS 26 . 0 , visionOS 26 . 0 , * )
308- extension NIOHTTPServerConfiguration . TransportSecurity {
309- /// Represents the certificate verification behaviour.
310- public struct CertificateVerification : Sendable {
311- enum VerificationMode {
312- case optionalVerification
313- case noHostnameVerification
314- }
308+ /// Represents the certificate verification behaviour.
309+ public struct CertificateVerificationMode : Sendable {
310+ enum VerificationMode {
311+ case optionalVerification
312+ case noHostnameVerification
313+ }
315314
316- let mode : VerificationMode
315+ let mode : VerificationMode
317316
318- /// Allows peers to connect without presenting any certificates. However, if the peer *does* present
319- /// certificates, they are validated like normal (exactly like with ``noHostnameVerification``), and the TLS
320- /// handshake will fail if verification fails.
321- ///
322- /// - Warning: With this mode, a peer can successfully connect even without presenting any certificates. As such,
323- /// this mode must be used with great caution.
324- public static var optionalVerification : Self {
325- Self ( mode: . optionalVerification)
326- }
317+ /// Allows peers to connect without presenting any certificates. However, if the peer *does* present
318+ /// certificates, they are validated like normal (exactly like with ``noHostnameVerification``), and the TLS
319+ /// handshake will fail if verification fails.
320+ ///
321+ /// - Warning: With this mode, a peer can successfully connect even without presenting any certificates. As such,
322+ /// this mode must be used with great caution.
323+ public static var optionalVerification : Self {
324+ Self ( mode: . optionalVerification)
325+ }
327326
328- /// Validates the certificates presented by the peer but skips hostname verification as it cannot succeed in
329- /// a server context.
330- public static var noHostnameVerification : Self {
331- Self ( mode: . noHostnameVerification)
332- }
327+ /// Validates the certificates presented by the peer but skips hostname verification as it cannot succeed in
328+ /// a server context.
329+ public static var noHostnameVerification : Self {
330+ Self ( mode: . noHostnameVerification)
333331 }
334332}
335333
336334@available ( macOS 26 . 0 , iOS 26 . 0 , watchOS 26 . 0 , tvOS 26 . 0 , visionOS 26 . 0 , * )
337335extension NIOSSL . CertificateVerification {
338- init ( _ verificationMode: NIOHTTPServerConfiguration . TransportSecurity . CertificateVerification ) {
336+ /// Maps ``CertificateVerificationMode`` to the NIOSSL representation.
337+ init ( _ verificationMode: CertificateVerificationMode ) {
339338 switch verificationMode. mode {
340339 case . noHostnameVerification:
341340 self = . noHostnameVerification
0 commit comments