Eliminated odd value types to remain more consistent with code base

This commit is contained in:
Brett Hazen 2026-02-20 12:06:53 -06:00
parent c0069b8c89
commit 64bda6c985
40 changed files with 222 additions and 303 deletions

View file

@ -118,7 +118,7 @@ internal class EndSessionCallbackHttpWriter : IHttpResponseWriter<EndSessionCall
switch (samlFrontChannelLogout.SamlBinding)
{
case SamlBinding.HttpPost:
var autoPostFormContent = HttpResponseBindings.GenerateAutoPostForm(SamlMessageName.SamlRequest, samlFrontChannelLogout.EncodedContent, samlFrontChannelLogout.Destination, samlFrontChannelLogout.RelayState, includeCsp: true);
var autoPostFormContent = HttpResponseBindings.GenerateAutoPostForm(SamlConstants.RequestProperties.SAMLRequest, samlFrontChannelLogout.EncodedContent, samlFrontChannelLogout.Destination, samlFrontChannelLogout.RelayState, includeCsp: true);
sb.Append(CultureInfo.InvariantCulture, $"<iframe sandbox='allow-forms allow-scripts allow-same-origin' srcdoc='{HtmlEncoder.Default.Encode(autoPostFormContent)}'></iframe>");
break;
case SamlBinding.HttpRedirect:

View file

@ -8,7 +8,7 @@ namespace Duende.IdentityServer.Internal.Saml.Infrastructure;
internal static class HttpResponseBindings
{
internal static string GenerateAutoPostForm(SamlMessageName messageName, string encodedMessage, Uri destination, string? relayState, bool includeCsp = false)
internal static string GenerateAutoPostForm(string messageName, string encodedMessage, Uri destination, string? relayState, bool includeCsp = false)
{
var relayStateField = relayState == null
? string.Empty
@ -30,7 +30,7 @@ internal static class HttpResponseBindings
<p><strong>Note:</strong> Since your browser does not support JavaScript, you must press the button below to proceed.</p>
</noscript>
<form method=""post"" action=""{HtmlEncoder.Default.Encode(destination.ToString())}"">
<input type=""hidden"" name=""{messageName.Value}"" value=""{HtmlEncoder.Default.Encode(encodedMessage)}"" />
<input type=""hidden"" name=""{messageName}"" value=""{HtmlEncoder.Default.Encode(encodedMessage)}"" />
{relayStateField}
<noscript>
<input type=""submit"" value=""Continue"" />

View file

@ -13,7 +13,7 @@ internal interface ISamlRequest
{
internal static abstract string MessageName { get; }
internal string Issuer { get; }
internal SamlVersion Version { get; }
internal string Version { get; }
internal DateTime IssueInstant { get; }
internal Uri? Destination { get; }
}

View file

@ -9,7 +9,6 @@ using System.Xml.Linq;
using Duende.IdentityServer.Endpoints.Results;
using Duende.IdentityServer.Hosting;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Saml.Models;
using Microsoft.AspNetCore.Http;
namespace Duende.IdentityServer.Internal.Saml.Infrastructure;
@ -27,7 +26,7 @@ internal class SamlErrorResponse : EndpointResult<SamlErrorResponse>
/// <summary>
/// Gets the SAML status code for the error.
/// </summary>
public required SamlStatusCode StatusCode { get; init; }
public required string StatusCode { get; init; }
/// <summary>
/// Gets the human-readable error message.
@ -57,7 +56,7 @@ internal class SamlErrorResponse : EndpointResult<SamlErrorResponse>
/// <summary>
/// Gets an optional secondary status code for more specific error information.
/// </summary>
public SamlStatusCode? SubStatusCode { get; init; }
public string? SubStatusCode { get; init; }
/// <summary>
/// Gets or sets the Service Provider where the response will be sent.
@ -88,7 +87,7 @@ internal class SamlErrorResponse : EndpointResult<SamlErrorResponse>
var encodedResponse = Convert.ToBase64String(Encoding.UTF8.GetBytes(stringWriter.ToString()));
// Generate HTML form that auto-submits to the ACS URL
var html = HttpResponseBindings.GenerateAutoPostForm(SamlMessageName.SamlResponse, encodedResponse, result.AssertionConsumerServiceUrl,
var html = HttpResponseBindings.GenerateAutoPostForm(SamlConstants.RequestProperties.SAMLResponse, encodedResponse, result.AssertionConsumerServiceUrl,
result.RelayState);
httpContext.Response.ContentType = "text/html";

View file

@ -11,7 +11,7 @@ internal class SamlErrorResponseXmlSerializer : ISamlResultSerializer<SamlErrorR
{
public XElement Serialize(SamlErrorResponse result)
{
var responseId = ResponseId.New().ToString();
var responseId = SamlIds.NewResponseId();
var issueInstant = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture);
var protocolNs = XNamespace.Get(SamlConstants.Namespaces.Protocol);
@ -19,14 +19,14 @@ internal class SamlErrorResponseXmlSerializer : ISamlResultSerializer<SamlErrorR
// Build Status element
var statusCodeElement = new XElement(protocolNs + "StatusCode",
new XAttribute("Value", result.StatusCode.ToString()));
new XAttribute("Value", result.StatusCode));
// Add sub-status code if provided
if (result.SubStatusCode?.Value != null)
if (result.SubStatusCode != null)
{
statusCodeElement.Add(
new XElement(protocolNs + "StatusCode",
new XAttribute("Value", result.SubStatusCode.Value.ToString())));
new XAttribute("Value", result.SubStatusCode)));
}
var statusElement = new XElement(protocolNs + "Status",

View file

@ -122,7 +122,7 @@ internal abstract class SamlRequestProcessorBase<TMessage, TRequest, TSuccess>(
Type = SamlRequestErrorType.Protocol,
ProtocolError = new SamlProtocolError<TRequest>(sp, request, new SamlError
{
StatusCode = SamlStatusCode.Requester,
StatusCode = SamlStatusCodes.Requester,
Message = $"Unsupported binding for signature validation: {request.Binding}"
})
};

View file

@ -37,17 +37,17 @@ internal class SamlRequestSignatureValidator<TRequest, TSamlRequest>(TimeProvide
if (string.IsNullOrEmpty(signature))
{
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCode.Requester, Message = "Missing signature parameter" });
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCodes.Requester, Message = "Missing signature parameter" });
}
if (string.IsNullOrEmpty(sigAlg))
{
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCode.Requester, Message = "Missing signature algorithm parameter" });
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCodes.Requester, Message = "Missing signature algorithm parameter" });
}
if (!SupportedAlgorithms.Contains(sigAlg))
{
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCode.Requester, Message = $"Unsupported signature algorithm: {sigAlg}" });
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCodes.Requester, Message = $"Unsupported signature algorithm: {sigAlg}" });
}
// re-create the querystring part that is signed. The spec dictates the exact way this is to be done:
@ -105,7 +105,7 @@ internal class SamlRequestSignatureValidator<TRequest, TSamlRequest>(TimeProvide
}
catch (XmlException ex)
{
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCode.Requester, Message = $"Invalid XML: {ex.Message}" });
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCodes.Requester, Message = $"Invalid XML: {ex.Message}" });
}
// Find signature element
@ -115,14 +115,14 @@ internal class SamlRequestSignatureValidator<TRequest, TSamlRequest>(TimeProvide
var signatureNode = doc.SelectSingleNode("//ds:Signature", nsmgr);
if (signatureNode == null)
{
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCode.Requester, Message = "Signature element not found" });
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCodes.Requester, Message = "Signature element not found" });
}
// Get the request ID that must be signed
var requestId = doc.DocumentElement?.GetAttribute("ID");
if (string.IsNullOrEmpty(requestId))
{
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCode.Requester, Message = $"{TSamlRequest.MessageName} missing ID attribute" });
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCodes.Requester, Message = $"{TSamlRequest.MessageName} missing ID attribute" });
}
return ValidateWithCertificates(
@ -158,7 +158,7 @@ internal class SamlRequestSignatureValidator<TRequest, TSamlRequest>(TimeProvide
var validCertificates = serviceProvider.SigningCertificates?.Where(cert => ValidateCertificate(cert).Success).ToList();
if (validCertificates == null || validCertificates.Count == 0)
{
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCode.Responder, Message = "No valid certificates configured for service provider" });
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCodes.Responder, Message = "No valid certificates configured for service provider" });
}
foreach (var cert in validCertificates)
@ -169,7 +169,7 @@ internal class SamlRequestSignatureValidator<TRequest, TSamlRequest>(TimeProvide
}
}
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCode.Requester, Message = "Invalid signature" });
return Result<bool, SamlError>.FromError(new SamlError { StatusCode = SamlStatusCodes.Requester, Message = "Invalid signature" });
}
private Result<bool, string> ValidateCertificate(X509Certificate2 certificate)

