diff --git a/identity-server/src/IdentityServer/Extensions/HttpContextExtensions.cs b/identity-server/src/IdentityServer/Extensions/HttpContextExtensions.cs index 3c6df4833..60724792a 100644 --- a/identity-server/src/IdentityServer/Extensions/HttpContextExtensions.cs +++ b/identity-server/src/IdentityServer/Extensions/HttpContextExtensions.cs @@ -71,7 +71,7 @@ public static class HttpContextExtensions } var samlEntityIds = samlSessions.Select(s => s.EntityId); - if (await AnyClientHasFrontChannelLogout(logoutMessage.ClientIds) || await AnySamlServiceProviderHasFrontChannelLogout(samlEntityIds)) + if (await AnyClientHasFrontChannelLogout(logoutMessage.ClientIds) || await AnySamlServiceProviderHasFrontChannelLogout(samlEntityIds, context.RequestAborted)) { endSessionMsg = new LogoutNotificationContext { @@ -90,7 +90,7 @@ public static class HttpContextExtensions var samlEntityIds = samlSessions.Select(s => s.EntityId); if ((clientIds.Any() && await AnyClientHasFrontChannelLogout(clientIds)) || - (samlEntityIds.Any() && await AnySamlServiceProviderHasFrontChannelLogout(samlEntityIds))) + (samlEntityIds.Any() && await AnySamlServiceProviderHasFrontChannelLogout(samlEntityIds, context.RequestAborted))) { endSessionMsg = new LogoutNotificationContext { @@ -135,12 +135,12 @@ public static class HttpContextExtensions return false; } - async Task AnySamlServiceProviderHasFrontChannelLogout(IEnumerable entityIds) + async Task AnySamlServiceProviderHasFrontChannelLogout(IEnumerable entityIds, Ct ct) { var serviceProviderStore = context.RequestServices.GetRequiredService(); foreach (var entityId in entityIds) { - var sp = await serviceProviderStore.FindByEntityIdAsync(entityId); + var sp = await serviceProviderStore.FindByEntityIdAsync(entityId, ct); if (sp?.Enabled == true && sp.SingleLogoutServiceUrl != null) { return true; diff --git a/identity-server/src/IdentityServer/Internal/Saml/DefaultSamlInteractionService.cs b/identity-server/src/IdentityServer/Internal/Saml/DefaultSamlInteractionService.cs index c36fbfeca..0a2b1fe35 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/DefaultSamlInteractionService.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/DefaultSamlInteractionService.cs @@ -17,7 +17,7 @@ internal class DefaultSamlInteractionService( ILogger logger) : ISamlInteractionService { - public async Task GetAuthenticationRequestContextAsync(CT ct = default) + public async Task GetAuthenticationRequestContextAsync(Ct ct = default) { using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultSamlInteractionService.GetAuthenticationRequestContext"); @@ -34,7 +34,7 @@ internal class DefaultSamlInteractionService( return null; } - var sp = await serviceProviderStore.FindByEntityIdAsync(state.ServiceProviderEntityId); + var sp = await serviceProviderStore.FindByEntityIdAsync(state.ServiceProviderEntityId, ct); if (sp == null) { logger.ServiceProviderNotFound(LogLevel.Warning, state.ServiceProviderEntityId); @@ -52,7 +52,7 @@ internal class DefaultSamlInteractionService( }; } - public async Task StoreRequestedAuthnContextResultAsync(bool requestedAuthnContextRequirementsWereMet, CT ct = default) + public async Task StoreRequestedAuthnContextResultAsync(bool requestedAuthnContextRequirementsWereMet, Ct ct = default) { using var activity = Tracing.ServiceActivitySource.StartActivity("DefaultSamlInteractionService.StoreRequestedAuthnContextResult"); diff --git a/identity-server/src/IdentityServer/Internal/Saml/EmptySamlServiceProviderStore.cs b/identity-server/src/IdentityServer/Internal/Saml/EmptySamlServiceProviderStore.cs index eda5b1d3b..989159e2d 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/EmptySamlServiceProviderStore.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/EmptySamlServiceProviderStore.cs @@ -8,5 +8,5 @@ namespace Duende.IdentityServer.Internal.Saml; internal class EmptySamlServiceProviderStore : ISamlServiceProviderStore { - public Task FindByEntityIdAsync(string entityId) => Task.FromResult(null); + public Task FindByEntityIdAsync(string entityId, Ct ct) => Task.FromResult(null); } diff --git a/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/ISamlSigningService.cs b/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/ISamlSigningService.cs index 6c6359326..f0b803fae 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/ISamlSigningService.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/ISamlSigningService.cs @@ -13,19 +13,21 @@ internal interface ISamlSigningService /// /// Gets the X509 certificate used for signing SAML messages. /// + /// The cancellation token. /// The signing certificate with private key. /// /// Thrown when no signing credential is available, when the credential is not an X509 certificate, /// or when the certificate does not have a private key. /// - Task GetSigningCertificateAsync(); + Task GetSigningCertificateAsync(Ct ct); /// /// Gets the X509 certificate as a base64-encoded string for inclusion in SAML metadata. /// + /// The cancellation token. /// Base64-encoded certificate bytes. /// /// Thrown when no signing credential is available or when the credential is not an X509 certificate. /// - Task GetSigningCertificateBase64Async(); + Task GetSigningCertificateBase64Async(Ct ct); } diff --git a/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlProtocolMessageSigner.cs b/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlProtocolMessageSigner.cs index d7ed97c90..b5ed433db 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlProtocolMessageSigner.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlProtocolMessageSigner.cs @@ -14,12 +14,12 @@ internal class SamlProtocolMessageSigner( ISamlSigningService samlSigningService, ILogger logger) { - internal async Task SignProtocolMessage(XElement messageElement, SamlServiceProvider serviceProvider) + internal async Task SignProtocolMessage(XElement messageElement, SamlServiceProvider serviceProvider, Ct ct) { ArgumentNullException.ThrowIfNull(messageElement); ArgumentNullException.ThrowIfNull(serviceProvider); - var certificate = await samlSigningService.GetSigningCertificateAsync(); + var certificate = await samlSigningService.GetSigningCertificateAsync(ct); logger.SigningSamlProtocolMessage(LogLevel.Debug, serviceProvider.EntityId, messageElement.Name.LocalName); @@ -38,9 +38,9 @@ internal class SamlProtocolMessageSigner( } } - internal async Task SignQueryString(string queryString) + internal async Task SignQueryString(string queryString, Ct ct) { - var certificate = await samlSigningService.GetSigningCertificateAsync(); + var certificate = await samlSigningService.GetSigningCertificateAsync(ct); using var rsa = certificate.GetRSAPrivateKey(); if (rsa == null) { diff --git a/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlRequestProcessorBase.cs b/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlRequestProcessorBase.cs index a4a1d0567..8c3505174 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlRequestProcessorBase.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlRequestProcessorBase.cs @@ -28,9 +28,9 @@ internal abstract class SamlRequestProcessorBase( protected readonly ILogger Logger = logger; protected readonly string ExpectedDestination = expectedDestination; - internal async Task>> ProcessAsync(TRequest request, CT ct = default) + internal async Task>> ProcessAsync(TRequest request, Ct ct = default) { - var sp = await ServiceProviderStore.FindByEntityIdAsync(request.Request.Issuer); + var sp = await ServiceProviderStore.FindByEntityIdAsync(request.Request.Issuer, ct); if (sp?.Enabled != true) { Logger.ServiceProviderNotFound(LogLevel.Warning, request.Request.Issuer); @@ -140,5 +140,5 @@ internal abstract class SamlRequestProcessorBase( return null; } protected abstract SamlRequestError? ValidateMessageSpecific(SamlServiceProvider sp, TRequest request); - protected abstract Task>> ProcessValidatedRequestAsync(SamlServiceProvider sp, TRequest request, CT ct = default); + protected abstract Task>> ProcessValidatedRequestAsync(SamlServiceProvider sp, TRequest request, Ct ct = default); } diff --git a/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlResponseSigner.cs b/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlResponseSigner.cs index 6029f1e24..cea97b2f7 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlResponseSigner.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlResponseSigner.cs @@ -14,7 +14,7 @@ internal class SamlResponseSigner( IOptions samlOptions, ILogger logger) { - internal async Task SignResponse(XElement responseElement, SamlServiceProvider serviceProvider) + internal async Task SignResponse(XElement responseElement, SamlServiceProvider serviceProvider, Ct ct) { var signingBehavior = serviceProvider.SigningBehavior ?? samlOptions.Value.DefaultSigningBehavior; @@ -24,7 +24,7 @@ internal class SamlResponseSigner( return responseElement.ToString(SaveOptions.DisableFormatting); } - var certificate = await samlSigningService.GetSigningCertificateAsync(); + var certificate = await samlSigningService.GetSigningCertificateAsync(ct); logger.SigningSamlResponse(LogLevel.Debug, serviceProvider.EntityId, signingBehavior); diff --git a/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlSigningService.cs b/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlSigningService.cs index ab184eede..578c7edb9 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlSigningService.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/Infrastructure/SamlSigningService.cs @@ -18,9 +18,9 @@ internal class SamlSigningService( ILogger logger) : ISamlSigningService { /// - public async Task GetSigningCertificateAsync() + public async Task GetSigningCertificateAsync(Ct ct) { - var credential = await GetSigningCredentialsAsync(); + var credential = await GetSigningCredentialsAsync(ct); if (!TryExtractCertificateFromCredential(credential, out var certificate)) { throw new InvalidOperationException( @@ -37,9 +37,9 @@ internal class SamlSigningService( } /// - public async Task GetSigningCertificateBase64Async() + public async Task GetSigningCertificateBase64Async(Ct ct) { - var credential = await GetSigningCredentialsAsync(); + var credential = await GetSigningCredentialsAsync(ct); if (TryExtractCertificateFromCredential(credential, out var certificate)) { var certBytes = certificate.Export(X509ContentType.Cert); @@ -50,9 +50,9 @@ internal class SamlSigningService( "Signing credential key is not an X509SecurityKey and cannot be used to extract an X509 certificate for SAML metadata."); } - private async Task GetSigningCredentialsAsync() + private async Task GetSigningCredentialsAsync(Ct ct) { - var credential = await keyMaterialService.GetSigningCredentialsAsync(); + var credential = await keyMaterialService.GetSigningCredentialsAsync(null, ct); return credential ?? throw new InvalidOperationException("No signing credential available. Configure a signing certificate."); } diff --git a/identity-server/src/IdentityServer/Internal/Saml/Metadata/SamlMetaDataEndpoint.cs b/identity-server/src/IdentityServer/Internal/Saml/Metadata/SamlMetaDataEndpoint.cs index 97efdbc65..2eb747acb 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/Metadata/SamlMetaDataEndpoint.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/Metadata/SamlMetaDataEndpoint.cs @@ -32,10 +32,10 @@ internal class SamlMetaDataEndpoint( } var options = samlOptions.Value; - var issuerUri = await issuerNameService.GetCurrentAsync(); + var issuerUri = await issuerNameService.GetCurrentAsync(context.RequestAborted); var baseUrl = urls.BaseUrl; - var certificateBase64 = await samlSigningService.GetSigningCertificateBase64Async(); + var certificateBase64 = await samlSigningService.GetSigningCertificateBase64Async(context.RequestAborted); var singleSignOnService = BuildServiceUrl(baseUrl, options.UserInteraction.Route, options.UserInteraction.SignInPath); var singleLogoutService = BuildServiceUrl(baseUrl, options.UserInteraction.Route, options.UserInteraction.SingleLogoutPath); diff --git a/identity-server/src/IdentityServer/Internal/Saml/NopSamlLogoutNotificationService.cs b/identity-server/src/IdentityServer/Internal/Saml/NopSamlLogoutNotificationService.cs index a4991e603..97a2fb983 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/NopSamlLogoutNotificationService.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/NopSamlLogoutNotificationService.cs @@ -10,6 +10,6 @@ namespace Duende.IdentityServer.Internal.Saml; internal class NopSamlLogoutNotificationService : ISamlLogoutNotificationService { - public Task> GetSamlFrontChannelLogoutsAsync(LogoutNotificationContext context) => + public Task> GetSamlFrontChannelLogoutsAsync(LogoutNotificationContext context, Ct ct) => Task.FromResult(Enumerable.Empty()); } diff --git a/identity-server/src/IdentityServer/Internal/Saml/SamlClaimService.cs b/identity-server/src/IdentityServer/Internal/Saml/SamlClaimService.cs index 3d86cdaad..1fcd05b71 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SamlClaimService.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SamlClaimService.cs @@ -19,7 +19,7 @@ internal class SamlClaimsService( IOptions options, ISamlClaimsMapper? customMapper = null) { - private async Task> GetClaimsAsync(ClaimsPrincipal user, SamlServiceProvider serviceProvider) + private async Task> GetClaimsAsync(ClaimsPrincipal user, SamlServiceProvider serviceProvider, Ct ct) { ArgumentNullException.ThrowIfNull(user); ArgumentNullException.ThrowIfNull(serviceProvider); @@ -32,13 +32,13 @@ internal class SamlClaimsService( Subject = user, Client = new Client { - ClientId = serviceProvider.EntityId.ToString() + ClientId = serviceProvider.EntityId }, RequestedClaimTypes = requestedClaimTypes, Caller = "SAML" }; - await profileService.GetProfileDataAsync(context); + await profileService.GetProfileDataAsync(context, ct); var claims = context.IssuedClaims; @@ -49,12 +49,13 @@ internal class SamlClaimsService( internal async Task> GetMappedAttributesAsync( ClaimsPrincipal user, - SamlServiceProvider serviceProvider) + SamlServiceProvider serviceProvider, + Ct ct) { ArgumentNullException.ThrowIfNull(user); ArgumentNullException.ThrowIfNull(serviceProvider); - var claims = await GetClaimsAsync(user, serviceProvider); + var claims = await GetClaimsAsync(user, serviceProvider, ct); if (customMapper != null) { diff --git a/identity-server/src/IdentityServer/Internal/Saml/SamlResponseBuilder.cs b/identity-server/src/IdentityServer/Internal/Saml/SamlResponseBuilder.cs index 24d800cc7..f2a1d2c18 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SamlResponseBuilder.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SamlResponseBuilder.cs @@ -133,16 +133,17 @@ internal class SamlResponseBuilder( ClaimsPrincipal user, SamlServiceProvider samlServiceProvider, SamlAuthenticationState samlAuthenticationState, - string sessionIndex) + string sessionIndex, + Ct ct) { var now = timeProvider.GetUtcNow().DateTime; var options = samlOptions.Value; var nameId = nameIdGenerator.GenerateNameIdentifier(user, samlServiceProvider, samlAuthenticationState.Request); - var attributes = await samlClaimsService.GetMappedAttributesAsync(user, samlServiceProvider); + var attributes = await samlClaimsService.GetMappedAttributesAsync(user, samlServiceProvider, ct); var acsUrl = GetAcsUrl(samlAuthenticationState.Request, samlServiceProvider); - var issuer = await issuerNameService.GetCurrentAsync(); + var issuer = await issuerNameService.GetCurrentAsync(ct); return new SamlResponse { diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/LogoutResponseBuilder.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/LogoutResponseBuilder.cs index 6006e5d6c..3a3fa7349 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/LogoutResponseBuilder.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/LogoutResponseBuilder.cs @@ -17,9 +17,10 @@ internal class LogoutResponseBuilder( internal async Task BuildSuccessResponseAsync( string logoutRequestId, SamlServiceProvider serviceProvider, - string? relayState) + string? relayState, + Ct ct) { - var issuer = await issuerNameService.GetCurrentAsync(); + var issuer = await issuerNameService.GetCurrentAsync(ct); var destination = serviceProvider.SingleLogoutServiceUrl ?? throw new InvalidOperationException("No SingleLogout service url configured"); return new LogoutResponse @@ -41,9 +42,10 @@ internal class LogoutResponseBuilder( internal async Task BuildErrorResponseAsync( SamlLogoutRequest request, SamlServiceProvider serviceProvider, - SamlError error) + SamlError error, + Ct ct) { - var issuer = await issuerNameService.GetCurrentAsync(); + var issuer = await issuerNameService.GetCurrentAsync(ct); var destination = serviceProvider.SingleLogoutServiceUrl ?? throw new InvalidOperationException("No SingleLogout service url configured"); return new LogoutResponse diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/Models/LogoutResponse.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/Models/LogoutResponse.cs index c7c3e3e94..acc1d8aaa 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/Models/LogoutResponse.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/Models/LogoutResponse.cs @@ -76,7 +76,7 @@ internal class LogoutResponse : EndpointResult { var responseXml = serializer.Serialize(result); - var signedResponseXml = await samlProtocolMessageSigner.SignProtocolMessage(responseXml, result.ServiceProvider); + var signedResponseXml = await samlProtocolMessageSigner.SignProtocolMessage(responseXml, result.ServiceProvider, httpContext.RequestAborted); var encodedResponse = Convert.ToBase64String(Encoding.UTF8.GetBytes(signedResponseXml)); diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlFrontChannelLogoutRequestBuilder.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlFrontChannelLogoutRequestBuilder.cs index 4bbd8f58a..f42774efc 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlFrontChannelLogoutRequestBuilder.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlFrontChannelLogoutRequestBuilder.cs @@ -24,7 +24,8 @@ internal class SamlFrontChannelLogoutRequestBuilder( string nameId, string? nameIdFormat, string sessionIndex, - string issuer) + string issuer, + Ct ct) { ArgumentNullException.ThrowIfNull(serviceProvider); @@ -48,8 +49,8 @@ internal class SamlFrontChannelLogoutRequestBuilder( return serviceProvider.SingleLogoutServiceUrl.Binding switch { - SamlBinding.HttpRedirect => await BuildRedirectLogoutRequest(serviceProvider.SingleLogoutServiceUrl.Location, requestXml), - SamlBinding.HttpPost => await BuildHttpPostLogoutRequest(serviceProvider, requestXml), + SamlBinding.HttpRedirect => await BuildRedirectLogoutRequest(serviceProvider.SingleLogoutServiceUrl.Location, requestXml, ct), + SamlBinding.HttpPost => await BuildHttpPostLogoutRequest(serviceProvider, requestXml, ct), _ => throw new InvalidOperationException( $"Binding '{serviceProvider.SingleLogoutServiceUrl.Binding}' is not supported") }; @@ -106,13 +107,13 @@ internal class SamlFrontChannelLogoutRequestBuilder( return requestElement; } - private async Task BuildRedirectLogoutRequest(Uri singleLogoutServiceUri, XElement requestXml) + private async Task BuildRedirectLogoutRequest(Uri singleLogoutServiceUri, XElement requestXml, Ct ct) { var encodedRequest = DeflateAndEncode(requestXml.ToString()); var queryString = $"?SAMLRequest={Uri.EscapeDataString(encodedRequest)}"; - var signedQueryString = await samlProtocolMessageSigner.SignQueryString(queryString); + var signedQueryString = await samlProtocolMessageSigner.SignQueryString(queryString, ct); return new SamlHttpRedirectFrontChannelLogout(singleLogoutServiceUri, signedQueryString); } @@ -130,9 +131,9 @@ internal class SamlFrontChannelLogoutRequestBuilder( return Convert.ToBase64String(output.ToArray()); } - private async Task BuildHttpPostLogoutRequest(SamlServiceProvider serviceProvider, XElement requestXml) + private async Task BuildHttpPostLogoutRequest(SamlServiceProvider serviceProvider, XElement requestXml, Ct ct) { - var signedRequestXml = await samlProtocolMessageSigner.SignProtocolMessage(requestXml, serviceProvider); + var signedRequestXml = await samlProtocolMessageSigner.SignProtocolMessage(requestXml, serviceProvider, ct); var encodedXml = Convert.ToBase64String(Encoding.UTF8.GetBytes(signedRequestXml)); diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlLogoutCallbackProcessor.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlLogoutCallbackProcessor.cs index 24931cac2..419107550 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlLogoutCallbackProcessor.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlLogoutCallbackProcessor.cs @@ -18,9 +18,9 @@ internal class SamlLogoutCallbackProcessor( LogoutResponseBuilder logoutResponseBuilder, ILogger logger) { - internal async Task> ProcessAsync(string logoutId, CT ct = default) + internal async Task> ProcessAsync(string logoutId, Ct ct = default) { - var logoutMessage = await logoutMessageStore.ReadAsync(logoutId); + var logoutMessage = await logoutMessageStore.ReadAsync(logoutId, ct); if (logoutMessage?.Data == null) { logger.NoLogoutMessageFound(LogLevel.Warning, logoutId); @@ -36,7 +36,7 @@ internal class SamlLogoutCallbackProcessor( logger.BuildingLogoutResponseForSp(LogLevel.Debug, data.SamlServiceProviderEntityId); - var sp = await serviceProviderStore.FindByEntityIdAsync(data.SamlServiceProviderEntityId); + var sp = await serviceProviderStore.FindByEntityIdAsync(data.SamlServiceProviderEntityId, ct); if (sp == null) { logger.ServiceProviderNotFound(LogLevel.Error, data.SamlServiceProviderEntityId); @@ -64,7 +64,8 @@ internal class SamlLogoutCallbackProcessor( var response = await logoutResponseBuilder.BuildSuccessResponseAsync( data.SamlLogoutRequestId, sp, - data.SamlRelayState); + data.SamlRelayState, + ct); logger.SuccessfullyBuiltLogoutResponse(LogLevel.Information, data.SamlServiceProviderEntityId, data.SamlLogoutRequestId); diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlLogoutNotificationService.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlLogoutNotificationService.cs index 1db38b0a1..2e48c1ded 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlLogoutNotificationService.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlLogoutNotificationService.cs @@ -15,7 +15,7 @@ internal class SamlLogoutNotificationService( SamlFrontChannelLogoutRequestBuilder frontChannelLogoutRequestBuilder, ILogger logger) : ISamlLogoutNotificationService { - public async Task> GetSamlFrontChannelLogoutsAsync(LogoutNotificationContext context) + public async Task> GetSamlFrontChannelLogoutsAsync(LogoutNotificationContext context, Ct ct) { using var activity = Tracing.ServiceActivitySource.StartActivity("LogoutNotificationService.GetSamlFrontChannelLogoutUrls"); @@ -27,11 +27,11 @@ internal class SamlLogoutNotificationService( return logoutUrls; } - var issuer = await issuerNameService.GetCurrentAsync(); + var issuer = await issuerNameService.GetCurrentAsync(ct); foreach (var sessionData in context.SamlSessions ?? []) { - var sp = await serviceProviderStore.FindByEntityIdAsync(sessionData.EntityId); + var sp = await serviceProviderStore.FindByEntityIdAsync(sessionData.EntityId, ct); if (sp?.Enabled != true) { logger.SkippingLogoutUrlGenerationForUnknownOrDisabledServiceProvider(LogLevel.Debug, sessionData.EntityId); @@ -51,7 +51,8 @@ internal class SamlLogoutNotificationService( sessionData.NameId, sessionData.NameIdFormat, sessionData.SessionIndex, - issuer); + issuer, + ct); logoutUrls.Add(logoutUrl); } diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlLogoutRequestProcessor.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlLogoutRequestProcessor.cs index 34a9dbcb3..7e585d937 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlLogoutRequestProcessor.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlLogoutRequestProcessor.cs @@ -55,7 +55,7 @@ internal class SamlLogoutRequestProcessor : SamlRequestProcessorBase>> ProcessValidatedRequestAsync( SamlServiceProvider sp, SamlLogoutRequest request, - CT ct = default) + Ct ct = default) { var logoutRequest = request.LogoutRequest; @@ -71,27 +71,27 @@ internal class SamlLogoutRequestProcessor : SamlRequestProcessorBase ValidateSessionIndexAsync(SamlServiceProvider sp, string sessionIndex) + private async Task ValidateSessionIndexAsync(SamlServiceProvider sp, string sessionIndex, Ct ct) { - var samlSessions = await _userSession.GetSamlSessionListAsync(); + var samlSessions = await _userSession.GetSamlSessionListAsync(ct); - var spSession = samlSessions.FirstOrDefault(s => s.EntityId == sp.EntityId.ToString()); + var spSession = samlSessions.FirstOrDefault(s => s.EntityId == sp.EntityId); if (spSession == null) { @@ -150,16 +150,16 @@ internal class SamlLogoutRequestProcessor : SamlRequestProcessorBase StoreLogoutMessageAsync(ClaimsPrincipal user, SamlServiceProvider serviceProvider, SamlLogoutRequest logoutRequest) + private async Task StoreLogoutMessageAsync(ClaimsPrincipal user, SamlServiceProvider serviceProvider, SamlLogoutRequest logoutRequest, Ct ct) { - var samlSessions = await _userSession.GetSamlSessionListAsync(); + var samlSessions = await _userSession.GetSamlSessionListAsync(ct); - var oidcClientIds = await _userSession.GetClientListAsync(); + var oidcClientIds = await _userSession.GetClientListAsync(ct); var logoutMessage = new LogoutMessage { SubjectId = user.GetSubjectId(), - SessionId = await _userSession.GetSessionIdAsync(), + SessionId = await _userSession.GetSessionIdAsync(ct), ClientIds = oidcClientIds, SamlServiceProviderEntityId = serviceProvider.EntityId, SamlSessions = samlSessions, @@ -170,6 +170,6 @@ internal class SamlLogoutRequestProcessor : SamlRequestProcessorBase(logoutMessage, _timeProvider.GetUtcNow().UtcDateTime); - return await _logoutMessageStore.WriteAsync(msg); + return await _logoutMessageStore.WriteAsync(msg, ct); } } diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlSingleLogoutEndpoint.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlSingleLogoutEndpoint.cs index 8b53f2057..9aba25db7 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlSingleLogoutEndpoint.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleLogout/SamlSingleLogoutEndpoint.cs @@ -32,7 +32,7 @@ internal class SamlSingleLogoutEndpoint( return await ProcessLogoutRequest(logoutRequest, context.RequestAborted); } - internal async Task ProcessLogoutRequest(SamlLogoutRequest logoutRequest, CT ct = default) + internal async Task ProcessLogoutRequest(SamlLogoutRequest logoutRequest, Ct ct = default) { logger.ReceivedLogoutRequest(LogLevel.Debug, logoutRequest.LogoutRequest.Issuer, logoutRequest.LogoutRequest.Id, logoutRequest.LogoutRequest.SessionIndex); @@ -44,7 +44,7 @@ internal class SamlSingleLogoutEndpoint( return error.Type switch { SamlRequestErrorType.Validation => HandleValidationError(error), - SamlRequestErrorType.Protocol => await HandleProtocolError(error), + SamlRequestErrorType.Protocol => await HandleProtocolError(error, ct), _ => throw new InvalidOperationException($"Unexpected error type: {error.Type}") }; } @@ -61,7 +61,7 @@ internal class SamlSingleLogoutEndpoint( return new ValidationProblemResult(error.ValidationMessage!); } - private async Task HandleProtocolError(SamlRequestError error) + private async Task HandleProtocolError(SamlRequestError error, Ct ct) { var protocolError = error.ProtocolError!; logger.SamlLogoutProtocolError(LogLevel.Information, @@ -71,6 +71,7 @@ internal class SamlSingleLogoutEndpoint( return await responseBuilder.BuildErrorResponseAsync( protocolError.Request, protocolError.ServiceProvider, - protocolError.Error); + protocolError.Error, + ct); } } diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/DefaultSamlSigninInteractionResponseGenerator.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/DefaultSamlSigninInteractionResponseGenerator.cs index fd2d67939..f338ac15c 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/DefaultSamlSigninInteractionResponseGenerator.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/DefaultSamlSigninInteractionResponseGenerator.cs @@ -18,9 +18,9 @@ internal class DefaultSamlSigninInteractionResponseGenerator( IHttpContextAccessor httpContextAccessor) : ISamlSigninInteractionResponseGenerator { - public async Task ProcessInteractionAsync(SamlServiceProvider sp, AuthNRequest request, CT ct = default) + public async Task ProcessInteractionAsync(SamlServiceProvider sp, AuthNRequest request, Ct ct = default) { - var signedInUser = await userSession.GetUserAsync(); + var signedInUser = await userSession.GetUserAsync(ct); if (signedInUser != null) { diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/DistributedCacheSamlSigninStateStore.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/DistributedCacheSamlSigninStateStore.cs index 80aa0bb91..456acdaeb 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/DistributedCacheSamlSigninStateStore.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/DistributedCacheSamlSigninStateStore.cs @@ -16,7 +16,7 @@ internal class DistributedCacheSamlSigninStateStore(IDistributedCache cache) : I AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10) }; - public async Task StoreSigninRequestStateAsync(SamlAuthenticationState state, CT ct = default) + public async Task StoreSigninRequestStateAsync(SamlAuthenticationState state, Ct ct = default) { var stateId = StateId.NewId(); var key = GetKey(stateId); @@ -27,7 +27,7 @@ internal class DistributedCacheSamlSigninStateStore(IDistributedCache cache) : I return stateId; } - public async Task RetrieveSigninRequestStateAsync(StateId stateId, CT ct = default) + public async Task RetrieveSigninRequestStateAsync(StateId stateId, Ct ct = default) { var key = GetKey(stateId); var json = await cache.GetStringAsync(key, ct); @@ -42,7 +42,7 @@ internal class DistributedCacheSamlSigninStateStore(IDistributedCache cache) : I return JsonSerializer.Deserialize(json); } - public async Task UpdateSigninRequestStateAsync(StateId stateId, SamlAuthenticationState state, CT ct = default) + public async Task UpdateSigninRequestStateAsync(StateId stateId, SamlAuthenticationState state, Ct ct = default) { var key = GetKey(stateId); var json = JsonSerializer.Serialize(state); diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/ISamlSigninStateStore.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/ISamlSigninStateStore.cs index 66af16413..a95a953e0 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/ISamlSigninStateStore.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/ISamlSigninStateStore.cs @@ -8,7 +8,7 @@ namespace Duende.IdentityServer.Internal.Saml.SingleSignin; internal interface ISamlSigninStateStore { - Task StoreSigninRequestStateAsync(SamlAuthenticationState request, CT ct = default); - Task RetrieveSigninRequestStateAsync(StateId stateId, CT ct = default); - Task UpdateSigninRequestStateAsync(StateId stateId, SamlAuthenticationState state, CT ct = default); + Task StoreSigninRequestStateAsync(SamlAuthenticationState request, Ct ct = default); + Task RetrieveSigninRequestStateAsync(StateId stateId, Ct ct = default); + Task UpdateSigninRequestStateAsync(StateId stateId, SamlAuthenticationState state, Ct ct = default); } diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/Models/SamlResponse.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/Models/SamlResponse.cs index cb6bce4f2..ebff6f7e9 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/Models/SamlResponse.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/Models/SamlResponse.cs @@ -78,7 +78,7 @@ internal class SamlResponse : EndpointResult { var responseXml = serializer.Serialize(result); - var signedResponseXml = await samlResponseSigner.SignResponse(responseXml, result.ServiceProvider); + var signedResponseXml = await samlResponseSigner.SignResponse(responseXml, result.ServiceProvider, httpContext.RequestAborted); if (result.ServiceProvider.EncryptAssertions) { diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlIdpInitiatedEndpoint.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlIdpInitiatedEndpoint.cs index db08c2eb7..128cda8dd 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlIdpInitiatedEndpoint.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlIdpInitiatedEndpoint.cs @@ -40,7 +40,7 @@ internal class SamlIdpInitiatedEndpoint( internal async Task ProcessInternalAsync( string spEntityId, string? relayState, - CT ct = default) + Ct ct = default) { logger.StartIdpInitiatedRequest(LogLevel.Debug, spEntityId); diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlIdpInitiatedRequestProcessor.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlIdpInitiatedRequestProcessor.cs index 1aa42822f..db5375382 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlIdpInitiatedRequestProcessor.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlIdpInitiatedRequestProcessor.cs @@ -24,9 +24,9 @@ internal class SamlIdpInitiatedRequestProcessor( internal async Task>> ProcessAsync( string spEntityId, string? relayState, - CT ct = default) + Ct ct = default) { - var sp = await serviceProviderStore.FindByEntityIdAsync(spEntityId); + var sp = await serviceProviderStore.FindByEntityIdAsync(spEntityId, ct); if (sp == null) { return new SamlRequestError diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninCallbackEndpoint.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninCallbackEndpoint.cs index 12c53f0ee..d1fdbdbe1 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninCallbackEndpoint.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninCallbackEndpoint.cs @@ -24,7 +24,7 @@ internal class SamlSigninCallbackEndpoint(SamlResponseBuilder responseBuilder, S return await Process(context.RequestAborted); } - internal async Task Process(CT ct = default) + internal async Task Process(Ct ct = default) { logger.StartSamlSigninCallbackRequest(LogLevel.Debug); diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninCallbackRequestProcessor.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninCallbackRequestProcessor.cs index d99a85b14..ce83e1364 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninCallbackRequestProcessor.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninCallbackRequestProcessor.cs @@ -18,7 +18,7 @@ internal class SamlSigninCallbackRequestProcessor( SamlUrlBuilder samlUrlBuilder, SamlResponseBuilder responseBuilder) { - internal async Task>> ProcessAsync(CT ct = default) + internal async Task>> ProcessAsync(Ct ct = default) { if (!stateIdCookie.TryGetSamlSigninStateId(out var stateId)) { @@ -39,7 +39,7 @@ internal class SamlSigninCallbackRequestProcessor( }; } - var user = await userSession.GetUserAsync(); + var user = await userSession.GetUserAsync(ct); if (user == null || !user.IsAuthenticated()) { var loginUri = samlUrlBuilder.SamlLoginUri(); @@ -48,7 +48,7 @@ internal class SamlSigninCallbackRequestProcessor( } var samlServiceProvider = - await serviceProviderStore.FindByEntityIdAsync(authenticationState.ServiceProviderEntityId); + await serviceProviderStore.FindByEntityIdAsync(authenticationState.ServiceProviderEntityId, ct); if (samlServiceProvider is not { Enabled: true }) { @@ -61,7 +61,7 @@ internal class SamlSigninCallbackRequestProcessor( } // Check if this SP already has a session - if so, reuse the SessionIndex - var existingSessions = await userSession.GetSamlSessionListAsync(); + var existingSessions = await userSession.GetSamlSessionListAsync(ct); var existingSession = existingSessions.FirstOrDefault(s => s.EntityId == samlServiceProvider.EntityId); string sessionIndex; @@ -76,7 +76,7 @@ internal class SamlSigninCallbackRequestProcessor( sessionIndex = Guid.NewGuid().ToString("N"); } - var samlResponse = await responseBuilder.BuildSuccessResponseAsync(user, samlServiceProvider, authenticationState, sessionIndex); + var samlResponse = await responseBuilder.BuildSuccessResponseAsync(user, samlServiceProvider, authenticationState, sessionIndex, ct); if (string.IsNullOrEmpty(samlResponse.Assertion?.Subject?.NameId?.Value)) { @@ -96,7 +96,7 @@ internal class SamlSigninCallbackRequestProcessor( NameId = samlResponse.Assertion.Subject.NameId.Value, NameIdFormat = samlResponse.Assertion.Subject.NameId.Format }; - await userSession.AddSamlSessionAsync(sessionData); + await userSession.AddSamlSessionAsync(sessionData, ct); stateIdCookie.ClearAuthenticationState(); diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninEndpoint.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninEndpoint.cs index 8cc2f4e09..ccd852773 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninEndpoint.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninEndpoint.cs @@ -34,7 +34,7 @@ internal class SamlSigninEndpoint( internal async Task ProcessSpInitiatedSignin( SamlSigninRequest signinRequest, - CT ct = default) + Ct ct = default) { logger.StartSamlSigninRequest(LogLevel.Debug); diff --git a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninRequestProcessor.cs b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninRequestProcessor.cs index 6eff522a9..fe37d5293 100644 --- a/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninRequestProcessor.cs +++ b/identity-server/src/IdentityServer/Internal/Saml/SingleSignin/SamlSigninRequestProcessor.cs @@ -38,7 +38,7 @@ internal class SamlSigninRequestProcessor( protected override async Task>> ProcessValidatedRequestAsync( SamlServiceProvider sp, SamlSigninRequest signinRequest, - CT ct = default) + Ct ct = default) { var authNRequest = signinRequest.AuthNRequest; @@ -178,7 +178,7 @@ internal class SamlSigninRequestProcessor( Uri assertionConsumerServiceUrl, AuthNRequest authNRequest, SamlServiceProvider sp, - CT ct = default) + Ct ct = default) { var state = new SamlAuthenticationState { diff --git a/identity-server/src/IdentityServer/Saml/ISamlInteractionService.cs b/identity-server/src/IdentityServer/Saml/ISamlInteractionService.cs index 2b9a8d5c2..fa87f56eb 100644 --- a/identity-server/src/IdentityServer/Saml/ISamlInteractionService.cs +++ b/identity-server/src/IdentityServer/Saml/ISamlInteractionService.cs @@ -15,7 +15,7 @@ public interface ISamlInteractionService /// Gets the SAML authentication request context from the current request's state cookie. /// Returns null if no SAML authentication is in progress. /// - Task GetAuthenticationRequestContextAsync(CT ct = default); + Task GetAuthenticationRequestContextAsync(Ct ct = default); /// /// Stores whether the user met the requirements of the RequestedAuthnContext in the @@ -26,5 +26,5 @@ public interface ISamlInteractionService /// Whether the requirements of the RequestedAuthnContext were met. /// Cancellation token /// - Task StoreRequestedAuthnContextResultAsync(bool requestedAuthnContextRequirementsWereMet, CT ct = default); + Task StoreRequestedAuthnContextResultAsync(bool requestedAuthnContextRequirementsWereMet, Ct ct = default); } diff --git a/identity-server/src/IdentityServer/Saml/ISamlLogoutNotificationService.cs b/identity-server/src/IdentityServer/Saml/ISamlLogoutNotificationService.cs index 236ca8551..3d0f8b5bf 100644 --- a/identity-server/src/IdentityServer/Saml/ISamlLogoutNotificationService.cs +++ b/identity-server/src/IdentityServer/Saml/ISamlLogoutNotificationService.cs @@ -7,5 +7,10 @@ namespace Duende.IdentityServer.Saml; public interface ISamlLogoutNotificationService { - Task> GetSamlFrontChannelLogoutsAsync(LogoutNotificationContext context); + /// + /// Builds the URLs needed for front-channel logout notification. + /// + /// The context for the logout notification. + /// The cancellation token. + Task> GetSamlFrontChannelLogoutsAsync(LogoutNotificationContext context, Ct ct); } diff --git a/identity-server/src/IdentityServer/Saml/ISamlSigninInteractionResponseGenerator.cs b/identity-server/src/IdentityServer/Saml/ISamlSigninInteractionResponseGenerator.cs index a9803bbf2..28492afcc 100644 --- a/identity-server/src/IdentityServer/Saml/ISamlSigninInteractionResponseGenerator.cs +++ b/identity-server/src/IdentityServer/Saml/ISamlSigninInteractionResponseGenerator.cs @@ -8,5 +8,5 @@ namespace Duende.IdentityServer.Saml; public interface ISamlSigninInteractionResponseGenerator { - Task ProcessInteractionAsync(SamlServiceProvider sp, AuthNRequest request, CT ct = default); + Task ProcessInteractionAsync(SamlServiceProvider sp, AuthNRequest request, Ct ct = default); } diff --git a/identity-server/src/IdentityServer/Services/Default/DefaultUserSession.cs b/identity-server/src/IdentityServer/Services/Default/DefaultUserSession.cs index 48cd6ac79..34e577641 100644 --- a/identity-server/src/IdentityServer/Services/Default/DefaultUserSession.cs +++ b/identity-server/src/IdentityServer/Services/Default/DefaultUserSession.cs @@ -364,7 +364,7 @@ public class DefaultUserSession : IUserSession } /// - public virtual async Task AddSamlSessionAsync(SamlSpSessionData session) + public virtual async Task AddSamlSessionAsync(SamlSpSessionData session, Ct ct) { ArgumentNullException.ThrowIfNull(session); @@ -377,7 +377,7 @@ public class DefaultUserSession : IUserSession } /// - public virtual async Task> GetSamlSessionListAsync() + public virtual async Task> GetSamlSessionListAsync(Ct ct) { await AuthenticateAsync(); @@ -397,7 +397,7 @@ public class DefaultUserSession : IUserSession } /// - public virtual async Task RemoveSamlSessionAsync(string entityId) + public virtual async Task RemoveSamlSessionAsync(string entityId, Ct ct) { ArgumentNullException.ThrowIfNull(entityId); diff --git a/identity-server/src/IdentityServer/Stores/InMemory/InMemorySamlServiceProviderStore.cs b/identity-server/src/IdentityServer/Stores/InMemory/InMemorySamlServiceProviderStore.cs index 81b9db4d8..d5ce470e3 100644 --- a/identity-server/src/IdentityServer/Stores/InMemory/InMemorySamlServiceProviderStore.cs +++ b/identity-server/src/IdentityServer/Stores/InMemory/InMemorySamlServiceProviderStore.cs @@ -30,8 +30,9 @@ public class InMemorySamlServiceProviderStore : ISamlServiceProviderStore /// Finds a SAML Service Provider by its entity identifier. /// /// The entity identifier of the Service Provider. + /// The cancellation token. /// The Service Provider, or null if not found. - public Task FindByEntityIdAsync(string entityId) + public Task FindByEntityIdAsync(string entityId, Ct ct) { using var activity = Tracing.StoreActivitySource.StartActivity("InMemorySamlServiceProviderStore.FindByEntityId"); activity?.SetTag(Tracing.Properties.SamlEntityId, entityId); diff --git a/identity-server/src/IdentityServer/Validation/Default/EndSessionRequestValidator.cs b/identity-server/src/IdentityServer/Validation/Default/EndSessionRequestValidator.cs index 2cc96b166..f00febfe0 100644 --- a/identity-server/src/IdentityServer/Validation/Default/EndSessionRequestValidator.cs +++ b/identity-server/src/IdentityServer/Validation/Default/EndSessionRequestValidator.cs @@ -251,7 +251,7 @@ public class EndSessionRequestValidator : IEndSessionRequestValidator result.IsError = false; result.FrontChannelLogoutUrls = await LogoutNotificationService.GetFrontChannelLogoutNotificationsUrlsAsync(endSessionMessage.Data, ct); - var samlFrontChannelLogouts = await SamlLogoutNotificationService.GetSamlFrontChannelLogoutsAsync(endSessionMessage.Data); + var samlFrontChannelLogouts = await SamlLogoutNotificationService.GetSamlFrontChannelLogoutsAsync(endSessionMessage.Data, ct); result.SamlFrontChannelLogouts = samlFrontChannelLogouts; } else diff --git a/identity-server/src/Storage/Stores/ISamlServiceProviderStore.cs b/identity-server/src/Storage/Stores/ISamlServiceProviderStore.cs index 8eae4d467..5e6735297 100644 --- a/identity-server/src/Storage/Stores/ISamlServiceProviderStore.cs +++ b/identity-server/src/Storage/Stores/ISamlServiceProviderStore.cs @@ -16,6 +16,7 @@ public interface ISamlServiceProviderStore /// Finds a SAML Service Provider by its entity identifier. /// /// The entity identifier of the Service Provider. + /// The cancellation token. /// The Service Provider, or null if not found. - Task FindByEntityIdAsync(string entityId); + Task FindByEntityIdAsync(string entityId, Ct ct); } diff --git a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/CookieHandler.cs b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/CookieHandler.cs index 514c82ef6..180bb4e53 100644 --- a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/CookieHandler.cs +++ b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/CookieHandler.cs @@ -13,7 +13,7 @@ internal class CookieHandler(HttpMessageHandler innerHandler, CookieContainer? c public void ClearCookies() => CookieContainer = new CookieContainer(); public CookieContainer CookieContainer { get; private set; } = cookies ?? new CookieContainer(); - protected override async Task SendAsync(HttpRequestMessage request, CT ct) + protected override async Task SendAsync(HttpRequestMessage request, Ct ct) { var requestUri = request.RequestUri; var header = CookieContainer.GetCookieHeader(requestUri!); diff --git a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlClaimsMappingTests.cs b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlClaimsMappingTests.cs index 0fb2503c4..92ca70314 100644 --- a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlClaimsMappingTests.cs +++ b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlClaimsMappingTests.cs @@ -16,6 +16,8 @@ public class SamlClaimsMappingTests { private const string Category = "SAML Claims Mapping"; + private readonly Ct _ct = TestContext.Current.CancellationToken; + private SamlFixture Fixture = new(); private SamlDataBuilder Build => Fixture.Builder; @@ -36,17 +38,17 @@ public class SamlClaimsMappingTests }; Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity(claims, "Test")); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.OK); - var successResponse = await ExtractSamlSuccessFromPostAsync(result, CT.None); + var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct); // Verify mapped attributes are present with correct names var attributes = successResponse.Assertion.Attributes; @@ -82,17 +84,17 @@ public class SamlClaimsMappingTests }; Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity(claims, "Test")); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.OK); - var successResponse = await ExtractSamlSuccessFromPostAsync(result, CT.None); + var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct); var attributes = successResponse.Assertion.Attributes; attributes.ShouldNotBeNull(); @@ -130,17 +132,17 @@ public class SamlClaimsMappingTests }; Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity(claims, "Test")); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.OK); - var successResponse = await ExtractSamlSuccessFromPostAsync(result, CT.None); + var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct); var attributes = successResponse.Assertion.Attributes; attributes.ShouldNotBeNull(); @@ -181,17 +183,17 @@ public class SamlClaimsMappingTests }; Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity(claims, "Test")); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.OK); - var successResponse = await ExtractSamlSuccessFromPostAsync(result, CT.None); + var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct); var attributes = successResponse.Assertion.Attributes; attributes.ShouldNotBeNull(); @@ -223,17 +225,17 @@ public class SamlClaimsMappingTests }; Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity(claims, "Test")); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.OK); - var successResponse = await ExtractSamlSuccessFromPostAsync(result, CT.None); + var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct); var attributes = successResponse.Assertion.Attributes; attributes.ShouldNotBeNull(); @@ -279,17 +281,17 @@ public class SamlClaimsMappingTests }; Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity(claims, "Test")); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.OK); - var successResponse = await ExtractSamlSuccessFromPostAsync(result, CT.None); + var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct); var attributes = successResponse.Assertion.Attributes; attributes.ShouldNotBeNull(); diff --git a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlEncryptionTests.cs b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlEncryptionTests.cs index bee4e1078..d9ea8608c 100644 --- a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlEncryptionTests.cs +++ b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlEncryptionTests.cs @@ -18,6 +18,8 @@ public class SamlEncryptionTests { private const string Category = "SAML Encryption"; + private readonly Ct _ct = TestContext.Current.CancellationToken; + private SamlFixture Fixture = new(); private SamlData Data => Fixture.Data; private SamlDataBuilder Build => Fixture.Builder; @@ -59,16 +61,16 @@ public class SamlEncryptionTests "Test")); await Fixture.InitializeAsync(); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); // Act - var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), CT.None); - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), _ct); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.OK); - var responseData = await ExtractSamlResponse(result, CT.None); + var responseData = await ExtractSamlResponse(result, _ct); var responseXml = responseData.responseXml; // Verify encrypted assertion is present @@ -105,16 +107,16 @@ public class SamlEncryptionTests "Test")); await Fixture.InitializeAsync(); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); // Act - var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), CT.None); - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), _ct); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert - Decrypt and verify actual content result.StatusCode.ShouldBe(HttpStatusCode.OK); - var samlResponse = await ExtractAndDecryptSamlSuccessFromPostAsync(result, encryptionCert, CT.None); + var samlResponse = await ExtractAndDecryptSamlSuccessFromPostAsync(result, encryptionCert, _ct); samlResponse.StatusCode.ShouldBe(SamlStatusCodes.Success); samlResponse.Assertion.ShouldNotBeNull(); @@ -163,16 +165,16 @@ public class SamlEncryptionTests "Test")); await Fixture.InitializeAsync(); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); // Act - var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), CT.None); - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), _ct); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert - Verify encrypted structure result.StatusCode.ShouldBe(HttpStatusCode.OK); - var responseData = await ExtractSamlResponse(result, CT.None); + var responseData = await ExtractSamlResponse(result, _ct); var responseXml = responseData.responseXml; // Verify encryption happened @@ -234,16 +236,16 @@ public class SamlEncryptionTests "Test")); await Fixture.InitializeAsync(); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); // Act - var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), CT.None); - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), _ct); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert - Verify structure is valid (can't test decryption due to helper limitations) result.StatusCode.ShouldBe(HttpStatusCode.OK); - var responseData = await ExtractSamlResponse(result, CT.None); + var responseData = await ExtractSamlResponse(result, _ct); var responseXml = responseData.responseXml; var (_, _, responseElement) = ParseSamlResponseXml(responseXml); @@ -268,16 +270,16 @@ public class SamlEncryptionTests "Test")); await Fixture.InitializeAsync(); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); // Act - var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), CT.None); - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), _ct); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.OK); - var responseData = await ExtractSamlResponse(result, CT.None); + var responseData = await ExtractSamlResponse(result, _ct); var responseXml = responseData.responseXml; var (_, _, responseElement) = ParseSamlResponseXml(responseXml); @@ -307,16 +309,16 @@ public class SamlEncryptionTests "Test")); await Fixture.InitializeAsync(); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); // Act - var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), CT.None); - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), _ct); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert - Encryption should happen after signing result.StatusCode.ShouldBe(HttpStatusCode.OK); - var responseData = await ExtractSamlResponse(result, CT.None); + var responseData = await ExtractSamlResponse(result, _ct); HasEncryptedAssertion(responseData.responseXml).ShouldBeTrue(); } @@ -337,16 +339,16 @@ public class SamlEncryptionTests "Test")); await Fixture.InitializeAsync(); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); // Act - var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), CT.None); - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), _ct); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.OK); - var responseData = await ExtractSamlResponse(result, CT.None); + var responseData = await ExtractSamlResponse(result, _ct); var responseXml = responseData.responseXml; var (_, _, responseElement) = ParseSamlResponseXml(responseXml); @@ -376,15 +378,15 @@ public class SamlEncryptionTests "Test")); await Fixture.InitializeAsync(); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); // Act - var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), CT.None); - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), _ct); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert - Should encrypt successfully with first valid cert result.StatusCode.ShouldBe(HttpStatusCode.OK); - var responseData = await ExtractSamlResponse(result, CT.None); + var responseData = await ExtractSamlResponse(result, _ct); HasEncryptedAssertion(responseData.responseXml).ShouldBeTrue(); } @@ -413,11 +415,11 @@ public class SamlEncryptionTests "Test")); await Fixture.InitializeAsync(); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); // Act - var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), CT.None); - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), _ct); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert - Expired cert is a configuration error, so expect 500 result.StatusCode.ShouldBe(HttpStatusCode.InternalServerError); @@ -436,23 +438,23 @@ public class SamlEncryptionTests "Test")); await Fixture.InitializeAsync(); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); // Act - var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), CT.None); - var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(Build.AuthNRequestXml(), _ct); + var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); // Assert - Should return plain assertion result.StatusCode.ShouldBe(HttpStatusCode.OK); - var responseData = await ExtractSamlResponse(result, CT.None); + var responseData = await ExtractSamlResponse(result, _ct); var responseXml = responseData.responseXml; HasPlainAssertion(responseXml).ShouldBeTrue("Response should contain plain Assertion"); HasEncryptedAssertion(responseXml).ShouldBeFalse("Response should not be encrypted"); // Verify can parse as success - var samlResponse = await ExtractSamlSuccessFromPostAsync(result, CT.None); + var samlResponse = await ExtractSamlSuccessFromPostAsync(result, _ct); samlResponse.StatusCode.ShouldBe(SamlStatusCodes.Success); samlResponse.Assertion.ShouldNotBeNull(); } diff --git a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlFixture.cs b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlFixture.cs index 39fdb3946..cacd07c61 100644 --- a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlFixture.cs +++ b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlFixture.cs @@ -21,6 +21,8 @@ namespace Duende.IdentityServer.IntegrationTests.Endpoints.Saml; internal class SamlFixture : IAsyncLifetime { + private readonly Ct _ct = TestContext.Current.CancellationToken; + public SamlData Data = new SamlData(); public SamlDataBuilder Builder => new SamlDataBuilder(Data); @@ -247,7 +249,7 @@ internal class SamlFixture : IAsyncLifetime { var samlInteractionService = ctx.RequestServices.GetRequiredService(); var authenticationRequest = - await samlInteractionService.GetAuthenticationRequestContextAsync(CT.None); + await samlInteractionService.GetAuthenticationRequestContextAsync(_ct); if (authenticationRequest == null) { diff --git a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlIdpInitiatedEndpointTests.cs b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlIdpInitiatedEndpointTests.cs index ac1c70fdd..03f8190fa 100644 --- a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlIdpInitiatedEndpointTests.cs +++ b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlIdpInitiatedEndpointTests.cs @@ -16,6 +16,8 @@ public class SamlIdpInitiatedEndpointTests { private const string Category = "SAML IdP-Initiated Endpoint"; + private readonly Ct _ct = TestContext.Current.CancellationToken; + private SamlFixture Fixture = new(); private SamlData Data => Fixture.Data; @@ -38,11 +40,11 @@ public class SamlIdpInitiatedEndpointTests Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.NonRedirectingClient.GetAsync("/__signin", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signin", _ct); // Act var spEntityId = HttpUtility.UrlEncode(Data.EntityId.ToString()); - var result = await Fixture.NonRedirectingClient.GetAsync($"/saml/idp-initiated?spEntityId={spEntityId}", CT.None); + var result = await Fixture.NonRedirectingClient.GetAsync($"/saml/idp-initiated?spEntityId={spEntityId}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.Found); @@ -67,23 +69,23 @@ public class SamlIdpInitiatedEndpointTests Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.NonRedirectingClient.GetAsync("/__signin", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signin", _ct); // Act var spEntityId = HttpUtility.UrlEncode(Data.EntityId.ToString()); var relayState = HttpUtility.UrlEncode("/my-app/dashboard"); var result = await Fixture.NonRedirectingClient.GetAsync( - $"/saml/idp-initiated?spEntityId={spEntityId}&relayState={relayState}", CT.None); + $"/saml/idp-initiated?spEntityId={spEntityId}&relayState={relayState}", _ct); result.StatusCode.ShouldBe(HttpStatusCode.Found); var redirectUri = result.Headers.Location; redirectUri.ShouldNotBeNull(); redirectUri.ToString().ShouldBe($"{SamlConstants.Urls.SamlRoute}{SamlConstants.Urls.SigninCallback}"); - var acsResult = await Fixture.NonRedirectingClient.GetAsync(redirectUri, CT.None); + var acsResult = await Fixture.NonRedirectingClient.GetAsync(redirectUri, _ct); // Assert - var samlResponse = await ExtractSamlSuccessFromPostAsync(acsResult, CT.None); + var samlResponse = await ExtractSamlSuccessFromPostAsync(acsResult, _ct); samlResponse.RelayState.ShouldBe(HttpUtility.UrlDecode(relayState)); } @@ -103,7 +105,7 @@ public class SamlIdpInitiatedEndpointTests // Act var spEntityId = HttpUtility.UrlEncode(Data.EntityId.ToString()); - var result = await Fixture.NonRedirectingClient.GetAsync($"/saml/idp-initiated?spEntityId={spEntityId}", CT.None); + var result = await Fixture.NonRedirectingClient.GetAsync($"/saml/idp-initiated?spEntityId={spEntityId}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.Found); @@ -126,11 +128,11 @@ public class SamlIdpInitiatedEndpointTests // Act var unknownEntityId = HttpUtility.UrlEncode("https://unknown.example.com"); - var result = await Fixture.Client.GetAsync($"/saml/idp-initiated?spEntityId={unknownEntityId}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/idp-initiated?spEntityId={unknownEntityId}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe("Service Provider 'https://unknown.example.com' is not registered"); } @@ -154,11 +156,11 @@ public class SamlIdpInitiatedEndpointTests // Act var spEntityId = HttpUtility.UrlEncode(Data.EntityId.ToString()); - var result = await Fixture.Client.GetAsync($"/saml/idp-initiated?spEntityId={spEntityId}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/idp-initiated?spEntityId={spEntityId}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); // Disabled SPs are filtered by the store, so they appear as not registered problemDetails.Detail.ShouldBe($"Service Provider '{Data.EntityId}' is not registered"); @@ -180,11 +182,11 @@ public class SamlIdpInitiatedEndpointTests // Act var spEntityId = HttpUtility.UrlEncode(Data.EntityId.ToString()); - var result = await Fixture.Client.GetAsync($"/saml/idp-initiated?spEntityId={spEntityId}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/idp-initiated?spEntityId={spEntityId}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe($"Service Provider '{Data.EntityId}' does not allow IdP-initiated SSO"); } @@ -212,11 +214,11 @@ public class SamlIdpInitiatedEndpointTests var spEntityId = HttpUtility.UrlEncode(Data.EntityId.ToString()); var longRelayState = HttpUtility.UrlEncode(new string('a', 100)); var result = await Fixture.Client.GetAsync( - $"/saml/idp-initiated?spEntityId={spEntityId}&relayState={longRelayState}", CT.None); + $"/saml/idp-initiated?spEntityId={spEntityId}&relayState={longRelayState}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe("RelayState exceeds maximum length of 50 bytes"); } @@ -238,11 +240,11 @@ public class SamlIdpInitiatedEndpointTests // Act var spEntityId = HttpUtility.UrlEncode(Data.EntityId.ToString()); - var result = await Fixture.Client.GetAsync($"/saml/idp-initiated?spEntityId={spEntityId}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/idp-initiated?spEntityId={spEntityId}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe($"Service Provider '{Data.EntityId}' has no AssertionConsumerServiceUrls configured"); } @@ -267,21 +269,21 @@ public class SamlIdpInitiatedEndpointTests Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.NonRedirectingClient.GetAsync("/__signin", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signin", _ct); // Act var spEntityId = HttpUtility.UrlEncode(Data.EntityId.ToString()); - var result = await Fixture.NonRedirectingClient.GetAsync($"/saml/idp-initiated?spEntityId={spEntityId}", CT.None); + var result = await Fixture.NonRedirectingClient.GetAsync($"/saml/idp-initiated?spEntityId={spEntityId}", _ct); result.StatusCode.ShouldBe(HttpStatusCode.Found); var redirectUri = result.Headers.Location; redirectUri.ShouldNotBeNull(); redirectUri.ToString().ShouldBe($"{SamlConstants.Urls.SamlRoute}{SamlConstants.Urls.SigninCallback}"); - var acsResult = await Fixture.NonRedirectingClient.GetAsync(redirectUri.ToString(), CT.None); + var acsResult = await Fixture.NonRedirectingClient.GetAsync(redirectUri.ToString(), _ct); // Assert - var samlResponse = await ExtractSamlSuccessFromPostAsync(acsResult, CT.None); + var samlResponse = await ExtractSamlSuccessFromPostAsync(acsResult, _ct); samlResponse.Destination.ShouldBe(firstAcsUrl.ToString()); } @@ -306,7 +308,7 @@ public class SamlIdpInitiatedEndpointTests new Claim(JwtClaimTypes.Email, "user@example.com"), new Claim(JwtClaimTypes.Name, "Test User") ], "Test")); - await Fixture.NonRedirectingClient.GetAsync("/__signin", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signin", _ct); var spEntityId = HttpUtility.UrlEncode(Data.EntityId.ToString()); var relayState = HttpUtility.UrlEncode("/target/page"); @@ -314,7 +316,7 @@ public class SamlIdpInitiatedEndpointTests // Act // Step 1: Initiate IdP SSO var initiateResult = await Fixture.NonRedirectingClient.GetAsync( - $"/saml/idp-initiated?spEntityId={spEntityId}&relayState={relayState}", CT.None); + $"/saml/idp-initiated?spEntityId={spEntityId}&relayState={relayState}", _ct); initiateResult.StatusCode.ShouldBe(HttpStatusCode.Found); initiateResult.Headers.Location.ShouldNotBeNull(); @@ -324,12 +326,12 @@ public class SamlIdpInitiatedEndpointTests stateId.ShouldNotBeNull(); // Step 2: Follow redirect to signin callback - var callbackResult = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", CT.None); + var callbackResult = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", _ct); // Assert callbackResult.StatusCode.ShouldBe(HttpStatusCode.OK); - var samlResponse = await ExtractSamlSuccessFromPostAsync(callbackResult, CT.None); + var samlResponse = await ExtractSamlSuccessFromPostAsync(callbackResult, _ct); samlResponse.ShouldNotBeNull(); samlResponse.Issuer.ShouldBe(Fixture.Url()); @@ -361,7 +363,7 @@ public class SamlIdpInitiatedEndpointTests // Act var spEntityId = HttpUtility.UrlEncode(Data.EntityId.ToString()); - var result = await Fixture.Client.GetAsync($"/saml/idp-initiated?spEntityId={spEntityId}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/idp-initiated?spEntityId={spEntityId}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.NotFound); @@ -378,7 +380,7 @@ public class SamlIdpInitiatedEndpointTests }; await Fixture.InitializeAsync(); - var result = await Fixture.Client.GetAsync("/saml/idp-initiated", CT.None); + var result = await Fixture.Client.GetAsync("/saml/idp-initiated", _ct); result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); } diff --git a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlMetadataEndpointTests.cs b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlMetadataEndpointTests.cs index 70bb0a591..c7e03a10d 100644 --- a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlMetadataEndpointTests.cs +++ b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlMetadataEndpointTests.cs @@ -11,6 +11,8 @@ public class SamlMetadataEndpointTests { private const string Category = "SAML Metadata Endpoint"; + private readonly Ct _ct = TestContext.Current.CancellationToken; + private SamlFixture Fixture = new(); [Fact] @@ -19,14 +21,14 @@ public class SamlMetadataEndpointTests { await Fixture.InitializeAsync(); - var result = await Fixture.Client.GetAsync("/saml/metadata", CT.None); + var result = await Fixture.Client.GetAsync("/saml/metadata", _ct); result.StatusCode.ShouldBe(HttpStatusCode.OK); result.Content.Headers.ContentType .ShouldNotBeNull() .MediaType .ShouldBe(SamlConstants.ContentTypes.Metadata); - var content = await result.Content.ReadAsStringAsync(CT.None); + var content = await result.Content.ReadAsStringAsync(_ct); var settings = new VerifySettings(); var hostUri = Fixture.Url(); @@ -49,10 +51,10 @@ public class SamlMetadataEndpointTests await Fixture.InitializeAsync(); - var result = await Fixture.Client.GetAsync("/saml/metadata", CT.None); + var result = await Fixture.Client.GetAsync("/saml/metadata", _ct); result.StatusCode.ShouldBe(HttpStatusCode.OK); - var content = await result.Content.ReadAsStringAsync(CT.None); + var content = await result.Content.ReadAsStringAsync(_ct); var expectedValidUntil = Fixture.Now.Add(TimeSpan.FromDays(30)).UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ssZ"); content.ShouldContain($"validUntil=\"{expectedValidUntil}\""); @@ -69,10 +71,10 @@ public class SamlMetadataEndpointTests await Fixture.InitializeAsync(); - var result = await Fixture.Client.GetAsync("/saml/metadata", CT.None); + var result = await Fixture.Client.GetAsync("/saml/metadata", _ct); result.StatusCode.ShouldBe(HttpStatusCode.OK); - var content = await result.Content.ReadAsStringAsync(CT.None); + var content = await result.Content.ReadAsStringAsync(_ct); content.ShouldContain("WantAuthnRequestsSigned=\"true\""); } @@ -90,10 +92,10 @@ public class SamlMetadataEndpointTests await Fixture.InitializeAsync(); - var result = await Fixture.Client.GetAsync("/saml/metadata", CT.None); + var result = await Fixture.Client.GetAsync("/saml/metadata", _ct); result.StatusCode.ShouldBe(HttpStatusCode.OK); - var content = await result.Content.ReadAsStringAsync(CT.None); + var content = await result.Content.ReadAsStringAsync(_ct); content.ShouldContain("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"); content.ShouldContain("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"); @@ -106,10 +108,10 @@ public class SamlMetadataEndpointTests { await Fixture.InitializeAsync(); - var result = await Fixture.Client.GetAsync("/saml/metadata", CT.None); + var result = await Fixture.Client.GetAsync("/saml/metadata", _ct); result.StatusCode.ShouldBe(HttpStatusCode.OK); - var content = await result.Content.ReadAsStringAsync(CT.None); + var content = await result.Content.ReadAsStringAsync(_ct); content.ShouldContain(" Fixture.Data; @@ -34,11 +36,11 @@ public class SamlSigninCallbackEndpointTests Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); - var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); + var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); signinResult.StatusCode.ShouldBe(HttpStatusCode.Found); signinResult.Headers.Location.ShouldNotBeNull(); @@ -48,12 +50,12 @@ public class SamlSigninCallbackEndpointTests // Remove state from store so the next request is sent with a state id that for state which no longer exists var samlSigninStateStore = Fixture.Get(); - await samlSigninStateStore.RetrieveSigninRequestStateAsync(new StateId(Guid.Parse(stateId)), CT.None); + await samlSigninStateStore.RetrieveSigninRequestStateAsync(new StateId(Guid.Parse(stateId)), _ct); - var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", CT.None); + var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", _ct); result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe($"The request {stateId} could not be found."); } @@ -67,14 +69,14 @@ public class SamlSigninCallbackEndpointTests Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); // Do not make request to the sign-in endpoint first so no state id is created - var result = await Fixture.Client.GetAsync("/saml/signin_callback", CT.None); + var result = await Fixture.Client.GetAsync("/saml/signin_callback", _ct); result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe("No state id could be found."); } @@ -88,20 +90,20 @@ public class SamlSigninCallbackEndpointTests Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.NonRedirectingClient.GetAsync("/__signin", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); - var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); + var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); signinResult.StatusCode.ShouldBe(HttpStatusCode.Found); var redirectUri = signinResult.Headers.Location; redirectUri.ShouldNotBeNull(); redirectUri.ToString().ShouldStartWith("/saml/signin_callback"); - await Fixture.NonRedirectingClient.GetAsync("/__signout", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signout", _ct); - var result = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin_callback", CT.None); + var result = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin_callback", _ct); result.StatusCode.ShouldBe(HttpStatusCode.Found); var resultRedirectUri = result.Headers.Location; @@ -119,11 +121,11 @@ public class SamlSigninCallbackEndpointTests Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.NonRedirectingClient.GetAsync("/__signin", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); - var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); + var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); signinResult.StatusCode.ShouldBe(HttpStatusCode.Found); var redirectUri = signinResult.Headers.Location; @@ -131,10 +133,10 @@ public class SamlSigninCallbackEndpointTests Fixture.ClearServiceProvidersAsync(); - var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", CT.None); + var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", _ct); result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe($"Service Provider '{sp.EntityId}' is not registered or is disabled"); } @@ -149,11 +151,11 @@ public class SamlSigninCallbackEndpointTests Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.NonRedirectingClient.GetAsync("/__signin", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); - var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); + var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); signinResult.StatusCode.ShouldBe(HttpStatusCode.Found); var redirectUri = signinResult.Headers.Location; @@ -163,10 +165,10 @@ public class SamlSigninCallbackEndpointTests // we'll rely on everything being in memory and holding onto a reference to the SP in the store for now sp.Enabled = false; - var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", CT.None); + var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", _ct); result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe($"Service Provider '{sp.EntityId}' is not registered or is disabled"); } @@ -180,30 +182,30 @@ public class SamlSigninCallbackEndpointTests Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.NonRedirectingClient.GetAsync("/__signin", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); - var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); signinResult.StatusCode.ShouldBe(HttpStatusCode.Found); var redirectUri = signinResult.Headers.Location; redirectUri.ShouldNotBeNull(); - var firstResult = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", CT.None); + var firstResult = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", _ct); // First use succeeds firstResult.StatusCode.ShouldBe(HttpStatusCode.OK); - var html = await firstResult.Content.ReadAsStringAsync(CT.None); + var html = await firstResult.Content.ReadAsStringAsync(_ct); html.ShouldContain("SAMLResponse"); // Second callback with same stateId (replay attack) - var secondResult = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", CT.None); + var secondResult = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", _ct); // Second use should fail secondResult.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await secondResult.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await secondResult.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe("No state id could be found."); } @@ -217,12 +219,12 @@ public class SamlSigninCallbackEndpointTests Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.NonRedirectingClient.GetAsync("/__signin", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); - var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); signinResult.StatusCode.ShouldBe(HttpStatusCode.Found); signinResult.Headers.Location.ShouldNotBeNull(); @@ -231,10 +233,10 @@ public class SamlSigninCallbackEndpointTests Fixture.Data.FakeTimeProvider.Advance(TimeSpan.FromMinutes(11)); - var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", CT.None); + var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", _ct); result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe($"The request {stateId} could not be found."); } @@ -248,22 +250,22 @@ public class SamlSigninCallbackEndpointTests Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.NonRedirectingClient.GetAsync("/__signin", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signin", _ct); var specificRelayState = "test-relay-state-123"; var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); - var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}&RelayState={specificRelayState}", CT.None); + var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}&RelayState={specificRelayState}", _ct); signinResult.StatusCode.ShouldBe(HttpStatusCode.Found); var redirectUri = signinResult.Headers.Location; redirectUri.ShouldNotBeNull(); - var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", CT.None); + var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", _ct); result.StatusCode.ShouldBe(HttpStatusCode.OK); - var samlResponse = await ExtractSamlSuccessFromPostAsync(result, CT.None); + var samlResponse = await ExtractSamlSuccessFromPostAsync(result, _ct); samlResponse.ShouldNotBeNull(); samlResponse.RelayState.ShouldBe(specificRelayState); } @@ -279,24 +281,24 @@ public class SamlSigninCallbackEndpointTests Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.NonRedirectingClient.GetAsync("/__signin", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); - var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); + var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); signinResult.StatusCode.ShouldBe(HttpStatusCode.Found); var redirectUri = signinResult.Headers.Location; redirectUri.ShouldNotBeNull(); - var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", CT.None); + var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", _ct); result.StatusCode.ShouldBe(HttpStatusCode.OK); - var (responseXml, _, _) = await ExtractSamlResponse(result, CT.None); + var (responseXml, _, _) = await ExtractSamlResponse(result, _ct); VerifySignaturePresence(responseXml, expectResponseSignature: true, expectAssertionSignature: false); - var successResponse = await ExtractSamlSuccessFromPostAsync(result, CT.None); + var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct); successResponse.StatusCode.ShouldBe("urn:oasis:names:tc:SAML:2.0:status:Success"); successResponse.Assertion.Subject?.NameId.ShouldBe("user-id"); } @@ -312,24 +314,24 @@ public class SamlSigninCallbackEndpointTests Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.NonRedirectingClient.GetAsync("/__signin", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); - var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); + var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); signinResult.StatusCode.ShouldBe(HttpStatusCode.Found); var redirectUri = signinResult.Headers.Location; redirectUri.ShouldNotBeNull(); - var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", CT.None); + var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", _ct); result.StatusCode.ShouldBe(HttpStatusCode.OK); - var (responseXml, _, _) = await ExtractSamlResponse(result, CT.None); + var (responseXml, _, _) = await ExtractSamlResponse(result, _ct); VerifySignaturePresence(responseXml, expectResponseSignature: false, expectAssertionSignature: true); - var successResponse = await ExtractSamlSuccessFromPostAsync(result, CT.None); + var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct); successResponse.StatusCode.ShouldBe("urn:oasis:names:tc:SAML:2.0:status:Success"); successResponse.Assertion.Subject?.NameId.ShouldBe("user-id"); } @@ -345,24 +347,24 @@ public class SamlSigninCallbackEndpointTests Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.NonRedirectingClient.GetAsync("/__signin", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); - var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); + var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); signinResult.StatusCode.ShouldBe(HttpStatusCode.Found); var redirectUri = signinResult.Headers.Location; redirectUri.ShouldNotBeNull(); - var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", CT.None); + var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", _ct); result.StatusCode.ShouldBe(HttpStatusCode.OK); - var (responseXml, _, _) = await ExtractSamlResponse(result, CT.None); + var (responseXml, _, _) = await ExtractSamlResponse(result, _ct); VerifySignaturePresence(responseXml, expectResponseSignature: false, expectAssertionSignature: false); - var successResponse = await ExtractSamlSuccessFromPostAsync(result, CT.None); + var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct); successResponse.StatusCode.ShouldBe("urn:oasis:names:tc:SAML:2.0:status:Success"); successResponse.Assertion.Subject?.NameId.ShouldBe("user-id"); } @@ -404,23 +406,23 @@ public class SamlSigninCallbackEndpointTests }; Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity(claims, "Test")); - await Fixture.NonRedirectingClient.GetAsync("/__signin", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); - var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); + var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); signinResult.StatusCode.ShouldBe(HttpStatusCode.Found); signinResult.Headers.Location.ShouldNotBeNull(); - var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", CT.None); + var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", _ct); result.StatusCode.ShouldBe(HttpStatusCode.OK); - var (responseXml, _, _) = await ExtractSamlResponse(result, CT.None); + var (responseXml, _, _) = await ExtractSamlResponse(result, _ct); VerifySignaturePresence(responseXml, expectResponseSignature: true, expectAssertionSignature: true); - var successResponse = await ExtractSamlSuccessFromPostAsync(result, CT.None); + var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct); successResponse.StatusCode.ShouldBe("urn:oasis:names:tc:SAML:2.0:status:Success"); successResponse.Assertion.Attributes.ShouldNotBeNull(); successResponse.Assertion.Attributes!.Count.ShouldBeGreaterThan(4); // At least the claims we added @@ -444,26 +446,26 @@ public class SamlSigninCallbackEndpointTests }; Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity(claims, "Test")); - await Fixture.NonRedirectingClient.GetAsync("/__signin", CT.None); + await Fixture.NonRedirectingClient.GetAsync("/__signin", _ct); var authnRequestXml = Build.AuthNRequestXml(); - var urlEncoded = await EncodeRequest(authnRequestXml, CT.None); - var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", CT.None); + var urlEncoded = await EncodeRequest(authnRequestXml, _ct); + var signinResult = await Fixture.NonRedirectingClient.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct); signinResult.StatusCode.ShouldBe(HttpStatusCode.Found); signinResult.Headers.Location.ShouldNotBeNull(); - var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", CT.None); + var result = await Fixture.NonRedirectingClient.GetAsync("/saml/signin_callback", _ct); result.StatusCode.ShouldBe(HttpStatusCode.OK); - var (responseXml, _, _) = await ExtractSamlResponse(result, CT.None); + var (responseXml, _, _) = await ExtractSamlResponse(result, _ct); VerifySignaturePresence(responseXml, expectResponseSignature: false, expectAssertionSignature: true); var (_, _, responseElement) = ParseSamlResponseXml(responseXml); responseElement.ShouldNotBeNull(); - var successResponse = await ExtractSamlSuccessFromPostAsync(result, CT.None); + var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct); successResponse.StatusCode.ShouldBe("urn:oasis:names:tc:SAML:2.0:status:Success"); var nameAttribute = successResponse.Assertion.Attributes?.FirstOrDefault(a => a.Name == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"); diff --git a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlSigninEndpointTests.cs b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlSigninEndpointTests.cs index 5631a6220..6d36e4ee7 100644 --- a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlSigninEndpointTests.cs +++ b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlSigninEndpointTests.cs @@ -23,7 +23,7 @@ public class SamlSigninEndpointTests { private const string Category = "SAML Signin Endpoint"; - private readonly CT _ct = CT.None; + private readonly Ct _ct = TestContext.Current.CancellationToken; private SamlFixture Fixture = new(); diff --git a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlSingleLogoutCallbackEndpointTests.cs b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlSingleLogoutCallbackEndpointTests.cs index c8b8d8a98..4c404b963 100644 --- a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlSingleLogoutCallbackEndpointTests.cs +++ b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlSingleLogoutCallbackEndpointTests.cs @@ -12,6 +12,8 @@ public class SamlSingleLogoutCallbackEndpointTests { private const string Category = "SAML single logout callback endpoint"; + private readonly Ct _ct = TestContext.Current.CancellationToken; + private SamlFixture Fixture = new(); private SamlData Data => Fixture.Data; @@ -27,7 +29,7 @@ public class SamlSingleLogoutCallbackEndpointTests await Fixture.InitializeAsync(); // Act - var result = await Fixture.Client.PostAsync("/saml/logout_callback", new StringContent(""), CT.None); + var result = await Fixture.Client.PostAsync("/saml/logout_callback", new StringContent(""), _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.MethodNotAllowed); @@ -42,7 +44,7 @@ public class SamlSingleLogoutCallbackEndpointTests await Fixture.InitializeAsync(); // Act - var result = await Fixture.Client.GetAsync("/saml/logout_callback", CT.None); + var result = await Fixture.Client.GetAsync("/saml/logout_callback", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); @@ -57,7 +59,7 @@ public class SamlSingleLogoutCallbackEndpointTests await Fixture.InitializeAsync(); // Act - var result = await Fixture.Client.GetAsync("/saml/logout_callback?logoutId=invalid", CT.None); + var result = await Fixture.Client.GetAsync("/saml/logout_callback?logoutId=invalid", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); @@ -82,13 +84,13 @@ public class SamlSingleLogoutCallbackEndpointTests SamlRelayState = null }; var messageStore = Fixture.Get>(); - var logoutId = await messageStore.WriteAsync(new Message(logoutMessage, DateTime.UtcNow)); + var logoutId = await messageStore.WriteAsync(new Message(logoutMessage, DateTime.UtcNow), _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout_callback?logoutId={logoutId}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout_callback?logoutId={logoutId}", _ct); // Assert - var samlResponse = await SamlTestHelpers.ExtractSamlLogoutResponseFromPostAsync(result, CT.None); + var samlResponse = await SamlTestHelpers.ExtractSamlLogoutResponseFromPostAsync(result, _ct); samlResponse.StatusCode.ShouldBe(SamlStatusCodes.Success); } @@ -110,13 +112,13 @@ public class SamlSingleLogoutCallbackEndpointTests SamlRelayState = "mystate123" }; var messageStore = Fixture.Get>(); - var logoutId = await messageStore.WriteAsync(new Message(logoutMessage, DateTime.UtcNow)); + var logoutId = await messageStore.WriteAsync(new Message(logoutMessage, DateTime.UtcNow), _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout_callback?logoutId={logoutId}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout_callback?logoutId={logoutId}", _ct); // Assert - var response = await SamlTestHelpers.ExtractSamlLogoutResponseFromPostAsync(result, CT.None); + var response = await SamlTestHelpers.ExtractSamlLogoutResponseFromPostAsync(result, _ct); response.RelayState.ShouldBe(logoutMessage.SamlRelayState); } @@ -138,10 +140,10 @@ public class SamlSingleLogoutCallbackEndpointTests SamlLogoutRequestId = "_abc123" }; var messageStore = Fixture.Get>(); - var logoutId = await messageStore.WriteAsync(new Message(logoutMessage, DateTime.UtcNow)); + var logoutId = await messageStore.WriteAsync(new Message(logoutMessage, DateTime.UtcNow), _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout_callback?logoutId={logoutId}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout_callback?logoutId={logoutId}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); diff --git a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlSingleLogoutEndpointTests.cs b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlSingleLogoutEndpointTests.cs index 4f3b0646f..270720512 100644 --- a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlSingleLogoutEndpointTests.cs +++ b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlSingleLogoutEndpointTests.cs @@ -18,6 +18,8 @@ public class SamlSingleLogoutEndpointTests { private const string Category = "SAML single logout endpoint"; + private readonly Ct _ct = TestContext.Current.CancellationToken; + private SamlFixture Fixture = new(); private SamlData Data => Fixture.Data; @@ -33,11 +35,11 @@ public class SamlSingleLogoutEndpointTests await Fixture.InitializeAsync(); // Act - var result = await Fixture.Client.GetAsync("/saml/logout", CT.None); + var result = await Fixture.Client.GetAsync("/saml/logout", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe("Missing 'SAMLRequest' query parameter in SAML logout request"); } @@ -54,11 +56,11 @@ public class SamlSingleLogoutEndpointTests var stringContent = new StringContent(logoutRequestXml, Encoding.UTF8, "application/xml"); // Act - var result = await Fixture.Client.PostAsync("/saml/logout", stringContent, CT.None); + var result = await Fixture.Client.PostAsync("/saml/logout", stringContent, _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe("POST request does not have form content type for SAML logout request"); } @@ -72,7 +74,7 @@ public class SamlSingleLogoutEndpointTests await Fixture.InitializeAsync(); var logoutRequestXml = Build.LogoutRequestXml(); - var encodedRequest = await EncodeRequest(logoutRequestXml, CT.None); + var encodedRequest = await EncodeRequest(logoutRequestXml, _ct); var formData = new Dictionary { { "wrong_form_key", encodedRequest } @@ -80,11 +82,11 @@ public class SamlSingleLogoutEndpointTests var content = new FormUrlEncodedContent(formData); // Act - var result = await Fixture.Client.PostAsync("/saml/logout", content, CT.None); + var result = await Fixture.Client.PostAsync("/saml/logout", content, _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe("Missing 'SAMLRequest' form parameter in SAML logout request"); } @@ -98,14 +100,14 @@ public class SamlSingleLogoutEndpointTests var issuer = "https://wrong-issuer.com"; var logoutRequestXml = Build.LogoutRequestXml(issuer: issuer); - var urlEncoded = await EncodeRequest(logoutRequestXml, CT.None); + var urlEncoded = await EncodeRequest(logoutRequestXml, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe($"Service Provider '{issuer}' is not registered or is disabled"); } @@ -121,14 +123,14 @@ public class SamlSingleLogoutEndpointTests await Fixture.InitializeAsync(); var logoutRequestXml = Build.LogoutRequestXml(issuer: sp.EntityId); - var urlEncoded = await EncodeRequest(logoutRequestXml, CT.None); + var urlEncoded = await EncodeRequest(logoutRequestXml, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe($"Service Provider '{sp.EntityId}' is not registered or is disabled"); } @@ -147,19 +149,19 @@ public class SamlSingleLogoutEndpointTests // Sign in a user first Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); var logoutRequestXml = Build.LogoutRequestXml( destination: new Uri($"{Fixture.Url()}/saml/logout"), sessionIndex: "session123"); - var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, CT.None); + var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe($"Service Provider '{sp.EntityId}' has no SingleLogoutServiceUrl configured"); } @@ -177,13 +179,13 @@ public class SamlSingleLogoutEndpointTests var logoutRequestXml = Build.LogoutRequestXml( destination: new Uri($"{Fixture.Url()}/saml/logout"), version: "1.0"); - var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, CT.None); + var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", _ct); // Assert - var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CT.None); + var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, _ct); logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.VersionMismatch); } @@ -202,13 +204,13 @@ public class SamlSingleLogoutEndpointTests destination: new Uri($"{Fixture.Url()}/saml/logout"), issueInstant: futureTime, sessionIndex: "session123"); - var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, CT.None); + var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", _ct); // Assert - var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CT.None); + var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, _ct); logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Requester); logoutResponse.StatusMessage.ShouldBe("Request IssueInstant is in the future"); } @@ -228,13 +230,13 @@ public class SamlSingleLogoutEndpointTests destination: new Uri($"{Fixture.Url()}/saml/logout"), issueInstant: oldTime, sessionIndex: "session123"); - var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, CT.None); + var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", _ct); // Assert - var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CT.None); + var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, _ct); logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Requester); logoutResponse.StatusMessage.ShouldBe("Request has expired (IssueInstant too old)"); } @@ -252,13 +254,13 @@ public class SamlSingleLogoutEndpointTests var logoutRequestXml = Build.LogoutRequestXml( destination: new Uri("https://wrong-destination.com/saml/logout"), sessionIndex: "session123"); - var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, CT.None); + var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", _ct); // Assert - var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CT.None); + var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, _ct); logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Requester); logoutResponse.StatusMessage.ShouldBe($"Invalid destination. Expected '{Fixture.Url()}/saml/logout'"); } @@ -276,14 +278,14 @@ public class SamlSingleLogoutEndpointTests var logoutRequestXml = Build.LogoutRequestXml( destination: new Uri($"{Fixture.Url()}/saml/logout"), sessionIndex: "session123"); - var urlEncoded = await EncodeRequest(logoutRequestXml, CT.None); + var urlEncoded = await EncodeRequest(logoutRequestXml, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.BadRequest); - var problemDetails = await result.Content.ReadFromJsonAsync(CT.None); + var problemDetails = await result.Content.ReadFromJsonAsync(_ct); problemDetails.ShouldNotBeNull(); problemDetails.Detail.ShouldBe($"Service Provider '{sp.EntityId}' has no signing certificates configured and has sent a SAML logout request which requires signature validation"); } @@ -302,13 +304,13 @@ public class SamlSingleLogoutEndpointTests var logoutRequestXml = Build.LogoutRequestXml( destination: new Uri($"{Fixture.Url()}/saml/logout"), sessionIndex: "session123"); - var urlEncoded = await EncodeRequest(logoutRequestXml, CT.None); + var urlEncoded = await EncodeRequest(logoutRequestXml, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", _ct); // Assert - var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CT.None); + var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, _ct); logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Requester); logoutResponse.StatusMessage.ShouldBe("Missing signature parameter"); } @@ -327,13 +329,13 @@ public class SamlSingleLogoutEndpointTests var logoutRequestXml = Build.LogoutRequestXml( notOnOrAfter: expiredTime, sessionIndex: "session123"); - var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, CT.None); + var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", _ct); // Assert - var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CT.None); + var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, _ct); logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Requester); logoutResponse.StatusMessage.ShouldBe("Logout request expired (NotOnOrAfter is in the past)"); } @@ -353,13 +355,13 @@ public class SamlSingleLogoutEndpointTests var logoutRequestXml = Build.LogoutRequestXml( destination: new Uri($"{Fixture.Url()}/saml/logout"), sessionIndex: "session123"); - var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, CT.None); + var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", _ct); // Assert - var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CT.None); + var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, _ct); logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Success); } @@ -379,19 +381,19 @@ public class SamlSingleLogoutEndpointTests // Sign in a user first Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); // Use a different service provider than what was established var logoutRequestXml = Build.LogoutRequestXml( issuer: anotherSp.EntityId, // Use a different SP so session will not be found destination: new Uri($"{Fixture.Url()}/saml/logout")); - var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, CT.None); + var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", _ct); // Assert - var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CT.None); + var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, _ct); logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Success); } @@ -408,19 +410,19 @@ public class SamlSingleLogoutEndpointTests // Sign in a user first Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); // Use a different session index than what was established var logoutRequestXml = Build.LogoutRequestXml( destination: new Uri($"{Fixture.Url()}/saml/logout"), sessionIndex: "wrong-session-index"); - var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, CT.None); + var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", _ct); // Assert - var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CT.None); + var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, _ct); logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Success); } @@ -437,7 +439,7 @@ public class SamlSingleLogoutEndpointTests // Sign in a user first Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); // Perform logout to get correct session index from the response var sessionIndex = await PerformSigninAndExtractSessionIndex(sp); @@ -445,10 +447,10 @@ public class SamlSingleLogoutEndpointTests var logoutRequestXml = Build.LogoutRequestXml( destination: new Uri($"{Fixture.Url()}/saml/logout"), sessionIndex: sessionIndex); - var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, CT.None); + var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.OK); @@ -472,10 +474,10 @@ public class SamlSingleLogoutEndpointTests // Sign in a user first Fixture.UserToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id")], "Test")); - await Fixture.Client.GetAsync("/__signin", CT.None); + await Fixture.Client.GetAsync("/__signin", _ct); // Ensure user can access protected resource - var initialProtectedResourceResult = await Fixture.Client.GetAsync("__protected-resource", CT.None); + var initialProtectedResourceResult = await Fixture.Client.GetAsync("__protected-resource", _ct); initialProtectedResourceResult.StatusCode.ShouldBe(HttpStatusCode.OK); var sessionIndex = await PerformSigninAndExtractSessionIndex(sp); @@ -483,16 +485,16 @@ public class SamlSingleLogoutEndpointTests var logoutRequestXml = Build.LogoutRequestXml( destination: new Uri($"{Fixture.Url()}/saml/logout"), sessionIndex: sessionIndex); - var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, CT.None); + var urlEncoded = await EncodeAndSignRequest(logoutRequestXml, sp, _ct); // Act - var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", CT.None); + var result = await Fixture.Client.GetAsync($"/saml/logout?SAMLRequest={urlEncoded}", _ct); // Assert result.StatusCode.ShouldBe(HttpStatusCode.OK); // Follows redirect // Verify user can no longer access protected resource and is redirected to login - var finalProtectedResourceResult = await Fixture.Client.GetAsync("__protected-resource", CT.None); + var finalProtectedResourceResult = await Fixture.Client.GetAsync("__protected-resource", _ct); finalProtectedResourceResult.StatusCode.ShouldBe(HttpStatusCode.OK); finalProtectedResourceResult.RequestMessage?.RequestUri?.AbsoluteUri.ShouldStartWith($"{Fixture.Url()}{Fixture.LoginUrl.ToString()}"); } @@ -500,7 +502,7 @@ public class SamlSingleLogoutEndpointTests private static async Task EncodeAndSignRequest( string xml, SamlServiceProvider sp, - CT ct = default) + Ct ct = default) { var encoded = await EncodeRequest(xml, ct); @@ -514,9 +516,9 @@ public class SamlSingleLogoutEndpointTests private async Task PerformSigninAndExtractSessionIndex(SamlServiceProvider samlServiceProvider) { var signinRequest = Build.AuthNRequestXml(); - var encoded = await EncodeAndSignRequest(signinRequest, samlServiceProvider, CT.None); - var signinResult = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={encoded}", CT.None); - var samlResult = await ExtractSamlSuccessFromPostAsync(signinResult, CT.None); + var encoded = await EncodeAndSignRequest(signinRequest, samlServiceProvider, _ct); + var signinResult = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={encoded}", _ct); + var samlResult = await ExtractSamlSuccessFromPostAsync(signinResult, _ct); if (string.IsNullOrWhiteSpace(samlResult.Assertion.AuthnStatement?.SessionIndex)) { throw new InvalidOperationException("SAMLResult did not have a valid session index"); diff --git a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlTestHelpers.cs b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlTestHelpers.cs index d7db0c4bd..a2c931a21 100644 --- a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlTestHelpers.cs +++ b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SamlTestHelpers.cs @@ -16,7 +16,7 @@ namespace Duende.IdentityServer.IntegrationTests.Endpoints.Saml; internal static class SamlTestHelpers { - public static async Task EncodeRequest(string authenticationRequest, CT ct = default) + public static async Task EncodeRequest(string authenticationRequest, Ct ct = default) { var bytes = Encoding.UTF8.GetBytes(authenticationRequest); using var outputStream = new MemoryStream(); @@ -37,7 +37,7 @@ internal static class SamlTestHelpers /// /// Extracts SAML error response from an HTTP-POST binding auto-submit form. /// - public static async Task ExtractSamlErrorFromPostAsync(HttpResponseMessage response, CT ct = default) + public static async Task ExtractSamlErrorFromPostAsync(HttpResponseMessage response, Ct ct = default) { var (responseXml, relayState, acsUrl) = await ExtractSamlResponse(response, ct); var (samlpNs, samlNs, responseElement) = ParseSamlResponseXml(responseXml); @@ -58,7 +58,7 @@ internal static class SamlTestHelpers }; } - public static async Task ExtractSamlLogoutResponseFromPostAsync(HttpResponseMessage response, CT ct = default) + public static async Task ExtractSamlLogoutResponseFromPostAsync(HttpResponseMessage response, Ct ct = default) { response.StatusCode.ShouldBe(HttpStatusCode.OK); @@ -81,7 +81,7 @@ internal static class SamlTestHelpers }; } - public static async Task ExtractSamlSuccessFromPostAsync(HttpResponseMessage response, CT ct = default) + public static async Task ExtractSamlSuccessFromPostAsync(HttpResponseMessage response, Ct ct = default) { var (responseXml, relayState, acsUrl) = await ExtractSamlResponse(response, ct); var (samlpNs, samlNs, responseElement) = ParseSamlResponseXml(responseXml); @@ -105,7 +105,7 @@ internal static class SamlTestHelpers }; } - public static async Task<(string responseXml, string? relayState, string acsUrl)> ExtractSamlResponse(HttpResponseMessage response, CT ct = default) + public static async Task<(string responseXml, string? relayState, string acsUrl)> ExtractSamlResponse(HttpResponseMessage response, Ct ct = default) { response.StatusCode.ShouldBe(HttpStatusCode.OK); response.Content.Headers.ContentType?.MediaType.ShouldBe("text/html"); @@ -821,7 +821,7 @@ internal static class SamlTestHelpers public static async Task ExtractAndDecryptSamlSuccessFromPostAsync( HttpResponseMessage response, X509Certificate2 decryptionCertificate, - CT ct = default) + Ct ct = default) { var (responseXml, relayState, acsUrl) = await ExtractSamlResponse(response, ct); var (samlpNs, samlNs, responseElement) = ParseSamlResponseXml(responseXml); diff --git a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SustainSysSamlTestFixture.cs b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SustainSysSamlTestFixture.cs index 513709138..5897de5dd 100644 --- a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SustainSysSamlTestFixture.cs +++ b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SustainSysSamlTestFixture.cs @@ -23,6 +23,8 @@ namespace Duende.IdentityServer.IntegrationTests.Endpoints.Saml; internal class SustainSysSamlTestFixture(ITestOutputHelper output) : IAsyncLifetime { + private readonly Ct _ct = TestContext.Current.CancellationToken; + public KestrelTestHost? IdpHost; public KestrelTestHost? SpHost; public HttpClient? BrowserClient; @@ -37,7 +39,7 @@ internal class SustainSysSamlTestFixture(ITestOutputHelper output) : IAsyncLifet { _userToSignIn = new ClaimsPrincipal(new ClaimsIdentity([new Claim(JwtClaimTypes.Subject, "user-id"), new Claim("name", "Test User"), new Claim(JwtClaimTypes.AuthenticationMethod, "urn:oasis:names:tc:SAML:2.0:ac:classes:Password")], "Test")); - await BrowserClient!.GetAsync($"{IdpHost!.Uri()}/__signin", CT.None); + await BrowserClient!.GetAsync($"{IdpHost!.Uri()}/__signin", _ct); } public void GenerateSigningCertificate() => @@ -132,7 +134,7 @@ internal class SustainSysSamlTestFixture(ITestOutputHelper output) : IAsyncLifet ctx.Response.StatusCode = 204; }); }, - CT.None); + _ct); } private async Task InitializeServiceProvider(string identityProviderHostUri, X509Certificate2? signingCertificate = null) => SpHost = await KestrelTestHost.Create(output, @@ -183,7 +185,7 @@ internal class SustainSysSamlTestFixture(ITestOutputHelper output) : IAsyncLifet await context.Response.WriteAsync(userId.Value, context.RequestAborted); }).RequireAuthorization(); }, - CT.None); + _ct); public async ValueTask DisposeAsync() { diff --git a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SustainSysSigninTests.cs b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SustainSysSigninTests.cs index 438729fdb..0e6d2764b 100644 --- a/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SustainSysSigninTests.cs +++ b/identity-server/test/IdentityServer.IntegrationTests/Endpoints/Saml/SustainSysSigninTests.cs @@ -11,6 +11,8 @@ public class SustainSysSigninTests(ITestOutputHelper output) { private const string Category = "SustainSys SAML signin"; + private readonly Ct _ct = TestContext.Current.CancellationToken; + private SustainSysSamlTestFixture Fixture = new(output); [Fact] @@ -91,14 +93,14 @@ public class SustainSysSigninTests(ITestOutputHelper output) // since HttpClient doesn't support JavaScript, we need to extra the content from the auto form post and manually // complete the callback to the Service Provider's ACS URL the same way a user in a browser with JavaScript disabled // would have to manually submit the form - var (samlResponse, relayState, acsUrl) = await ExtractSamlResponse(response, CT.None); + var (samlResponse, relayState, acsUrl) = await ExtractSamlResponse(response, _ct); var formData = new Dictionary { { "SAMLResponse", ConvertToBase64Encoded(samlResponse) } }; if (!string.IsNullOrEmpty(relayState)) { formData.Add("RelayState", HttpUtility.UrlEncode(relayState)); } using var formContent = new FormUrlEncodedContent(formData); - var acsResult = await Fixture.BrowserClient!.PostAsync(acsUrl, formContent, CT.None); + var acsResult = await Fixture.BrowserClient!.PostAsync(acsUrl, formContent, _ct); return acsResult; } diff --git a/identity-server/test/IdentityServer.UnitTests/Common/MockSamlLogoutNotificationService.cs b/identity-server/test/IdentityServer.UnitTests/Common/MockSamlLogoutNotificationService.cs index 90d3bc86c..b92d9c52a 100644 --- a/identity-server/test/IdentityServer.UnitTests/Common/MockSamlLogoutNotificationService.cs +++ b/identity-server/test/IdentityServer.UnitTests/Common/MockSamlLogoutNotificationService.cs @@ -11,7 +11,7 @@ public class MockSamlLogoutNotificationService : ISamlLogoutNotificationService public bool GetSamlFrontChannelLogoutsAsyncCalled { get; set; } public List SamlFrontChannelLogouts { get; set; } = []; - public Task> GetSamlFrontChannelLogoutsAsync(LogoutNotificationContext context) + public Task> GetSamlFrontChannelLogoutsAsync(LogoutNotificationContext context, Ct _) { GetSamlFrontChannelLogoutsAsyncCalled = true; return Task.FromResult(SamlFrontChannelLogouts.AsEnumerable()); diff --git a/identity-server/test/IdentityServer.UnitTests/Common/MockSamlSigningService.cs b/identity-server/test/IdentityServer.UnitTests/Common/MockSamlSigningService.cs index 2c31daf75..2cac83a16 100644 --- a/identity-server/test/IdentityServer.UnitTests/Common/MockSamlSigningService.cs +++ b/identity-server/test/IdentityServer.UnitTests/Common/MockSamlSigningService.cs @@ -15,9 +15,9 @@ internal class MockSamlSigningService : ISamlSigningService public MockSamlSigningService(X509Certificate2 certificate) => _certificate = certificate; - public Task GetSigningCertificateAsync() => Task.FromResult(_certificate); + public Task GetSigningCertificateAsync(Ct _) => Task.FromResult(_certificate); - public Task GetSigningCertificateBase64Async() + public Task GetSigningCertificateBase64Async(Ct _) { var certBytes = _certificate.Export(X509ContentType.Cert); return Task.FromResult(Convert.ToBase64String(certBytes)); diff --git a/identity-server/test/IdentityServer.UnitTests/Common/MockUserSession.cs b/identity-server/test/IdentityServer.UnitTests/Common/MockUserSession.cs index a9872da50..f0d4d26ae 100644 --- a/identity-server/test/IdentityServer.UnitTests/Common/MockUserSession.cs +++ b/identity-server/test/IdentityServer.UnitTests/Common/MockUserSession.cs @@ -55,16 +55,16 @@ public class MockUserSession : IUserSession return Task.CompletedTask; } - public Task AddSamlSessionAsync(SamlSpSessionData session) + public Task AddSamlSessionAsync(SamlSpSessionData session, Ct _) { SamlSessions.RemoveAll(s => s.EntityId == session.EntityId); SamlSessions.Add(session); return Task.CompletedTask; } - public Task> GetSamlSessionListAsync() => Task.FromResult>(SamlSessions); + public Task> GetSamlSessionListAsync(Ct _) => Task.FromResult>(SamlSessions); - public Task RemoveSamlSessionAsync(string entityId) + public Task RemoveSamlSessionAsync(string entityId, Ct _) { SamlSessions.RemoveAll(s => s.EntityId == entityId); return Task.CompletedTask; diff --git a/identity-server/test/IdentityServer.UnitTests/Saml/SamlClaimsServiceTests.cs b/identity-server/test/IdentityServer.UnitTests/Saml/SamlClaimsServiceTests.cs index f5878ef61..3444dfb65 100644 --- a/identity-server/test/IdentityServer.UnitTests/Saml/SamlClaimsServiceTests.cs +++ b/identity-server/test/IdentityServer.UnitTests/Saml/SamlClaimsServiceTests.cs @@ -16,6 +16,8 @@ public class SamlClaimsServiceTests { private const string Category = "SAML Claims Service"; + private readonly Ct _ct = TestContext.Current.CancellationToken; + private readonly SamlOptions _samlOptions; private readonly IOptions _options; private readonly MockProfileService _profileService; @@ -51,7 +53,7 @@ public class SamlClaimsServiceTests _profileService.ProfileClaims = user.Claims.ToList(); // Act - var attributes = (await _service.GetMappedAttributesAsync(user, sp)).ToList(); + var attributes = (await _service.GetMappedAttributesAsync(user, sp, _ct)).ToList(); // Assert attributes.Count.ShouldBe(3); @@ -105,7 +107,7 @@ public class SamlClaimsServiceTests _profileService.ProfileClaims = user.Claims.ToList(); // Act - var attributes = (await service.GetMappedAttributesAsync(user, sp)).ToList(); + var attributes = (await service.GetMappedAttributesAsync(user, sp, _ct)).ToList(); // Assert attributes.Count.ShouldBe(4); @@ -140,7 +142,7 @@ public class SamlClaimsServiceTests _profileService.ProfileClaims = user.Claims.ToList(); // Act - var attributes = (await service.GetMappedAttributesAsync(user, sp)).ToList(); + var attributes = (await service.GetMappedAttributesAsync(user, sp, _ct)).ToList(); // Assert attributes.Count.ShouldBe(0); // No mappings, so no attributes @@ -180,7 +182,7 @@ public class SamlClaimsServiceTests _profileService.ProfileClaims = user.Claims.ToList(); // Act - var attributes = (await service.GetMappedAttributesAsync(user, sp)).ToList(); + var attributes = (await service.GetMappedAttributesAsync(user, sp, _ct)).ToList(); // Assert attributes.Count.ShouldBe(2); // Only email and department are mapped; sub and unmapped are excluded @@ -217,7 +219,7 @@ public class SamlClaimsServiceTests _profileService.ProfileClaims = user.Claims.ToList(); // Act - var attributes = (await _service.GetMappedAttributesAsync(user, sp)).ToList(); + var attributes = (await _service.GetMappedAttributesAsync(user, sp, _ct)).ToList(); // Assert attributes.Count.ShouldBe(2); // email and department from SP mappings; sub not mapped @@ -251,7 +253,7 @@ public class SamlClaimsServiceTests _profileService.ProfileClaims = user.Claims.ToList(); // Act - var attributes = (await _service.GetMappedAttributesAsync(user, sp)).ToList(); + var attributes = (await _service.GetMappedAttributesAsync(user, sp, _ct)).ToList(); // Assert attributes.Count.ShouldBe(1); // Only email is mapped (overridden by SP); sub and given_name are not in defaults @@ -292,7 +294,7 @@ public class SamlClaimsServiceTests _profileService.ProfileClaims = user.Claims.ToList(); // Act - var attributes = (await service.GetMappedAttributesAsync(user, sp)).ToList(); + var attributes = (await service.GetMappedAttributesAsync(user, sp, _ct)).ToList(); // Assert attributes.Count.ShouldBe(2); // sub + role (multi-valued) @@ -330,7 +332,7 @@ public class SamlClaimsServiceTests _profileService.ProfileClaims = user.Claims.ToList(); // Act - var attributes = (await service.GetMappedAttributesAsync(user, sp)).ToList(); + var attributes = (await service.GetMappedAttributesAsync(user, sp, _ct)).ToList(); // Assert attributes.Count.ShouldBe(1); @@ -360,7 +362,7 @@ public class SamlClaimsServiceTests _profileService.ProfileClaims = user.Claims.ToList(); // Act - var attributes = (await _service.GetMappedAttributesAsync(user, sp)).ToList(); + var attributes = (await _service.GetMappedAttributesAsync(user, sp, _ct)).ToList(); // Assert attributes.ShouldAllBe(a => a.NameFormat == _samlOptions.DefaultAttributeNameFormat); diff --git a/identity-server/test/IdentityServer.UnitTests/Saml/SamlFrontChannelLogoutRequestBuilderTests.cs b/identity-server/test/IdentityServer.UnitTests/Saml/SamlFrontChannelLogoutRequestBuilderTests.cs index f427c5b18..d45406711 100644 --- a/identity-server/test/IdentityServer.UnitTests/Saml/SamlFrontChannelLogoutRequestBuilderTests.cs +++ b/identity-server/test/IdentityServer.UnitTests/Saml/SamlFrontChannelLogoutRequestBuilderTests.cs @@ -19,6 +19,8 @@ public class SamlFrontChannelLogoutRequestBuilderTests { private const string Category = "SAML Front Channel Logout Request Builder"; + private readonly Ct _ct = TestContext.Current.CancellationToken; + private readonly FakeTimeProvider _timeProvider; private readonly SamlProtocolMessageSigner _signer; private readonly SamlFrontChannelLogoutRequestBuilder _subject; @@ -38,7 +40,7 @@ public class SamlFrontChannelLogoutRequestBuilderTests sp.SingleLogoutServiceUrl = null; await Should.ThrowAsync(async () => - await _subject.BuildLogoutRequestAsync(sp, "user@example.com", null, "session123", "https://idp.example.com") + await _subject.BuildLogoutRequestAsync(sp, "user@example.com", null, "session123", "https://idp.example.com", _ct) ); } @@ -53,7 +55,8 @@ public class SamlFrontChannelLogoutRequestBuilderTests "user@example.com", "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "session123", - "https://idp.example.com"); + "https://idp.example.com", + _ct); result.SamlBinding.ShouldBe(SamlBinding.HttpRedirect); result.Destination.ShouldBe(sp.SingleLogoutServiceUrl!.Location); @@ -75,7 +78,8 @@ public class SamlFrontChannelLogoutRequestBuilderTests "user@example.com", "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "session123", - "https://idp.example.com"); + "https://idp.example.com", + _ct); result.SamlBinding.ShouldBe(SamlBinding.HttpPost); } @@ -92,7 +96,7 @@ public class SamlFrontChannelLogoutRequestBuilderTests }; await Should.ThrowAsync(async () => - await _subject.BuildLogoutRequestAsync(sp, "user@example.com", null, "session123", "https://idp.example.com") + await _subject.BuildLogoutRequestAsync(sp, "user@example.com", null, "session123", "https://idp.example.com", _ct) ); } @@ -107,7 +111,8 @@ public class SamlFrontChannelLogoutRequestBuilderTests "user@example.com", "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "session123", - "https://idp.example.com"); + "https://idp.example.com", + _ct); result.EncodedContent.ShouldNotBeNullOrEmpty(); result.EncodedContent.ShouldContain("SAMLRequest="); @@ -126,7 +131,8 @@ public class SamlFrontChannelLogoutRequestBuilderTests "user@example.com", "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "session123", - "https://idp.example.com"); + "https://idp.example.com", + _ct); var queryString = result.EncodedContent; var samlRequestPart = queryString.Split('&')[0].Replace("?SAMLRequest=", ""); @@ -158,7 +164,8 @@ public class SamlFrontChannelLogoutRequestBuilderTests "user@example.com", "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "session123", - "https://idp.example.com"); + "https://idp.example.com", + _ct); result.EncodedContent.ShouldNotBeNullOrEmpty(); @@ -181,7 +188,8 @@ public class SamlFrontChannelLogoutRequestBuilderTests "user@example.com", null, "session123", - "https://idp.example.com"); + "https://idp.example.com", + _ct); var xml = await DecodeRedirectRequest(result.EncodedContent); var expectedIssueInstant = expectedTime.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture); @@ -199,7 +207,8 @@ public class SamlFrontChannelLogoutRequestBuilderTests "user@example.com", null, "session123", - "https://idp.example.com"); + "https://idp.example.com", + _ct); var xml = await DecodeRedirectRequest(result.EncodedContent); xml.ShouldContain($"Destination=\"{sp.SingleLogoutServiceUrl!.Location}\""); @@ -217,7 +226,8 @@ public class SamlFrontChannelLogoutRequestBuilderTests "user@example.com", null, "session123", - issuer); + issuer, + _ct); var xml = await DecodeRedirectRequest(result.EncodedContent); xml.ShouldContain($"{issuer}"); @@ -235,7 +245,8 @@ public class SamlFrontChannelLogoutRequestBuilderTests nameId, null, "session123", - "https://idp.example.com"); + "https://idp.example.com", + _ct); var xml = await DecodeRedirectRequest(result.EncodedContent); xml.ShouldContain($"{nameId}"); @@ -253,7 +264,8 @@ public class SamlFrontChannelLogoutRequestBuilderTests "user@example.com", nameIdFormat, "session123", - "https://idp.example.com"); + "https://idp.example.com", + _ct); var xml = await DecodeRedirectRequest(result.EncodedContent); xml.ShouldContain($"Format=\"{nameIdFormat}\""); @@ -270,7 +282,8 @@ public class SamlFrontChannelLogoutRequestBuilderTests "user@example.com", null, "session123", - "https://idp.example.com"); + "https://idp.example.com", + _ct); var xml = await DecodeRedirectRequest(result.EncodedContent); var doc = XDocument.Parse(xml); @@ -290,7 +303,8 @@ public class SamlFrontChannelLogoutRequestBuilderTests "user@example.com", null, sessionIndex, - "https://idp.example.com"); + "https://idp.example.com", + _ct); var xml = await DecodeRedirectRequest(result.EncodedContent); xml.ShouldContain($"{sessionIndex}"); @@ -302,8 +316,8 @@ public class SamlFrontChannelLogoutRequestBuilderTests { var sp = CreateServiceProvider(); - var result1 = await _subject.BuildLogoutRequestAsync(sp, "user@example.com", null, "session123", "https://idp.example.com"); - var result2 = await _subject.BuildLogoutRequestAsync(sp, "user@example.com", null, "session123", "https://idp.example.com"); + var result1 = await _subject.BuildLogoutRequestAsync(sp, "user@example.com", null, "session123", "https://idp.example.com", _ct); + var result2 = await _subject.BuildLogoutRequestAsync(sp, "user@example.com", null, "session123", "https://idp.example.com", _ct); var xml1 = await DecodeRedirectRequest(result1.EncodedContent); var xml2 = await DecodeRedirectRequest(result2.EncodedContent); @@ -327,7 +341,8 @@ public class SamlFrontChannelLogoutRequestBuilderTests "user@example.com", null, "session123", - "https://idp.example.com"); + "https://idp.example.com", + _ct); var xml = await DecodeRedirectRequest(result.EncodedContent); xml.ShouldContain("Version=\"2.0\""); diff --git a/identity-server/test/IdentityServer.UnitTests/Saml/SamlLogoutCallbackProcessorTests.cs b/identity-server/test/IdentityServer.UnitTests/Saml/SamlLogoutCallbackProcessorTests.cs index b97720a7a..2df8144af 100644 --- a/identity-server/test/IdentityServer.UnitTests/Saml/SamlLogoutCallbackProcessorTests.cs +++ b/identity-server/test/IdentityServer.UnitTests/Saml/SamlLogoutCallbackProcessorTests.cs @@ -16,6 +16,8 @@ public class SamlLogoutCallbackProcessorTests { private const string Category = "SAML Logout Callback Processor"; + private readonly Ct _ct = TestContext.Current.CancellationToken; + private readonly UnitTests.Common.MockMessageStore _logoutMessageStore = new(); private readonly MockServiceProviderStore _serviceProviderStore = new(); private readonly LogoutResponseBuilder _logoutResponseBuilder; @@ -38,7 +40,7 @@ public class SamlLogoutCallbackProcessorTests [Trait("Category", Category)] public async Task invalid_logout_id_should_return_error() { - var result = await _subject.ProcessAsync("invalid", CT.None); + var result = await _subject.ProcessAsync("invalid", _ct); result.Success.ShouldBeFalse(); result.Error.Message.ShouldContain("No logout message found"); @@ -56,7 +58,7 @@ public class SamlLogoutCallbackProcessorTests }; _logoutMessageStore.Messages["logoutId123"] = new Message(logoutMessage, DateTimeOffset.UtcNow.UtcDateTime); - var result = await _subject.ProcessAsync("logoutId123", CT.None); + var result = await _subject.ProcessAsync("logoutId123", _ct); result.Success.ShouldBeFalse(); result.Error.Message.ShouldContain("does not contain SAML SP entity ID"); @@ -75,7 +77,7 @@ public class SamlLogoutCallbackProcessorTests }; _logoutMessageStore.Messages["logoutId123"] = new Message(logoutMessage, DateTimeOffset.UtcNow.UtcDateTime); - var result = await _subject.ProcessAsync("logoutId123", CT.None); + var result = await _subject.ProcessAsync("logoutId123", _ct); result.Success.ShouldBeFalse(); result.Error.Message.ShouldContain("Service Provider not found"); @@ -97,7 +99,7 @@ public class SamlLogoutCallbackProcessorTests }; _logoutMessageStore.Messages["logoutId123"] = new Message(logoutMessage, DateTimeOffset.UtcNow.UtcDateTime); - var result = await _subject.ProcessAsync("logoutId123", CT.None); + var result = await _subject.ProcessAsync("logoutId123", _ct); result.Success.ShouldBeFalse(); result.Error.Message.ShouldContain("is disabled"); @@ -119,7 +121,7 @@ public class SamlLogoutCallbackProcessorTests }; _logoutMessageStore.Messages["logoutId123"] = new Message(logoutMessage, DateTimeOffset.UtcNow.UtcDateTime); - var result = await _subject.ProcessAsync("logoutId123", CT.None); + var result = await _subject.ProcessAsync("logoutId123", _ct); result.Success.ShouldBeFalse(); result.Error.Message.ShouldContain("has no SingleLogoutServiceUrl"); @@ -140,7 +142,7 @@ public class SamlLogoutCallbackProcessorTests }; _logoutMessageStore.Messages["logoutId123"] = new Message(logoutMessage, DateTimeOffset.UtcNow.UtcDateTime); - var result = await _subject.ProcessAsync("logoutId123", CT.None); + var result = await _subject.ProcessAsync("logoutId123", _ct); result.Success.ShouldBeFalse(); result.Error.Message.ShouldContain("does not contain SAML logout request ID"); @@ -162,7 +164,7 @@ public class SamlLogoutCallbackProcessorTests }; _logoutMessageStore.Messages["logoutId123"] = new Message(logoutMessage, DateTimeOffset.UtcNow.UtcDateTime); - var result = await _subject.ProcessAsync("logoutId123", CT.None); + var result = await _subject.ProcessAsync("logoutId123", _ct); result.Success.ShouldBeTrue(); var logoutResponse = result.Value; @@ -187,7 +189,7 @@ public class SamlLogoutCallbackProcessorTests }; _logoutMessageStore.Messages["logoutId123"] = new Message(logoutMessage, DateTimeOffset.UtcNow.UtcDateTime); - var result = await _subject.ProcessAsync("logoutId123", CT.None); + var result = await _subject.ProcessAsync("logoutId123", _ct); result.Success.ShouldBeTrue(); var logoutResponse = result.Value; @@ -211,7 +213,7 @@ public class SamlLogoutCallbackProcessorTests }; _logoutMessageStore.Messages["logoutId123"] = new Message(logoutMessage, DateTimeOffset.UtcNow.UtcDateTime); - var result = await _subject.ProcessAsync("logoutId123", CT.None); + var result = await _subject.ProcessAsync("logoutId123", _ct); result.Success.ShouldBeTrue(); result.Value.RelayState.ShouldBeNull(); @@ -232,7 +234,7 @@ public class SamlLogoutCallbackProcessorTests }; _logoutMessageStore.Messages["logoutId123"] = new Message(logoutMessage, DateTimeOffset.UtcNow.UtcDateTime); - var result = await _subject.ProcessAsync("logoutId123", CT.None); + var result = await _subject.ProcessAsync("logoutId123", _ct); result.Success.ShouldBeTrue(); result.Value.Issuer.ShouldBe("https://idp.example.com"); @@ -255,7 +257,7 @@ public class SamlLogoutCallbackProcessorTests { public Dictionary ServiceProviders { get; } = []; - public Task FindByEntityIdAsync(string entityId) + public Task FindByEntityIdAsync(string entityId, Ct _) { ServiceProviders.TryGetValue(entityId, out var sp); return Task.FromResult(sp); @@ -266,6 +268,6 @@ public class SamlLogoutCallbackProcessorTests { public string IssuerName { get; set; } = "https://idp.example.com"; - public Task GetCurrentAsync() => Task.FromResult(IssuerName); + public Task GetCurrentAsync(Ct _) => Task.FromResult(IssuerName); } } diff --git a/identity-server/test/IdentityServer.UnitTests/Saml/SamlLogoutNotificationServiceTests.cs b/identity-server/test/IdentityServer.UnitTests/Saml/SamlLogoutNotificationServiceTests.cs index 7d84d658d..c8a27d524 100644 --- a/identity-server/test/IdentityServer.UnitTests/Saml/SamlLogoutNotificationServiceTests.cs +++ b/identity-server/test/IdentityServer.UnitTests/Saml/SamlLogoutNotificationServiceTests.cs @@ -20,6 +20,8 @@ public class SamlLogoutNotificationServiceTests { private const string Category = "SAML Logout Notification Service"; + private readonly Ct _ct = TestContext.Current.CancellationToken; + private readonly MockUserSession _userSession = new(); private readonly TestIssuerNameService _issuerNameService = new(); @@ -70,7 +72,7 @@ public class SamlLogoutNotificationServiceTests }; var subject = CreateSubject(); - var result = await subject.GetSamlFrontChannelLogoutsAsync(context); + var result = await subject.GetSamlFrontChannelLogoutsAsync(context, _ct); result.ShouldBeEmpty(); } @@ -95,7 +97,7 @@ public class SamlLogoutNotificationServiceTests }; var subject = CreateSubject(); - var result = await subject.GetSamlFrontChannelLogoutsAsync(context); + var result = await subject.GetSamlFrontChannelLogoutsAsync(context, _ct); result.ShouldBeEmpty(); } @@ -121,7 +123,7 @@ public class SamlLogoutNotificationServiceTests }; var subject = CreateSubject(sp); - var result = await subject.GetSamlFrontChannelLogoutsAsync(context); + var result = await subject.GetSamlFrontChannelLogoutsAsync(context, _ct); result.ShouldBeEmpty(); } @@ -147,7 +149,7 @@ public class SamlLogoutNotificationServiceTests }; var subject = CreateSubject(sp); - var result = await subject.GetSamlFrontChannelLogoutsAsync(context); + var result = await subject.GetSamlFrontChannelLogoutsAsync(context, _ct); result.ShouldBeEmpty(); } @@ -172,7 +174,7 @@ public class SamlLogoutNotificationServiceTests }; var subject = CreateSubject(sp); - var result = await subject.GetSamlFrontChannelLogoutsAsync(context); + var result = await subject.GetSamlFrontChannelLogoutsAsync(context, _ct); result.ShouldHaveSingleItem(); } @@ -205,7 +207,7 @@ public class SamlLogoutNotificationServiceTests }; var subject = CreateSubject(sp1, sp2); - var result = await subject.GetSamlFrontChannelLogoutsAsync(context); + var result = await subject.GetSamlFrontChannelLogoutsAsync(context, _ct); result.Count().ShouldBe(2); } @@ -236,7 +238,7 @@ public class SamlLogoutNotificationServiceTests var subject = CreateSubject(sp); - var result = await subject.GetSamlFrontChannelLogoutsAsync(context); + var result = await subject.GetSamlFrontChannelLogoutsAsync(context, _ct); result.ShouldHaveSingleItem(); } @@ -274,7 +276,7 @@ public class SamlLogoutNotificationServiceTests var subject = CreateSubject(sp1, sp2); - var result = await subject.GetSamlFrontChannelLogoutsAsync(context); + var result = await subject.GetSamlFrontChannelLogoutsAsync(context, _ct); result.Count().ShouldBe(2); } diff --git a/identity-server/test/IdentityServer.UnitTests/Saml/SamlProtocolMessageSignerTests.cs b/identity-server/test/IdentityServer.UnitTests/Saml/SamlProtocolMessageSignerTests.cs index 03d1e546a..b07f09a7b 100644 --- a/identity-server/test/IdentityServer.UnitTests/Saml/SamlProtocolMessageSignerTests.cs +++ b/identity-server/test/IdentityServer.UnitTests/Saml/SamlProtocolMessageSignerTests.cs @@ -17,6 +17,8 @@ public class SamlProtocolMessageSignerTests { private const string Category = "SAML Protocol Message Signer"; + private readonly Ct _ct = TestContext.Current.CancellationToken; + private readonly SamlServiceProvider _samlServiceProvider = new SamlServiceProvider { EntityId = "https://sp.example.com", @@ -75,7 +77,7 @@ public class SamlProtocolMessageSignerTests var signer = CreateSigner(); var logoutResponse = CreateLogoutResponseElement(); - var signedXml = await signer.SignProtocolMessage(logoutResponse, _samlServiceProvider); + var signedXml = await signer.SignProtocolMessage(logoutResponse, _samlServiceProvider, _ct); signedXml.ShouldContain("Signature"); signedXml.ShouldContain("SignatureValue"); @@ -89,7 +91,7 @@ public class SamlProtocolMessageSignerTests var signer = CreateSigner(); var logoutResponse = CreateLogoutResponseElement(); - var signedXml = await signer.SignProtocolMessage(logoutResponse, _samlServiceProvider); + var signedXml = await signer.SignProtocolMessage(logoutResponse, _samlServiceProvider, _ct); var indexOfIssuer = signedXml.IndexOf("( - async () => await _signingService.GetSigningCertificateAsync()); + async () => await _signingService.GetSigningCertificateAsync(_ct)); ex.Message.ShouldBe("Signing credential must be an X509 certificate with private key."); } @@ -98,7 +100,7 @@ public class SamlSigningServiceTests // Act & Assert var ex = await Should.ThrowAsync( - async () => await _signingService.GetSigningCertificateAsync()); + async () => await _signingService.GetSigningCertificateAsync(_ct)); ex.Message.ShouldBe("Signing certificate must have a private key."); } @@ -111,7 +113,7 @@ public class SamlSigningServiceTests // Act & Assert var ex = await Should.ThrowAsync( - async () => await _signingService.GetSigningCertificateAsync()); + async () => await _signingService.GetSigningCertificateAsync(_ct)); ex.Message.ShouldBe("No signing credential available. Configure a signing certificate."); } @@ -126,7 +128,7 @@ public class SamlSigningServiceTests _mockKeyMaterialService.SigningCredentials.Add(credentials); // Act - var result = await _signingService.GetSigningCertificateBase64Async(); + var result = await _signingService.GetSigningCertificateBase64Async(_ct); // Assert result.ShouldNotBeNullOrEmpty(); @@ -150,7 +152,7 @@ public class SamlSigningServiceTests // Act & Assert var ex = await Should.ThrowAsync( - async () => await _signingService.GetSigningCertificateBase64Async()); + async () => await _signingService.GetSigningCertificateBase64Async(_ct)); ex.Message.ShouldBe("Signing credential key is not an X509SecurityKey and cannot be used to extract an X509 certificate for SAML metadata."); } @@ -163,7 +165,7 @@ public class SamlSigningServiceTests // Act & Assert var ex = await Should.ThrowAsync( - async () => await _signingService.GetSigningCertificateBase64Async()); + async () => await _signingService.GetSigningCertificateBase64Async(_ct)); ex.Message.ShouldBe("No signing credential available. Configure a signing certificate."); } @@ -178,7 +180,7 @@ public class SamlSigningServiceTests _mockKeyMaterialService.SigningCredentials.Add(credentials); // Act - var result = await _signingService.GetSigningCertificateBase64Async(); + var result = await _signingService.GetSigningCertificateBase64Async(_ct); var bytes = Convert.FromBase64String(result); var exportedCert = X509CertificateLoader.LoadCertificate(bytes); diff --git a/identity-server/test/IdentityServer.UnitTests/Services/Default/DefaultIdentityServerInteractionServiceTests.cs b/identity-server/test/IdentityServer.UnitTests/Services/Default/DefaultIdentityServerInteractionServiceTests.cs index 7ceba035a..9fe6a01ac 100644 --- a/identity-server/test/IdentityServer.UnitTests/Services/Default/DefaultIdentityServerInteractionServiceTests.cs +++ b/identity-server/test/IdentityServer.UnitTests/Services/Default/DefaultIdentityServerInteractionServiceTests.cs @@ -188,7 +188,7 @@ public class DefaultIdentityServerInteractionServiceTests NameId = "user123" }); - var context = await _subject.CreateLogoutContextAsync(); + var context = await _subject.CreateLogoutContextAsync(_ct); context.ShouldNotBeNull(); _mockLogoutMessageStore.Messages.ShouldNotBeEmpty(); @@ -218,7 +218,7 @@ public class DefaultIdentityServerInteractionServiceTests NameId = "user123" }); - var context = await _subject.CreateLogoutContextAsync(); + var context = await _subject.CreateLogoutContextAsync(_ct); context.ShouldNotBeNull(); _mockLogoutMessageStore.Messages.ShouldNotBeEmpty(); @@ -242,7 +242,7 @@ public class DefaultIdentityServerInteractionServiceTests _mockUserSession.SessionId = "session"; _mockUserSession.Clients.Add("client1"); - var context = await _subject.CreateLogoutContextAsync(); + var context = await _subject.CreateLogoutContextAsync(_ct); context.ShouldNotBeNull(); _mockLogoutMessageStore.Messages.ShouldNotBeEmpty(); diff --git a/identity-server/test/IdentityServer.UnitTests/Validation/EndSessionRequestValidation/EndSessionRequestValidatorTests.cs b/identity-server/test/IdentityServer.UnitTests/Validation/EndSessionRequestValidation/EndSessionRequestValidatorTests.cs index 5a51b8d7f..d55637d40 100644 --- a/identity-server/test/IdentityServer.UnitTests/Validation/EndSessionRequestValidation/EndSessionRequestValidatorTests.cs +++ b/identity-server/test/IdentityServer.UnitTests/Validation/EndSessionRequestValidation/EndSessionRequestValidatorTests.cs @@ -195,7 +195,7 @@ public class EndSessionRequestValidatorTests var parameters = new NameValueCollection(); - var result = await _subject.ValidateAsync(parameters, _user); + var result = await _subject.ValidateAsync(parameters, _user, _ct); result.IsError.ShouldBeFalse(); result.ValidatedRequest.SamlSessions.ShouldNotBeNull(); @@ -212,7 +212,7 @@ public class EndSessionRequestValidatorTests var parameters = new NameValueCollection(); - var result = await _subject.ValidateAsync(parameters, _user); + var result = await _subject.ValidateAsync(parameters, _user, _ct); result.IsError.ShouldBeFalse(); result.ValidatedRequest.SamlSessions.ShouldNotBeNull(); @@ -232,7 +232,7 @@ public class EndSessionRequestValidatorTests var parameters = new NameValueCollection(); - var result = await _subject.ValidateAsync(parameters, _user); + var result = await _subject.ValidateAsync(parameters, _user, _ct); result.IsError.ShouldBeFalse(); @@ -267,7 +267,7 @@ public class EndSessionRequestValidatorTests var parameters = new NameValueCollection(); parameters.Add("id_token_hint", "id_token"); - var result = await _subject.ValidateAsync(parameters, _user); + var result = await _subject.ValidateAsync(parameters, _user, _ct); result.IsError.ShouldBeFalse(); result.ValidatedRequest.SamlSessions.ShouldNotBeNull(); @@ -303,7 +303,7 @@ public class EndSessionRequestValidatorTests { "endSessionId", "endSessionId123" } }; - var result = await _subject.ValidateCallbackAsync(parameters); + var result = await _subject.ValidateCallbackAsync(parameters, _ct); result.IsError.ShouldBeFalse(); result.SamlFrontChannelLogouts.ShouldNotBeNull(); @@ -340,7 +340,7 @@ public class EndSessionRequestValidatorTests { "endSessionId", "endSessionId123" } }; - var result = await _subject.ValidateCallbackAsync(parameters); + var result = await _subject.ValidateCallbackAsync(parameters, _ct); result.IsError.ShouldBeFalse(); result.FrontChannelLogoutUrls.ShouldHaveSingleItem(); @@ -364,7 +364,7 @@ public class EndSessionRequestValidatorTests { "endSessionId", "endSessionId123" } }; - var result = await _subject.ValidateCallbackAsync(parameters); + var result = await _subject.ValidateCallbackAsync(parameters, _ct); result.IsError.ShouldBeTrue(); } @@ -403,7 +403,7 @@ public class EndSessionRequestValidatorTests { "endSessionId", "endSessionId123" } }; - await _subject.ValidateCallbackAsync(parameters); + await _subject.ValidateCallbackAsync(parameters, _ct); _mockSamlLogoutNotificationService.GetSamlFrontChannelLogoutsAsyncCalled.ShouldBeTrue(); } @@ -451,7 +451,7 @@ public class EndSessionRequestValidatorTests { "endSessionId", "endSessionId123" } }; - var result = await _subject.ValidateCallbackAsync(parameters); + var result = await _subject.ValidateCallbackAsync(parameters, _ct); result.IsError.ShouldBeFalse(); result.SamlFrontChannelLogouts.Count().ShouldBe(3);