99using LettuceEncrypt . Accounts ;
1010using LettuceEncrypt . Acme ;
1111using LettuceEncrypt . Internal . PfxBuilder ;
12+ using Microsoft . AspNetCore . Hosting . Server ;
1213using Microsoft . Extensions . Hosting ;
1314using Microsoft . Extensions . Logging ;
1415using Microsoft . Extensions . Options ;
@@ -28,6 +29,7 @@ internal class AcmeCertificateFactory
2829 private readonly IDnsChallengeProvider _dnsChallengeProvider ;
2930 private readonly ICertificateAuthorityConfiguration _certificateAuthority ;
3031 private readonly IPfxBuilderFactory _pfxBuilderFactory ;
32+ private readonly IServer _server ;
3133 private readonly TaskCompletionSource < object ? > _appStarted = new ( ) ;
3234 private AcmeClient ? _client ;
3335 private IKey ? _acmeAccountKey ;
@@ -43,6 +45,7 @@ public AcmeCertificateFactory(
4345 ICertificateAuthorityConfiguration certificateAuthority ,
4446 IDnsChallengeProvider dnsChallengeProvider ,
4547 IPfxBuilderFactory pfxBuilderFactory ,
48+ IServer server ,
4649 IAccountStore ? accountRepository = null )
4750 {
4851 _acmeClientFactory = acmeClientFactory ;
@@ -55,6 +58,7 @@ public AcmeCertificateFactory(
5558 _dnsChallengeProvider = dnsChallengeProvider ;
5659 _certificateAuthority = certificateAuthority ;
5760 _pfxBuilderFactory = pfxBuilderFactory ;
61+ _server = server ;
5862
5963 appLifetime . ApplicationStarted . Register ( ( ) => _appStarted . TrySetResult ( null ) ) ;
6064 if ( appLifetime . ApplicationStarted . IsCancellationRequested )
@@ -236,23 +240,27 @@ private async Task ValidateDomainOwnershipAsync(IAuthorizationContext authorizat
236240 cancellationToken . ThrowIfCancellationRequested ( ) ;
237241
238242 var validators = new List < DomainOwnershipValidator > ( ) ;
243+ var validationTimeout = _options . Value . ValidationTimeout ;
244+ var validationPollInterval = _options . Value . ValidationPollInterval ;
245+ var enableSelfTest = _options . Value . EnableChallengeSelfTest ;
246+ var selfTestBaseUrl = _options . Value . ChallengeSelfTestBaseUrl ;
239247
240248 if ( _tlsAlpnChallengeResponder . IsEnabled )
241249 {
242250 validators . Add ( new TlsAlpn01DomainValidator (
243- _tlsAlpnChallengeResponder , _appLifetime , _client , _logger , domainName ) ) ;
251+ _tlsAlpnChallengeResponder , _appLifetime , _client , _logger , domainName , validationTimeout , validationPollInterval ) ) ;
244252 }
245253
246254 if ( _options . Value . AllowedChallengeTypes . HasFlag ( ChallengeType . Http01 ) )
247255 {
248256 validators . Add ( new Http01DomainValidator (
249- _challengeStore , _appLifetime , _client , _logger , domainName ) ) ;
257+ _challengeStore , _appLifetime , _client , _logger , domainName , validationTimeout , validationPollInterval , enableSelfTest , selfTestBaseUrl , _server ) ) ;
250258 }
251259
252260 if ( _options . Value . AllowedChallengeTypes . HasFlag ( ChallengeType . Dns01 ) )
253261 {
254262 validators . Add ( new Dns01DomainValidator (
255- _dnsChallengeProvider , _appLifetime , _client , _logger , domainName ) ) ;
263+ _dnsChallengeProvider , _appLifetime , _client , _logger , domainName , validationTimeout , validationPollInterval ) ) ;
256264 }
257265
258266 if ( validators . Count == 0 )
@@ -263,23 +271,67 @@ private async Task ValidateDomainOwnershipAsync(IAuthorizationContext authorizat
263271 "Ensure at least one kind of these challenge types is configured: " + challengeTypes ) ;
264272 }
265273
274+ _logger . LogInformation (
275+ "Attempting domain validation for '{DomainName}' using {ValidatorCount} challenge method(s): {Validators}" ,
276+ domainName ,
277+ validators . Count ,
278+ string . Join ( ", " , validators . Select ( v => v . GetType ( ) . Name . Replace ( "DomainValidator" , "" ) ) ) ) ;
279+
280+ var failures = new List < Exception > ( ) ;
281+
266282 foreach ( var validator in validators )
267283 {
268284 cancellationToken . ThrowIfCancellationRequested ( ) ;
285+ var validatorName = validator . GetType ( ) . Name . Replace ( "DomainValidator" , "" ) ;
286+
269287 try
270288 {
289+ _logger . LogDebug ( "Trying {ValidatorName} validation for domain '{DomainName}'" ,
290+ validatorName , domainName ) ;
291+
271292 await validator . ValidateOwnershipAsync ( authorizationContext , cancellationToken ) ;
293+
272294 // The method above raises if validation fails. If no exception occurs, we assume validation completed successfully.
295+ _logger . LogInformation (
296+ "Domain validation succeeded using {ValidatorName} for '{DomainName}'" ,
297+ validatorName , domainName ) ;
273298 return ;
274299 }
275300 catch ( Exception ex )
276301 {
277- _logger . LogDebug ( ex , "Validation with {validatorType} failed with error: {error}" ,
278- validator . GetType ( ) . Name , ex . Message ) ;
302+ // Rethrow cancellation exceptions immediately to preserve cooperative cancellation semantics
303+ if ( ex is OperationCanceledException || ex is TaskCanceledException )
304+ {
305+ throw ;
306+ }
307+
308+ failures . Add ( ex ) ;
309+ _logger . LogWarning ( ex ,
310+ "Validation with {ValidatorName} failed for domain '{DomainName}'. " +
311+ "Error: {ErrorMessage}. " +
312+ "{RemainingValidators} validation method(s) remaining." ,
313+ validatorName ,
314+ domainName ,
315+ ex . Message ,
316+ validators . Count - failures . Count ) ;
279317 }
280318 }
281319
282- throw new InvalidOperationException ( $ "Failed to validate ownership of domainName '{ domainName } '") ;
320+ // All validators failed
321+ var failureDetails = string . Join ( "; " , failures . Select ( ( ex , i ) =>
322+ $ "{ validators [ i ] . GetType ( ) . Name . Replace ( "DomainValidator" , "" ) } : { ex . Message } ") ) ;
323+
324+ _logger . LogError (
325+ "All {ValidatorCount} validation methods failed for domain '{DomainName}'. Failures: {FailureDetails}" ,
326+ validators . Count ,
327+ domainName ,
328+ failureDetails ) ;
329+
330+ throw new AggregateException (
331+ $ "Failed to validate ownership of domainName '{ domainName } ' using any available challenge method. " +
332+ $ "Attempted: { string . Join ( ", " , validators . Select ( v => v . GetType ( ) . Name . Replace ( "DomainValidator" , "" ) ) ) } . " +
333+ $ "See inner exceptions for details.",
334+ failures ) ;
283335 }
284336
285337 private async Task < X509Certificate2 > CompleteCertificateRequestAsync ( IOrderContext order ,
0 commit comments