View file

@ -20,19 +20,19 @@ internal class SamlRequestValidator(TimeProvider timeProvider, IOptions<SamlOpti
/// Validates version, issue instant, and destination for a SAML request
/// </summary>
internal SamlValidationError? ValidateCommonFields(
SamlVersion version,
string version,
DateTime issueInstant,
Uri? destination,
SamlServiceProvider serviceProvider,
string expectedDestination)
{
// Version validation
if (version != SamlVersion.V2)
if (version != SamlVersions.V2)
{
return new SamlValidationError
{
Message = "Only Version 2.0 is supported",
StatusCode = SamlStatusCode.VersionMismatch
StatusCode = SamlStatusCodes.VersionMismatch
};
}
@ -44,7 +44,7 @@ internal class SamlRequestValidator(TimeProvider timeProvider, IOptions<SamlOpti
{
return new SamlValidationError
{
StatusCode = SamlStatusCode.Requester,
StatusCode = SamlStatusCodes.Requester,
Message = "Request IssueInstant is in the future"
};
}
@ -55,7 +55,7 @@ internal class SamlRequestValidator(TimeProvider timeProvider, IOptions<SamlOpti
{
return new SamlValidationError
{
StatusCode = SamlStatusCode.Requester,
StatusCode = SamlStatusCodes.Requester,
Message = "Request has expired (IssueInstant too old)"
};
}
@ -67,7 +67,7 @@ internal class SamlRequestValidator(TimeProvider timeProvider, IOptions<SamlOpti
{
return new SamlValidationError
{
StatusCode = SamlStatusCode.Requester,
StatusCode = SamlStatusCodes.Requester,
Message = $"Invalid destination. Expected '{expectedDestination}'"
};
}
@ -83,6 +83,6 @@ internal class SamlRequestValidator(TimeProvider timeProvider, IOptions<SamlOpti
internal class SamlValidationError
{
internal required string Message { get; init; }
internal SamlStatusCode StatusCode { get; init; } = SamlStatusCode.Requester;
internal SamlStatusCode? SubStatusCode { get; init; }
internal string StatusCode { get; init; } = SamlStatusCodes.Requester;
internal string? SubStatusCode { get; init; }
}

View file

@ -1,15 +0,0 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.
namespace Duende.IdentityServer.Internal.Saml;
internal readonly record struct SamlMessageName(string Value)
{
public static readonly SamlMessageName SamlResponse = new("SAMLResponse");
public static readonly SamlMessageName SamlRequest = new("SAMLRequest");
public static implicit operator SamlMessageName(string value) => new(value);
public override string ToString() => Value;
}

View file

@ -40,7 +40,7 @@ internal class SamlResponseBuilder(
ServiceProvider = serviceProvider,
Binding = serviceProvider.AssertionConsumerServiceBinding,
StatusCode = error.StatusCode,
SubStatusCode = error.SubStatusCode != null ? new SamlStatusCode(error.SubStatusCode) : null,
SubStatusCode = error.SubStatusCode,
Message = error.Message,
AssertionConsumerServiceUrl = acsUrl,
Issuer = serverUrls.Origin, // Todo: not sure if this is a valid issuer
@ -153,8 +153,8 @@ internal class SamlResponseBuilder(
Issuer = issuer,
Status = new Status
{
StatusCode = SamlStatusCode.Success,
NestedStatusCode = samlAuthenticationState.Request?.RequestedAuthnContext != null && !samlAuthenticationState.RequestedAuthnContextRequirementsWereMet ? (string)SamlStatusCode.NoAuthnContext : null,
StatusCode = SamlStatusCodes.Success,
NestedStatusCode = samlAuthenticationState.Request?.RequestedAuthnContext != null && !samlAuthenticationState.RequestedAuthnContextRequirementsWereMet ? SamlStatusCodes.NoAuthnContext : null,
},
Assertion = new Assertion
{

View file

@ -1,8 +1,6 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.
using Duende.IdentityServer.Internal.Saml.SingleSignin.Models;
using Duende.IdentityServer.Saml.Models;
using Microsoft.Extensions.Logging;
namespace Duende.IdentityServer.Internal.Saml.SingleLogout;
@ -32,7 +30,7 @@ internal static partial class Log
EventName = nameof(ParsedLogoutRequest),
Message = $"Parsed LogoutRequest. ID: {{{SingleLogoutLogParameters.RequestId}}}, Issuer: {{{SingleLogoutLogParameters.Issuer}}}, SessionIndex: {{{SingleLogoutLogParameters.SessionIndex}}}"
)]
internal static partial void ParsedLogoutRequest(this ILogger logger, LogLevel logLevel, RequestId requestId, string issuer, string sessionIndex);
internal static partial void ParsedLogoutRequest(this ILogger logger, LogLevel logLevel, string requestId, string issuer, string sessionIndex);
[LoggerMessage(
EventName = nameof(FailedToParseLogoutRequest),
@ -49,12 +47,12 @@ internal static partial class Log
[LoggerMessage(
EventName = nameof(ReceivedLogoutRequest),
Message = $"Received SAML LogoutRequest from {{{SingleLogoutLogParameters.Issuer}}}. RequestId: {{{SingleLogoutLogParameters.RequestId}}}, SessionIndex: {{{SingleLogoutLogParameters.SessionIndex}}}")]
internal static partial void ReceivedLogoutRequest(this ILogger logger, LogLevel logLevel, string issuer, RequestId requestId, string sessionIndex);
internal static partial void ReceivedLogoutRequest(this ILogger logger, LogLevel logLevel, string issuer, string requestId, string sessionIndex);
[LoggerMessage(
EventName = nameof(SuccessfullyProcessedLogoutRequest),
Message = $"Logout request {{{SingleLogoutLogParameters.RequestId}}} with session index {{{SingleLogoutLogParameters.SessionIndex}}} processed successfully")]
internal static partial void SuccessfullyProcessedLogoutRequest(this ILogger logger, LogLevel logLevel, RequestId requestId, string sessionIndex);
internal static partial void SuccessfullyProcessedLogoutRequest(this ILogger logger, LogLevel logLevel, string requestId, string sessionIndex);
[LoggerMessage(
EventName = nameof(SamlLogoutValidationError),
@ -74,17 +72,17 @@ internal static partial class Log
[LoggerMessage(
EventName = nameof(ProcessingSamlLogoutRequest),
Message = $"Processing LogoutRequest {{{SingleLogoutLogParameters.RequestId}}} from SP: {{{SingleLogoutLogParameters.SpName}}} ({{{SingleLogoutLogParameters.Issuer}}})")]
internal static partial void ProcessingSamlLogoutRequest(this ILogger logger, LogLevel logLevel, RequestId requestId, string spName, string issuer);
internal static partial void ProcessingSamlLogoutRequest(this ILogger logger, LogLevel logLevel, string requestId, string spName, string issuer);
[LoggerMessage(
EventName = nameof(SamlLogoutRequestReceivedButNoActiveUserSession),
Message = $"LogoutRequest {{{SingleLogoutLogParameters.RequestId}}} received from {{{SingleLogoutLogParameters.Issuer}}} but no active user session found")]
internal static partial void SamlLogoutRequestReceivedButNoActiveUserSession(this ILogger logger, LogLevel logLevel, RequestId requestId, string issuer);
internal static partial void SamlLogoutRequestReceivedButNoActiveUserSession(this ILogger logger, LogLevel logLevel, string requestId, string issuer);
[LoggerMessage(
EventName = nameof(SamlLogoutRequestReceivedWithWrongSessionIndex),
Message = $"SessionIndex mismatch. Request: {{{SingleLogoutLogParameters.RequestId}}}, SessionIndex: {{{SingleLogoutLogParameters.SessionIndex}}}")]
internal static partial void SamlLogoutRequestReceivedWithWrongSessionIndex(this ILogger logger, LogLevel logLevel, RequestId requestId, string sessionIndex);
internal static partial void SamlLogoutRequestReceivedWithWrongSessionIndex(this ILogger logger, LogLevel logLevel, string requestId, string sessionIndex);
[LoggerMessage(
EventName = nameof(SamlLogoutRedirectToLogoutPage),
@ -99,7 +97,7 @@ internal static partial class Log
[LoggerMessage(
EventName = nameof(SamlLogoutRequestExpired),
Message = $"LogoutRequest {{{SingleLogoutLogParameters.RequestId}}} expired. NotOnOrAfter: {{{SingleLogoutLogParameters.NotOnOrAfter}}}")]
internal static partial void SamlLogoutRequestExpired(this ILogger logger, LogLevel logLevel, RequestId requestId, DateTime notOnOrAfter);
internal static partial void SamlLogoutRequestExpired(this ILogger logger, LogLevel logLevel, string requestId, DateTime notOnOrAfter);
[LoggerMessage(
EventName = nameof(SamlLogoutSignatureValidationFailed),
@ -129,22 +127,22 @@ internal static partial class Log
[LoggerMessage(
EventName = nameof(SamlLogoutUnsupportedVersion),
Message = $"LogoutRequest has unsupported SAML version: {{{SingleLogoutLogParameters.Version}}}. Only 2.0 is supported")]
internal static partial void SamlLogoutUnsupportedVersion(this ILogger logger, LogLevel logLevel, SamlVersion version);
internal static partial void SamlLogoutUnsupportedVersion(this ILogger logger, LogLevel logLevel, string version);
[LoggerMessage(
EventName = nameof(SamlLogoutRequestIssueInstantInFuture),
Message = $"LogoutRequest {{{SingleLogoutLogParameters.RequestId}}} has IssueInstant in the future: {{{SingleLogoutLogParameters.IssueInstant}}}")]
internal static partial void SamlLogoutRequestIssueInstantInFuture(this ILogger logger, LogLevel logLevel, RequestId requestId, DateTime issueInstant);
internal static partial void SamlLogoutRequestIssueInstantInFuture(this ILogger logger, LogLevel logLevel, string requestId, DateTime issueInstant);
[LoggerMessage(
EventName = nameof(SamlLogoutRequestIssueInstantTooOld),
Message = $"LogoutRequest {{{SingleLogoutLogParameters.RequestId}}} has IssueInstant too old (expired): {{{SingleLogoutLogParameters.IssueInstant}}}")]
internal static partial void SamlLogoutRequestIssueInstantTooOld(this ILogger logger, LogLevel logLevel, RequestId requestId, DateTime issueInstant);
internal static partial void SamlLogoutRequestIssueInstantTooOld(this ILogger logger, LogLevel logLevel, string requestId, DateTime issueInstant);
[LoggerMessage(
EventName = nameof(SamlLogoutRequestInvalidDestination),
Message = $"LogoutRequest {{{SingleLogoutLogParameters.RequestId}}} has invalid Destination. Received: {{{SingleLogoutLogParameters.Destination}}}, Expected: {{{SingleLogoutLogParameters.ExpectedDestination}}}")]
internal static partial void SamlLogoutRequestInvalidDestination(this ILogger logger, LogLevel logLevel, RequestId requestId, Uri destination, string expectedDestination);
internal static partial void SamlLogoutRequestInvalidDestination(this ILogger logger, LogLevel logLevel, string requestId, Uri destination, string expectedDestination);
[LoggerMessage(
EventName = nameof(ProcessingSamlLogoutCallback),

View file

@ -15,7 +15,7 @@ internal class LogoutResponseBuilder(
TimeProvider timeProvider)
{
internal async Task<LogoutResponse> BuildSuccessResponseAsync(
RequestId logoutRequestId,
string logoutRequestId,
SamlServiceProvider serviceProvider,
string? relayState)
{
@ -24,15 +24,14 @@ internal class LogoutResponseBuilder(
return new LogoutResponse
{
Id = ResponseId.New(),
Version = SamlVersion.V2,
Id = SamlIds.NewResponseId(),
IssueInstant = timeProvider.GetUtcNow().UtcDateTime,
Destination = destination.Location,
Issuer = issuer,
InResponseTo = logoutRequestId.ToString(),
InResponseTo = logoutRequestId,
Status = new Status
{
StatusCode = SamlStatusCode.Success
StatusCode = SamlStatusCodes.Success
},
ServiceProvider = serviceProvider,
RelayState = relayState
@ -49,12 +48,11 @@ internal class LogoutResponseBuilder(
return new LogoutResponse
{
Id = ResponseId.New(),
Version = SamlVersion.V2,
Id = SamlIds.NewResponseId(),
IssueInstant = timeProvider.GetUtcNow().UtcDateTime,
Destination = destination.Location,
Issuer = issuer,
InResponseTo = request.LogoutRequest.Id.ToString(),
InResponseTo = request.LogoutRequest.Id,
Status = new Status
{
StatusCode = error.StatusCode,

View file

@ -18,12 +18,12 @@ internal record LogoutRequest : ISamlRequest
/// <summary>
/// Gets or sets the unique identifier for this request.
/// </summary>
public required RequestId Id { get; set; }
public required string Id { get; set; }
/// <summary>
/// Gets or sets the SAML version. Must be "2.0".
/// </summary>
public SamlVersion Version { get; set; }
public string Version { get; set; } = SamlVersions.V2;
/// <summary>
/// Gets or sets the time instant of issue in UTC.

View file

@ -23,12 +23,12 @@ internal class LogoutResponse : EndpointResult<LogoutResponse>
/// <summary>
/// Gets or sets the unique identifier for this response.
/// </summary>
public required ResponseId Id { get; set; }
public required string Id { get; set; }
/// <summary>
/// Gets or sets the SAML version. Must be "2.0".
/// </summary>
public SamlVersion Version { get; set; } = SamlVersion.V2;
public string Version { get; set; } = SamlVersions.V2;
/// <summary>
/// Gets or sets the time instant of issue in UTC.
@ -80,7 +80,7 @@ internal class LogoutResponse : EndpointResult<LogoutResponse>
var encodedResponse = Convert.ToBase64String(Encoding.UTF8.GetBytes(signedResponseXml));
var html = HttpResponseBindings.GenerateAutoPostForm(SamlMessageName.SamlResponse, encodedResponse, result.Destination, result.RelayState);
var html = HttpResponseBindings.GenerateAutoPostForm(SamlConstants.RequestProperties.SAMLResponse, encodedResponse, result.Destination, result.RelayState);
httpContext.Response.ContentType = "text/html";
httpContext.Response.Headers.CacheControl = "no-cache, no-store";
@ -118,8 +118,8 @@ internal class LogoutResponse : EndpointResult<LogoutResponse>
// Build LogoutResponse element
var responseElement = new XElement(protocolNs + ElementNames.RootElement,
new XAttribute("ID", toSerialize.Id.Value),
new XAttribute("Version", toSerialize.Version.ToString()),
new XAttribute("ID", toSerialize.Id),
new XAttribute("Version", toSerialize.Version),
new XAttribute("IssueInstant", issueInstant),
new XAttribute("Destination", toSerialize.Destination),
new XAttribute("InResponseTo", toSerialize.InResponseTo),

View file

@ -37,8 +37,7 @@ internal class SamlFrontChannelLogoutRequestBuilder(
var logoutRequest = new LogoutRequest
{
Id = RequestId.New(),
Version = SamlVersion.V2,
Id = SamlIds.NewRequestId(),
IssueInstant = timeProvider.GetUtcNow().UtcDateTime,
Destination = serviceProvider.SingleLogoutServiceUrl.Location,
Issuer = issuer,
@ -65,8 +64,8 @@ internal class SamlFrontChannelLogoutRequestBuilder(
var assertionNs = XNamespace.Get(SamlConstants.Namespaces.Assertion);
var requestElement = new XElement(protocolNs + LogoutRequest.ElementNames.RootElement,
new XAttribute("ID", logoutRequest.Id.Value),
new XAttribute("Version", logoutRequest.Version.ToString()),
new XAttribute("ID", logoutRequest.Id),
new XAttribute("Version", logoutRequest.Version),
new XAttribute("IssueInstant", issueInstant),
new XAttribute("Destination", logoutRequest.Destination!),
new XElement(assertionNs + LogoutRequest.ElementNames.Issuer, logoutRequest.Issuer));

View file

@ -3,7 +3,6 @@
using Duende.IdentityServer.Internal.Saml.Infrastructure;
using Duende.IdentityServer.Internal.Saml.SingleLogout.Models;
using Duende.IdentityServer.Internal.Saml.SingleSignin.Models;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Stores;
using Microsoft.Extensions.Logging;
@ -63,7 +62,7 @@ internal class SamlLogoutCallbackProcessor(
}
var response = await logoutResponseBuilder.BuildSuccessResponseAsync(
new RequestId(data.SamlLogoutRequestId),
data.SamlLogoutRequestId,
sp,
data.SamlRelayState);

View file

@ -119,7 +119,7 @@ internal class SamlLogoutRequestProcessor : SamlRequestProcessorBase<LogoutReque
Type = SamlRequestErrorType.Protocol,
ProtocolError = new SamlProtocolError<SamlLogoutRequest>(sp, request, new SamlError
{
StatusCode = SamlStatusCode.Requester,
StatusCode = SamlStatusCodes.Requester,
Message = "Logout request expired (NotOnOrAfter is in the past)"
})
};
@ -163,7 +163,7 @@ internal class SamlLogoutRequestProcessor : SamlRequestProcessorBase<LogoutReque
ClientIds = oidcClientIds,
SamlServiceProviderEntityId = serviceProvider.EntityId,
SamlSessions = samlSessions,
SamlLogoutRequestId = logoutRequest.LogoutRequest.Id.Value,
SamlLogoutRequestId = logoutRequest.LogoutRequest.Id,
SamlRelayState = logoutRequest.RelayState,
PostLogoutRedirectUri = _urlBuilder.SamlLogoutCallBackUri().ToString()
};

View file

@ -9,7 +9,6 @@ using Duende.IdentityServer.Services;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using SamlStatusCode = Duende.IdentityServer.Saml.Models.SamlStatusCode;
namespace Duende.IdentityServer.Internal.Saml.SingleSignin;
@ -37,7 +36,7 @@ internal class DefaultSamlSigninInteractionResponseGenerator(
// ForceAuthn and IsPassive are "true", the identity provider MUST NOT freshly authenticate the
//presenter unless the constraints of IsPassive can be met.
logger.SamlInteractionPassiveAndForced(LogLevel.Debug);
return SamlInteractionResponse.CreateError(SamlStatusCode.NoPassive, "The user is not currently logged in");
return SamlInteractionResponse.CreateError(SamlStatusCodes.NoPassive, "The user is not currently logged in");
}
if (request.ForceAuthn)
@ -57,7 +56,7 @@ internal class DefaultSamlSigninInteractionResponseGenerator(
if (request.IsPassive)
{
logger.SamlInteractionNoPassive(LogLevel.Debug);
return SamlInteractionResponse.CreateError(SamlStatusCode.NoPassive, "The user is not currently logged in and passive login was requested.");
return SamlInteractionResponse.CreateError(SamlStatusCodes.NoPassive, "The user is not currently logged in and passive login was requested.");
}
// Todo: The AuthN request may contain hints on account creation 3.4.1.1 Element <NameIDPolicy>: AllowCreate

View file

@ -19,12 +19,12 @@ internal record Assertion
///- xs:ID must conform to the NCName production (Non-Colonized Name) from the XML Namespaces specification
///- NCName cannot start with a digit, colon, or certain other characters
/// </summary>
public AssertionId Id { get; } = AssertionId.NewId();
public string Id { get; } = SamlIds.NewAssertionId();
/// <summary>
/// SAML version (must be "2.0")
/// </summary>
public SamlVersion Version { get; } = SamlVersion.V2;
public string Version { get; } = SamlVersions.V2;
/// <summary>
/// Time instant of issuance

View file

@ -1,22 +0,0 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.
namespace Duende.IdentityServer.Internal.Saml.SingleSignin.Models;
/// <summary>
/// Unique identifier for this assertion
/// Must start with a _ character and be unique
///
/// According to SAML 2.0 Core Specification (Section 1.3.4):
///- ID attributes must be of type xs:ID
///- xs:ID must conform to the NCName production (Non-Colonized Name) from the XML Namespaces specification
///- NCName cannot start with a digit, colon, or certain other characters
/// </summary>
internal readonly record struct AssertionId(string Value)
{
public static AssertionId NewId() => new("_" + Guid.NewGuid().ToString("N"));
public static implicit operator AssertionId(string value) => new(value);
public override string ToString() => Value;
}

View file

@ -1,22 +0,0 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.
namespace Duende.IdentityServer.Internal.Saml.SingleSignin.Models;
/// <summary>
/// Unique identifier for this assertion
/// Must start with a _ character and be unique
///
/// According to SAML 2.0 Core Specification (Section 1.3.4):
///- ID attributes must be of type xs:ID
///- xs:ID must conform to the NCName production (Non-Colonized Name) from the XML Namespaces specification
///- NCName cannot start with a digit, colon, or certain other characters
/// </summary>
internal readonly record struct RequestId(string Value)
{
public static RequestId New() => new("_" + Guid.NewGuid().ToString("N"));
public static implicit operator RequestId(string value) => new(value);
public override string ToString() => Value;
}

View file

@ -1,22 +0,0 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.
namespace Duende.IdentityServer.Internal.Saml.SingleSignin.Models;
/// <summary>
/// Unique identifier for this assertion
/// Must start with a _ character and be unique
///
/// According to SAML 2.0 Core Specification (Section 1.3.4):
///- ID attributes must be of type xs:ID
///- xs:ID must conform to the NCName production (Non-Colonized Name) from the XML Namespaces specification
///- NCName cannot start with a digit, colon, or certain other characters
/// </summary>
internal readonly record struct ResponseId(string Value)
{
public static ResponseId New() => new("_" + Guid.NewGuid().ToString("N"));
public static implicit operator ResponseId(string value) => new(value);
public override string ToString() => Value;
}

View file

@ -0,0 +1,16 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.
namespace Duende.IdentityServer.Internal.Saml.SingleSignin.Models;
/// <summary>
/// Generates SAML-compliant ID values.
/// SAML 2.0 IDs must conform to xs:ID (NCName), which cannot start with a digit,
/// so we prefix with underscore as required by the spec.
/// </summary>
internal static class SamlIds
{
internal static string NewRequestId() => "_" + Guid.NewGuid().ToString("N");
internal static string NewResponseId() => "_" + Guid.NewGuid().ToString("N");
internal static string NewAssertionId() => "_" + Guid.NewGuid().ToString("N");
}

View file

@ -19,12 +19,12 @@ internal class SamlResponse : EndpointResult<SamlResponse>
/// <summary>
/// Gets or sets the unique identifier for this response.
/// </summary>
public ResponseId Id { get; } = ResponseId.New();
public string Id { get; } = SamlIds.NewResponseId();
/// <summary>
/// Gets or sets the SAML version. Must be "2.0".
/// </summary>
public SamlVersion Version { get; } = SamlVersion.V2;
public string Version { get; } = SamlVersions.V2;
/// <summary>
/// Gets or sets the time instant of issue in UTC.
@ -87,7 +87,7 @@ internal class SamlResponse : EndpointResult<SamlResponse>
var encodedResponse = Convert.ToBase64String(Encoding.UTF8.GetBytes(signedResponseXml));
var html = HttpResponseBindings.GenerateAutoPostForm(SamlMessageName.SamlResponse, encodedResponse, result.Destination, result.RelayState);
var html = HttpResponseBindings.GenerateAutoPostForm(SamlConstants.RequestProperties.SAMLResponse, encodedResponse, result.Destination, result.RelayState);
httpContext.Response.ContentType = "text/html";
httpContext.Response.Headers.CacheControl = "no-cache, no-store";

View file

@ -1,11 +0,0 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.
namespace Duende.IdentityServer.Internal.Saml.SingleSignin.Models;
internal readonly record struct SessionId(Guid Value)
{
public static SessionId NewId() => new(Guid.NewGuid());
public override string ToString() => Value.ToString();
}

View file

@ -2,7 +2,6 @@
// See LICENSE in the project root for license information.
#nullable enable
using Duende.IdentityServer.Saml.Models;
namespace Duende.IdentityServer.Internal.Saml.SingleSignin.Models;
@ -14,7 +13,7 @@ internal record Status
/// <summary>
/// Gets or sets the status code indicating the success or failure of the request.
/// </summary>
public required SamlStatusCode StatusCode { get; set; }
public required string StatusCode { get; set; }
/// <summary>
/// Gets or sets an optional human-readable message providing additional information about the status.

View file

@ -13,7 +13,6 @@ using Duende.IdentityServer.Services;
using Duende.IdentityServer.Stores;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using SamlStatusCode = Duende.IdentityServer.Saml.Models.SamlStatusCode;
namespace Duende.IdentityServer.Internal.Saml.SingleSignin;
@ -157,8 +156,8 @@ internal class SamlSigninRequestProcessor(
var samlError = new SamlError
{
StatusCode = SamlStatusCode.Responder,
SubStatusCode = SamlStatusCode.InvalidNameIdPolicy,
StatusCode = SamlStatusCodes.Responder,
SubStatusCode = SamlStatusCodes.InvalidNameIdPolicy,
Message = $"Requested NameID format '{requestedFormat}' is not supported by this IdP"
};
return new SamlRequestError<SamlSigninRequest>

View file

@ -23,7 +23,7 @@ public record AuthNRequest : ISamlRequest
/// <summary>
/// Gets or sets the SAML version. Must be "2.0".
/// </summary>
public required SamlVersion Version { get; set; }
public required string Version { get; set; }
/// <summary>
/// Gets or sets the time instant of issue in UTC.

View file

@ -1,31 +0,0 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.
namespace Duende.IdentityServer.Saml.Models;
/// <summary>
/// Represents a SAML 2.0 status code as defined in the SAML 2.0 Core specification.
/// </summary>
public readonly record struct SamlStatusCode(string Value)
{
public static readonly SamlStatusCode Success = new("urn:oasis:names:tc:SAML:2.0:status:Success");
public static readonly SamlStatusCode Requester = new("urn:oasis:names:tc:SAML:2.0:status:Requester");
public static readonly SamlStatusCode Responder = new("urn:oasis:names:tc:SAML:2.0:status:Responder");
public static readonly SamlStatusCode VersionMismatch = new("urn:oasis:names:tc:SAML:2.0:status:VersionMismatch");
public static readonly SamlStatusCode NoAuthnContext = new("urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext");
public static readonly SamlStatusCode AuthnFailed = new("urn:oasis:names:tc:SAML:2.0:status:AuthnFailed");
public static readonly SamlStatusCode InvalidNameIdPolicy = new("urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy");
public static readonly SamlStatusCode RequestDenied = new("urn:oasis:names:tc:SAML:2.0:status:RequestDenied");
public static readonly SamlStatusCode UnknownPrincipal = new("urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal");
public static readonly SamlStatusCode UnsupportedBinding = new("urn:oasis:names:tc:SAML:2.0:status:UnsupportedBinding");
public static readonly SamlStatusCode NoPassive = new("urn:oasis:names:tc:SAML:2.0:status:NoPassive");
/// <inheritdoc />
public override string ToString() => Value;
public static implicit operator string(SamlStatusCode statusCode) => statusCode.Value;
public static implicit operator SamlStatusCode(string value) => new(value);
public SamlStatusCode ToSamlStatusCode() => Value;
}

View file

@ -0,0 +1,43 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.
namespace Duende.IdentityServer.Saml.Models;
/// <summary>
/// Well-known SAML 2.0 status code URNs as defined in the SAML 2.0 Core specification.
/// </summary>
public static class SamlStatusCodes
{
/// <summary>The request succeeded.</summary>
public const string Success = "urn:oasis:names:tc:SAML:2.0:status:Success";
/// <summary>The request could not be performed due to an error on the part of the requester.</summary>
public const string Requester = "urn:oasis:names:tc:SAML:2.0:status:Requester";
/// <summary>The request could not be performed due to an error on the part of the SAML responder or SAML authority.</summary>
public const string Responder = "urn:oasis:names:tc:SAML:2.0:status:Responder";
/// <summary>The SAML responder could not process the request because the version of the request message was incorrect.</summary>
public const string VersionMismatch = "urn:oasis:names:tc:SAML:2.0:status:VersionMismatch";
/// <summary>The responding provider cannot authenticate the principal by means of the currently deployed authentication authority.</summary>
public const string NoAuthnContext = "urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext";
/// <summary>The authentication attempt failed.</summary>
public const string AuthnFailed = "urn:oasis:names:tc:SAML:2.0:status:AuthnFailed";
/// <summary>The responding provider cannot permit a subject confirmation based on the requirements of the requester.</summary>
public const string InvalidNameIdPolicy = "urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy";
/// <summary>The SAML responder or SAML authority is able to process the request but has chosen not to respond.</summary>
public const string RequestDenied = "urn:oasis:names:tc:SAML:2.0:status:RequestDenied";
/// <summary>The responding provider does not recognize the principal specified or implied by the request.</summary>
public const string UnknownPrincipal = "urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal";
/// <summary>The SAML responder cannot properly fulfill the request using the protocol binding specified in the request.</summary>
public const string UnsupportedBinding = "urn:oasis:names:tc:SAML:2.0:status:UnsupportedBinding";
/// <summary>The identity provider cannot authenticate the presenter in a manner that satisfies the IsPassive constraint of the request.</summary>
public const string NoPassive = "urn:oasis:names:tc:SAML:2.0:status:NoPassive";
}

View file

@ -1,19 +0,0 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.
namespace Duende.IdentityServer.Saml.Models;
/// <summary>
/// Represents a SAML version string.
/// </summary>
public readonly record struct SamlVersion(string Value)
{
public static readonly SamlVersion V2 = new("2.0");
/// <inheritdoc />
public override string ToString() => Value;
public static implicit operator SamlVersion(string value) => new(value);
public SamlVersion ToSamlVersion() => Value;
}

View file

@ -0,0 +1,13 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.
namespace Duende.IdentityServer.Saml.Models;
/// <summary>
/// Well-known SAML version strings.
/// </summary>
public static class SamlVersions
{
/// <summary>SAML version 2.0.</summary>
public const string V2 = "2.0";
}

View file

@ -10,7 +10,7 @@ using System.Security.Cryptography.X509Certificates;
using Duende.IdentityModel;
using Duende.IdentityServer.Models;
using static Duende.IdentityServer.IntegrationTests.Endpoints.Saml.SamlTestHelpers;
using SamlStatusCode = Duende.IdentityServer.Saml.Models.SamlStatusCode;
using Duende.IdentityServer.Saml.Models;
namespace Duende.IdentityServer.IntegrationTests.Endpoints.Saml;
@ -116,7 +116,7 @@ public class SamlEncryptionTests
var samlResponse = await ExtractAndDecryptSamlSuccessFromPostAsync(result, encryptionCert, CancellationToken.None);
samlResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
samlResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
samlResponse.Assertion.ShouldNotBeNull();
samlResponse.Assertion.Subject.ShouldNotBeNull();
@ -453,7 +453,7 @@ public class SamlEncryptionTests
// Verify can parse as success
var samlResponse = await ExtractSamlSuccessFromPostAsync(result, CancellationToken.None);
samlResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
samlResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
samlResponse.Assertion.ShouldNotBeNull();
}
}

View file

@ -16,7 +16,6 @@ using Duende.IdentityServer.Models;
using Duende.IdentityServer.Saml.Models;
using Microsoft.AspNetCore.Mvc;
using static Duende.IdentityServer.IntegrationTests.Endpoints.Saml.SamlTestHelpers;
using SamlStatusCode = Duende.IdentityServer.Saml.Models.SamlStatusCode;
namespace Duende.IdentityServer.IntegrationTests.Endpoints.Saml;
@ -128,7 +127,7 @@ public class SamlSigninEndpointTests
await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}&RelayState={Data.RelayState}", _ct);
var samlSuccessResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
samlSuccessResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
samlSuccessResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
samlSuccessResponse.RelayState.ShouldBe(Data.RelayState);
}
@ -251,7 +250,7 @@ public class SamlSigninEndpointTests
var result = await Fixture.Client.PostAsync($"/saml/signin?", content, _ct);
var samlSuccessResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
samlSuccessResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
samlSuccessResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
samlSuccessResponse.RelayState.ShouldBe(Data.RelayState);
}
@ -542,7 +541,7 @@ public class SamlSigninEndpointTests
successResponse.Issuer.ShouldBe(Fixture.Url());
successResponse.InResponseTo.ShouldBe(Data.RequestId);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
var assertion = successResponse.Assertion;
assertion.ShouldNotBeNull();
@ -836,7 +835,7 @@ public class SamlSigninEndpointTests
result.StatusCode.ShouldBe(HttpStatusCode.OK);
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameId.ShouldBe("user-id");
}
@ -859,7 +858,7 @@ public class SamlSigninEndpointTests
// Without ForceAuthn, authenticated user goes directly to callback
normalResult.StatusCode.ShouldBe(HttpStatusCode.OK);
var samlSuccessResponse = await ExtractSamlSuccessFromPostAsync(normalResult, _ct);
samlSuccessResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
samlSuccessResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
var forceAuthnRequestXml = Build.AuthNRequestXml(forceAuthn: true);
var forceAuthnUrlEncoded = await EncodeRequest(forceAuthnRequestXml);
@ -1038,7 +1037,7 @@ public class SamlSigninEndpointTests
var signinCallbackResponse = await Fixture.Client.GetAsync(returnUrl, _ct);
var samlSuccessResponse = await ExtractSamlSuccessFromPostAsync(signinCallbackResponse, _ct);
samlSuccessResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
samlSuccessResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
samlSuccessResponse.Destination.ShouldBe(primaryAcsUrl.ToString());
}
@ -1151,7 +1150,7 @@ public class SamlSigninEndpointTests
var (_, _, responseElement) = ParseSamlResponseXml(responseXml);
VerifySignaturePositionAfterIssuer(responseElement);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameId.ShouldBe("user-id");
}
@ -1183,7 +1182,7 @@ public class SamlSigninEndpointTests
assertionElement.ShouldNotBeNull();
VerifySignaturePositionAfterIssuer(assertionElement!);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameId.ShouldBe("user-id");
}
@ -1217,7 +1216,7 @@ public class SamlSigninEndpointTests
assertionElement.ShouldNotBeNull();
VerifySignaturePositionAfterIssuer(assertionElement!);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameId.ShouldBe("user-id");
}
@ -1244,7 +1243,7 @@ public class SamlSigninEndpointTests
VerifySignaturePresence(responseXml, expectResponseSignature: false, expectAssertionSignature: false);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameId.ShouldBe("user-id");
}
@ -1272,7 +1271,7 @@ public class SamlSigninEndpointTests
// Default behavior should be SignAssertion per SAML best practices
VerifySignaturePresence(responseXml, expectResponseSignature: false, expectAssertionSignature: true);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameId.ShouldBe("user-id");
}
@ -1564,7 +1563,7 @@ public class SamlSigninEndpointTests
successResponse.RelayState.ShouldBe(relayStateValue);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameId.ShouldBe("user-id");
}
@ -1585,7 +1584,7 @@ public class SamlSigninEndpointTests
var normalUrlEncoded = await EncodeRequest(normalRequestXml);
var normalResult = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={normalUrlEncoded}", _ct);
var normalResponse = await ExtractSamlSuccessFromPostAsync(normalResult, _ct);
normalResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
normalResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
var forceAuthnRequestXml = Build.AuthNRequestXml(forceAuthn: true);
var forceAuthnUrlEncoded = await EncodeRequest(forceAuthnRequestXml);
@ -1624,7 +1623,7 @@ public class SamlSigninEndpointTests
VerifySignaturePresence(responseXml, expectResponseSignature: true, expectAssertionSignature: true);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameId.ShouldBe("user-id");
}
@ -1656,7 +1655,7 @@ public class SamlSigninEndpointTests
VerifySignaturePresence(responseXml, expectResponseSignature: true, expectAssertionSignature: false);
successResponse.Destination.ShouldBe(secondaryAcsUrl.ToString());
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameId.ShouldBe("user-id");
}
@ -1673,7 +1672,7 @@ public class SamlSigninEndpointTests
var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct);
var samlError = await ExtractSamlErrorFromPostAsync(result);
samlError.StatusCode.ShouldBe(SamlStatusCode.Requester.Value);
samlError.StatusCode.ShouldBe(SamlStatusCodes.Requester);
samlError.StatusMessage.ShouldBe("Missing signature parameter");
}
@ -1693,7 +1692,7 @@ public class SamlSigninEndpointTests
var result = await Fixture.Client.PostAsync("/saml/signin", content, _ct);
var samlError = await ExtractSamlErrorFromPostAsync(result);
samlError.StatusCode.ShouldBe(SamlStatusCode.Requester.Value);
samlError.StatusCode.ShouldBe(SamlStatusCodes.Requester);
samlError.StatusMessage.ShouldBe("Signature element not found");
}
@ -1714,7 +1713,7 @@ public class SamlSigninEndpointTests
$"/saml/signin?SAMLRequest={urlEncoded}&Signature={Uri.EscapeDataString(signature)}&SigAlg={Uri.EscapeDataString(sigAlg)}", _ct);
var samlError = await ExtractSamlErrorFromPostAsync(result);
samlError.StatusCode.ShouldBe(SamlStatusCode.Requester.Value);
samlError.StatusCode.ShouldBe(SamlStatusCodes.Requester);
samlError.StatusMessage.ShouldBe("Invalid signature");
}
@ -1736,7 +1735,7 @@ public class SamlSigninEndpointTests
var result = await Fixture.Client.PostAsync("/saml/signin", content, _ct);
var samlError = await ExtractSamlErrorFromPostAsync(result);
samlError.StatusCode.ShouldBe(SamlStatusCode.Requester.Value);
samlError.StatusCode.ShouldBe(SamlStatusCodes.Requester);
samlError.StatusMessage.ShouldBe("Invalid signature");
}
@ -1778,7 +1777,7 @@ public class SamlSigninEndpointTests
$"/saml/signin?SAMLRequest={urlEncoded}&Signature={Uri.EscapeDataString(signature)}&SigAlg={Uri.EscapeDataString(sigAlg)}", _ct);
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
}
[Fact]
@ -1802,7 +1801,7 @@ public class SamlSigninEndpointTests
var result = await Fixture.Client.PostAsync("/saml/signin", content, _ct);
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
}
[Fact]
@ -1821,7 +1820,7 @@ public class SamlSigninEndpointTests
var result = await Fixture.Client.GetAsync($"/saml/signin?SAMLRequest={urlEncoded}", _ct);
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
}
[Fact]
@ -1844,7 +1843,7 @@ public class SamlSigninEndpointTests
$"/saml/signin?SAMLRequest={urlEncoded}&Signature={Uri.EscapeDataString(signature)}&SigAlg={Uri.EscapeDataString(sigAlg)}", _ct);
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
}
[Fact]
@ -1883,7 +1882,7 @@ public class SamlSigninEndpointTests
$"/saml/signin?SAMLRequest={urlEncoded}&Signature={Uri.EscapeDataString(signature)}&SigAlg={Uri.EscapeDataString(sigAlg)}", _ct);
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
}
[Fact]
@ -1904,7 +1903,7 @@ public class SamlSigninEndpointTests
$"/saml/signin?SAMLRequest={urlEncoded}&Signature={Uri.EscapeDataString(signature)}&SigAlg={Uri.EscapeDataString(sigAlg)}", _ct);
var samlError = await ExtractSamlErrorFromPostAsync(result);
samlError.StatusCode.ShouldBe(SamlStatusCode.Requester.Value);
samlError.StatusCode.ShouldBe(SamlStatusCodes.Requester);
samlError.StatusMessage.ShouldBe("Unsupported signature algorithm: http://www.w3.org/2000/09/xmldsig#rsa-sha1");
}
@ -1926,7 +1925,7 @@ public class SamlSigninEndpointTests
$"/saml/signin?SAMLRequest={urlEncoded}&SigAlg={Uri.EscapeDataString(sigAlg)}&Signature={Uri.EscapeDataString(signature)}", _ct);
var samlError = await ExtractSamlErrorFromPostAsync(result);
samlError.StatusCode.ShouldBe(SamlStatusCode.Requester.Value);
samlError.StatusCode.ShouldBe(SamlStatusCodes.Requester);
samlError.StatusMessage.ShouldBe("Invalid signature");
}
@ -1951,7 +1950,7 @@ public class SamlSigninEndpointTests
$"/saml/signin?SAMLRequest={urlEncoded}&RelayState={Uri.EscapeDataString(relayState)}&Signature={Uri.EscapeDataString(signature)}&SigAlg={Uri.EscapeDataString(sigAlg)}", _ct);
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.RelayState.ShouldBe(relayState);
}
@ -1973,7 +1972,7 @@ public class SamlSigninEndpointTests
var result = await Fixture.Client.PostAsync("/saml/signin", content, _ct);
var samlError = await ExtractSamlErrorFromPostAsync(result);
samlError.StatusCode.ShouldBe(SamlStatusCode.Requester.Value);
samlError.StatusCode.ShouldBe(SamlStatusCodes.Requester);
samlError.StatusMessage.ShouldBe("Invalid signature");
}
@ -1996,7 +1995,7 @@ public class SamlSigninEndpointTests
var result = await Fixture.Client.PostAsync("/saml/signin", content, _ct);
var samlError = await ExtractSamlErrorFromPostAsync(result);
samlError.StatusCode.ShouldBe(SamlStatusCode.Requester.Value);
samlError.StatusCode.ShouldBe(SamlStatusCodes.Requester);
samlError.StatusMessage.ShouldBe("Invalid signature");
}
@ -2022,7 +2021,7 @@ public class SamlSigninEndpointTests
var result = await Fixture.Client.PostAsync("/saml/signin", content, _ct);
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
}
[Fact]
@ -2041,7 +2040,7 @@ public class SamlSigninEndpointTests
$"/saml/signin?SAMLRequest={urlEncoded}&Signature={Uri.EscapeDataString(signature)}&SigAlg={Uri.EscapeDataString(sigAlg)}", _ct);
var samlError = await ExtractSamlErrorFromPostAsync(result);
samlError.StatusCode.ShouldBe(SamlStatusCode.Responder.Value);
samlError.StatusCode.ShouldBe(SamlStatusCodes.Responder);
samlError.StatusMessage.ShouldBe("No valid certificates configured for service provider");
}
@ -2061,7 +2060,7 @@ public class SamlSigninEndpointTests
$"/saml/signin?SAMLRequest={urlEncoded}&Signature={Uri.EscapeDataString(signature)}&SigAlg={Uri.EscapeDataString(sigAlg)}", _ct);
var samlError = await ExtractSamlErrorFromPostAsync(result);
samlError.StatusCode.ShouldBe(SamlStatusCode.Responder.Value);
samlError.StatusCode.ShouldBe(SamlStatusCodes.Responder);
samlError.StatusMessage.ShouldBe("No valid certificates configured for service provider");
}
@ -2085,7 +2084,7 @@ public class SamlSigninEndpointTests
$"/saml/signin?SAMLRequest={urlEncoded}&Signature={Uri.EscapeDataString(signature)}&SigAlg={Uri.EscapeDataString(sigAlg)}", _ct);
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
}
[Fact]
@ -2124,7 +2123,7 @@ public class SamlSigninEndpointTests
// Assert
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.AuthnStatement?.AuthnContextClassRef.ShouldBe("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport");
}
@ -2164,8 +2163,8 @@ public class SamlSigninEndpointTests
// Assert
var samlResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
samlResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
samlResponse.SubStatusCode.ShouldBe(SamlStatusCode.NoAuthnContext.Value);
samlResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
samlResponse.SubStatusCode.ShouldBe(SamlStatusCodes.NoAuthnContext);
samlResponse.Assertion.AuthnStatement?.AuthnContextClassRef.ShouldBe("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport");
}
@ -2195,8 +2194,8 @@ public class SamlSigninEndpointTests
// Assert
var samlResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
samlResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
samlResponse.SubStatusCode.ShouldBe(SamlStatusCode.NoAuthnContext.Value);
samlResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
samlResponse.SubStatusCode.ShouldBe(SamlStatusCodes.NoAuthnContext);
samlResponse.Assertion.AuthnStatement?.AuthnContextClassRef.ShouldBe("urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified");
}
@ -2224,7 +2223,7 @@ public class SamlSigninEndpointTests
// Assert
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.AuthnStatement?.AuthnContextClassRef.ShouldBe("urn:oasis:names:tc:SAML:2.0:ac:classes:X509");
}
@ -2299,8 +2298,8 @@ public class SamlSigninEndpointTests
// Assert
var errorResponse = await ExtractSamlErrorFromPostAsync(result);
errorResponse.StatusCode.ShouldBe(SamlStatusCode.Responder.Value);
errorResponse.SubStatusCode.ShouldBe(SamlStatusCode.InvalidNameIdPolicy.Value);
errorResponse.StatusCode.ShouldBe(SamlStatusCodes.Responder);
errorResponse.SubStatusCode.ShouldBe(SamlStatusCodes.InvalidNameIdPolicy);
errorResponse.StatusMessage.ShouldBe($"Requested NameID format '{unsupportedFormat}' is not supported by this IdP");
}
@ -2367,7 +2366,7 @@ public class SamlSigninEndpointTests
// Assert
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameId.ShouldBe("test@test.com");
successResponse.Assertion.Subject?.NameIdFormat.ShouldBe(SamlConstants.NameIdentifierFormats.EmailAddress);
}
@ -2395,7 +2394,7 @@ public class SamlSigninEndpointTests
// Assert
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameId.ShouldBe(persistentIdentifier);
successResponse.Assertion.Subject?.NameIdFormat.ShouldBe(SamlConstants.NameIdentifierFormats.Persistent);
}
@ -2425,7 +2424,7 @@ public class SamlSigninEndpointTests
// Assert
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameIdFormat.ShouldBe(SamlConstants.NameIdentifierFormats.Unspecified);
successResponse.Assertion.Subject?.NameId.ShouldBe("user-id");
}
@ -2456,8 +2455,8 @@ public class SamlSigninEndpointTests
var nameId2 = response2.Assertion.Subject?.NameId;
// Verify both responses succeeded
response1.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
response2.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
response1.StatusCode.ShouldBe(SamlStatusCodes.Success);
response2.StatusCode.ShouldBe(SamlStatusCodes.Success);
// Verify format is transient
response1.Assertion.Subject?.NameIdFormat.ShouldBe(SamlConstants.NameIdentifierFormats.Transient);
@ -2494,7 +2493,7 @@ public class SamlSigninEndpointTests
// Assert
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameId.ShouldBe(persistentId);
successResponse.Assertion.Subject?.NameIdFormat.ShouldBe(SamlConstants.NameIdentifierFormats.Persistent);
successResponse.Assertion.Subject?.SPNameQualifier.ShouldBe(Data.EntityId.ToString());
@ -2527,7 +2526,7 @@ public class SamlSigninEndpointTests
// Assert
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameId.ShouldBe(spSpecificId); // Uses SP override, not default
successResponse.Assertion.Subject?.NameIdFormat.ShouldBe(SamlConstants.NameIdentifierFormats.Persistent);
successResponse.Assertion.Subject?.SPNameQualifier.ShouldBe(Data.EntityId.ToString());
@ -2612,7 +2611,7 @@ public class SamlSigninEndpointTests
// Assert
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.SPNameQualifier.ShouldBe(Data.EntityId.ToString());
successResponse.Assertion.Subject?.NameIdFormat.ShouldBe(SamlConstants.NameIdentifierFormats.Persistent);
}
@ -2648,7 +2647,7 @@ public class SamlSigninEndpointTests
// Assert
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameId.ShouldBe(customPersistentId);
successResponse.Assertion.Subject?.NameIdFormat.ShouldBe(SamlConstants.NameIdentifierFormats.Persistent);
}
@ -2689,8 +2688,8 @@ public class SamlSigninEndpointTests
var responseB = await ExtractSamlSuccessFromPostAsync(resultB, _ct);
// Assert
responseA.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
responseB.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
responseA.StatusCode.ShouldBe(SamlStatusCodes.Success);
responseB.StatusCode.ShouldBe(SamlStatusCodes.Success);
responseA.Assertion.Subject?.NameId.ShouldBe(userAPersistentId);
responseB.Assertion.Subject?.NameId.ShouldBe(userBPersistentId);
@ -2778,7 +2777,7 @@ public class SamlSigninEndpointTests
// Assert
var successResponse = await ExtractSamlSuccessFromPostAsync(result, _ct);
successResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
successResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
successResponse.Assertion.Subject?.NameId.ShouldBe("test@testing.com");
successResponse.Assertion.Subject?.NameIdFormat.ShouldBe(SamlConstants.NameIdentifierFormats.EmailAddress);
}

