FatAlbert
FatAlbert

Reputation: 4970

Sustainsys.Saml2 Redirects to /Saml2/Logout, but Middleware Returns 404

Problem I'm trying to implement login and logout using an external IdP in a .NET 8 application with Sustainsys.Saml2.AspNetCore2 (Version=2.10.0).

Configuration in Startup.cs

var saml2Options = Configuration.GetSection("Authentication:Saml2").Get<Saml2Settings>();

services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = Saml2Defaults.Scheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddSaml2(options =>
{
    options.SPOptions.EntityId = new EntityId(saml2Options.EntityId);
    LoadCertificate(options, saml2Options.Certificate);

    options.IdentityProviders.Add(new IdentityProvider(
        new EntityId(saml2Options.IdentityProvider.EntityId),
        options.SPOptions)
    {
        MetadataLocation = saml2Options.IdentityProvider.MetadataLocation,
        LoadMetadata = true
    });
});

Logout Action Method This method initiates federated logout by sending a SAML LogoutRequest.

[HttpPost]
public IActionResult ExecuteLogout()
{
    var props = new AuthenticationProperties
    {
        RedirectUri = "/"
    };

    var user = HttpContext.User;

    var sessionIndex = user.FindFirst(Saml2ClaimTypes.SessionIndex)?.Value;
    if (!string.IsNullOrEmpty(sessionIndex))
    {
        props.Items.Add(Saml2ClaimTypes.SessionIndex, sessionIndex);
    }

    var logoutNameIdentifier = user.FindFirst(Saml2ClaimTypes.LogoutNameIdentifier)?.Value;
    if (!string.IsNullOrEmpty(logoutNameIdentifier))
    {
        props.Items.Add(Saml2ClaimTypes.LogoutNameIdentifier, logoutNameIdentifier);
    }

    return SignOut(props, Saml2Defaults.Scheme);
}

Attempted Fix: Creating an Action for /Saml2/Logout Even though I created an explicit controller method for handling /Saml2/Logout, it never gets called.

[AllowAnonymous]
[Route("Saml2")]
public class LoggedOutController : Controller
{
    private readonly ILogger<LoggedOutController> logger;
    private readonly Saml2Settings samlSettings;

    public LoggedOutController(ILogger<LoggedOutController> logger, IOptions<Saml2Settings> samlSettingsOptions)
    {
        this.logger = logger;
        this.samlSettings = samlSettingsOptions.Value;
    }

    [AllowAnonymous]
    [HttpGet("Logout")]
    public IActionResult Logout([FromQuery] string SAMLResponse)
    {
        logger.LogInformation($"User logged out. Saml response: {SAMLResponse}");
        return View("Logout");
    }
}

Any advice would be greatly appreciated!

Update: here is how the certificate is loaded

private static void LoadCertificate(Saml2Options options, CertificateSettings certSettings)
{
    var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    try
    {
        store.Open(OpenFlags.ReadOnly);
        var certificates = store.Certificates.Find(
            X509FindType.FindBySubjectName, certSettings.SubjectName, false);
        if (certificates.Count > 0)
        {
            options.SPOptions.ServiceCertificates.Add(certificates[0]);
        }
        else
        {
            throw new InvalidOperationException("Required certificate not found.");
        }
    }
    finally
    {
        store.Close();
    }
}

Upvotes: 0

Views: 41

Answers (0)

Related Questions