View file

@ -89,7 +89,7 @@ public class SamlSingleLogoutCallbackEndpointTests
// Assert
var samlResponse = await SamlTestHelpers.ExtractSamlLogoutResponseFromPostAsync(result, CancellationToken.None);
samlResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
samlResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
}
[Fact]

View file

@ -184,7 +184,7 @@ public class SamlSingleLogoutEndpointTests
// Assert
var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CancellationToken.None);
logoutResponse.StatusCode.ShouldBe(SamlStatusCode.VersionMismatch.Value);
logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.VersionMismatch);
}
[Fact]
@ -209,7 +209,7 @@ public class SamlSingleLogoutEndpointTests
// Assert
var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CancellationToken.None);
logoutResponse.StatusCode.ShouldBe(SamlStatusCode.Requester.Value);
logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Requester);
logoutResponse.StatusMessage.ShouldBe("Request IssueInstant is in the future");
}
@ -235,7 +235,7 @@ public class SamlSingleLogoutEndpointTests
// Assert
var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CancellationToken.None);
logoutResponse.StatusCode.ShouldBe(SamlStatusCode.Requester.Value);
logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Requester);
logoutResponse.StatusMessage.ShouldBe("Request has expired (IssueInstant too old)");
}
@ -259,7 +259,7 @@ public class SamlSingleLogoutEndpointTests
// Assert
var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CancellationToken.None);
logoutResponse.StatusCode.ShouldBe(SamlStatusCode.Requester.Value);
logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Requester);
logoutResponse.StatusMessage.ShouldBe($"Invalid destination. Expected '{Fixture.Url()}/saml/logout'");
}
@ -309,7 +309,7 @@ public class SamlSingleLogoutEndpointTests
// Assert
var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CancellationToken.None);
logoutResponse.StatusCode.ShouldBe(SamlStatusCode.Requester.Value);
logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Requester);
logoutResponse.StatusMessage.ShouldBe("Missing signature parameter");
}
@ -334,7 +334,7 @@ public class SamlSingleLogoutEndpointTests
// Assert
var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CancellationToken.None);
logoutResponse.StatusCode.ShouldBe(SamlStatusCode.Requester.Value);
logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Requester);
logoutResponse.StatusMessage.ShouldBe("Logout request expired (NotOnOrAfter is in the past)");
}
@ -360,7 +360,7 @@ public class SamlSingleLogoutEndpointTests
// Assert
var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CancellationToken.None);
logoutResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
}
[Fact]
@ -392,7 +392,7 @@ public class SamlSingleLogoutEndpointTests
// Assert
var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CancellationToken.None);
logoutResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
}
[Fact]
@ -421,7 +421,7 @@ public class SamlSingleLogoutEndpointTests
// Assert
var logoutResponse = await ExtractSamlLogoutResponseFromPostAsync(result, CancellationToken.None);
logoutResponse.StatusCode.ShouldBe(SamlStatusCode.Success.Value);
logoutResponse.StatusCode.ShouldBe(SamlStatusCodes.Success);
}
[Fact]

View file

@ -50,7 +50,7 @@ public class LogoutRequestParserTests
var result = _parser.Parse(doc);
result.ShouldNotBeNull();
result.Id.Value.ShouldBe("_test-logout-id");
result.Id.ShouldBe("_test-logout-id");
result.Issuer.ShouldBe("https://sp.example.com");
result.Destination!.ToString().ShouldBe("https://idp.example.com/saml/logout");
result.NameId.Value.ShouldBe("user@example.com");

View file

@ -168,7 +168,7 @@ public class SamlLogoutCallbackProcessorTests
var logoutResponse = result.Value;
logoutResponse.InResponseTo.ShouldBe("_request123");
logoutResponse.Destination.ShouldBe(sp.SingleLogoutServiceUrl!.Location);
logoutResponse.Status.StatusCode.ShouldBe(SamlStatusCode.Success);
logoutResponse.Status.StatusCode.ShouldBe(SamlStatusCodes.Success);
}
[Fact]

View file

@ -65,7 +65,7 @@ public class SamlProtocolMessageSignerTests
new XElement(assertionNs + "Issuer", "https://idp.example.com"),
new XElement(protocolNs + "Status",
new XElement(protocolNs + "StatusCode",
new XAttribute("Value", SamlStatusCode.Success.ToString()))));
new XAttribute("Value", SamlStatusCodes.Success))));
}
[Fact]

View file

@ -10,7 +10,7 @@ using Duende.IdentityServer.Internal.Saml;
using Duende.IdentityServer.Internal.Saml.Infrastructure;
using Duende.IdentityServer.Internal.Saml.SingleSignin.Models;
using Duende.IdentityServer.Models;
using SamlStatusCode = Duende.IdentityServer.Saml.Models.SamlStatusCode;
using Duende.IdentityServer.Saml.Models;
namespace UnitTests.Saml;
@ -58,7 +58,7 @@ public class XmlSignatureHelperTests
Destination = new Uri("https://sp.example.com/acs"),
Status = new Status
{
StatusCode = SamlStatusCode.Success
StatusCode = SamlStatusCodes.Success
},
};
@ -87,7 +87,7 @@ public class XmlSignatureHelperTests
IssueInstant = DateTime.UtcNow,
Issuer = "https://idp.example.com",
Destination = new Uri("https://sp.example.com/acs"),
Status = new Status { StatusCode = SamlStatusCode.Success },
Status = new Status { StatusCode = SamlStatusCodes.Success },
Assertion = new Assertion
{
IssueInstant = DateTime.UtcNow,
@ -131,7 +131,7 @@ public class XmlSignatureHelperTests
IssueInstant = DateTime.UtcNow,
Issuer = "https://idp.example.com",
Destination = new Uri("https://sp.example.com/acs"),
Status = new Status { StatusCode = SamlStatusCode.Success },
Status = new Status { StatusCode = SamlStatusCodes.Success },
Assertion = new Assertion
{
IssueInstant = DateTime.UtcNow,
@ -171,7 +171,7 @@ public class XmlSignatureHelperTests
IssueInstant = DateTime.UtcNow,
Issuer = "https://idp.example.com",
Destination = new Uri("https://sp.example.com/acs"),
Status = new Status { StatusCode = SamlStatusCode.Success }
Status = new Status { StatusCode = SamlStatusCodes.Success }
};
var responseElement = _responseSerializer.Serialize(response);
@ -236,7 +236,7 @@ public class XmlSignatureHelperTests
IssueInstant = DateTime.UtcNow,
Issuer = "https://idp.example.com",
Destination = new Uri("https://sp.example.com/acs"),
Status = new Status { StatusCode = SamlStatusCode.Success }
Status = new Status { StatusCode = SamlStatusCodes.Success }
};
var responseElement = _responseSerializer.Serialize(response);
@ -258,7 +258,7 @@ public class XmlSignatureHelperTests
IssueInstant = DateTime.UtcNow,
Issuer = "https://idp.example.com",
Destination = new Uri("https://sp.example.com/acs"),
Status = new Status { StatusCode = SamlStatusCode.Success }
Status = new Status { StatusCode = SamlStatusCodes.Success }
};
var responseElement = _responseSerializer.Serialize(response);
@ -313,7 +313,7 @@ public class XmlSignatureHelperTests
IssueInstant = DateTime.UtcNow,
Issuer = "https://idp.example.com",
Destination = new Uri("https://sp.example.com/acs"),
Status = new Status { StatusCode = SamlStatusCode.Success }
Status = new Status { StatusCode = SamlStatusCodes.Success }
};
var responseElement = _responseSerializer.Serialize(response);
@ -332,7 +332,7 @@ public class XmlSignatureHelperTests
IssueInstant = DateTime.UtcNow,
Issuer = "https://idp.example.com",
Destination = new Uri("https://sp.example.com/acs"),
Status = new Status { StatusCode = SamlStatusCode.Success }
Status = new Status { StatusCode = SamlStatusCodes.Success }
// No Assertion!
};