• +45 4949 9091
  • Support
  • Languages
    • Languages
    • English
    • Dansk
    • Svenska
    • Norsk
    • Deutsch
    • Nederlands
    • Polski
    • Français
    • Español
    • Italiano
    • Português
  • Sign up
  • Log in
FoxIDs logo
  • Docs & Tools
    • Documentation
    • Articles
    • JWT Tool
    • SAML 2.0 Tool
    • Certificate Tool
  • Use cases
    • Overview
    • Customers
    • Employees
    • Partners
    • Customer Stories
  • Services
  • Platform
    • Deployment
    • Multi-tenant
    • Identity Logic
    • Compliance
  • Pricing
  • Sign up
  • Log in

ITfoxtec Identity SAML 2.0

The open-source ITfoxtec Identity Saml2 package adds SAML 2.0 / SAML-P support for .NET applications that need to act as an Identity Provider (IdP), Service Provider (SP), or Relying Party (RP).

The ITfoxtec Identity Saml2 package is useful when SAML 2.0 belongs directly in the application. It supports SAML 2.0 login, logout, single logout, metadata, Redirect Binding, POST Binding, Artifact Binding, message signing and validation, ECDSA signatures on supported modern .NET targets, and configurable assertion encryption and decryption.

The company name ITfoxtec has changed to FoxIDs but the components will keep the ITfoxtec name as part of the component name for now.

Support

Ask questions on Stack Overflow and tag with 'itfoxtec-identity-saml2'.

For implementation help or architecture guidance, email contact@foxids.com.

Package

Releases NuGet ITfoxtec Identity SAML 2.0 NuGet ITfoxtec Identity SAML 2.0 MVC NuGet ITfoxtec Identity SAML 2.0 MVC Core

Code and license

Code on GitHub Test samples on GitHub Open-source license

When FoxIDs is relevant

There are two practical paths. Use the open-source library when SAML 2.0 should be implemented directly in your .NET application. If the application already supports OpenID Connect or WS-Federation, FoxIDs can handle the SAML 2.0 integration externally while the application keeps using its existing protocol.

  • Keep applications on OpenID Connect or WS-Federation while FoxIDs handles the SAML 2.0 connection
  • Operate protocol translation, certificates, metadata, and partner-specific federation configuration outside the application
  • Use FoxIDs Cloud, self-hosted, or hybrid deployment depending on operational requirements
  • FoxIDs uses ITfoxtec.Identity.Saml2 for SAML 2.0 protocol handling, so the library and bridge option are technically aligned
SAML 2.0 bridge documentation Talk to an expert Get started for free

Tested environments

The ITfoxtec Identity Saml2 package is tested for compliance with Microsoft Entra ID (Azure AD), AD FS, Azure AD B2C, the Danish NemLog-in3 (MitID), the Danish Context Handler (in Danish called Fælleskommunal Adgangsstyring) and many other IdPs and RPs.

ASP.NET MVC and ASP.NET Core MVC are supported by the ITfoxtec Identity SAML 2.0 MVC and MVC Core packages, which help integrate the ITfoxtec SAML 2.0 package into MVC and ASP.NET Core MVC applications.

Supported .NET versions

.NET 10.0 .NET 9.0 .NET 8.0 .NET 7.0 .NET 6.0 .NET Standard 2.1 .NET Framework 4.6.2 .NET Framework 4.8

Supported bindings

  • Redirect Binding
  • Post Binding
  • Artifact Binding

The bindings can be used as needed for:

  • Authn Request
  • Authn Response (SAML 2.0 Response)
  • Logout Request
  • Logout Response

Signing algorithms

RSA SHA1/SHA256/SHA384/SHA512, RSA-PSS SHA256, and ECDSA SHA256/SHA384/SHA512 are supported for message signing and validation. ECDSA support applies to supported modern .NET targets.

RSA SHA1 RSA SHA256 RSA SHA384 RSA SHA512 RSA-PSS SHA256 ECDSA SHA256 ECDSA SHA384 ECDSA SHA512
Show algorithm URIs
  • RSA SHA1http://www.w3.org/2000/09/xmldsig#rsa-sha1
  • RSA SHA256http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
  • RSA SHA384http://www.w3.org/2001/04/xmldsig-more#rsa-sha384
  • RSA SHA512http://www.w3.org/2001/04/xmldsig-more#rsa-sha512
  • RSA-PSS SHA256http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1
  • ECDSA SHA256http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256
  • ECDSA SHA384http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384
  • ECDSA SHA512http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512

Configurable encryption

Assertion encryption can be configured separately for data encryption and key encryption. Supported options include AES-CBC, AES-GCM, RSA key transport, and XML Encryption 1.1 RSA-OAEP with validated MGF parameters.

Data
AES-CBC AES-GCM
Key
RSA 1.5 RSA-OAEP RSA-OAEP 1.1

You can use the SAML 2.0 tool to decode tokens and create self-signed certificates with the certificate tool.

Code

The code shown is only a selection of the example code in GitHub.

The ITfoxtec Identity Saml2 package is integrated into an ASP.NET Core MVC Relying Party (RP) application by configuration in Startup and adding an Auth Controller with the following methods. The binding shown can be changed as needed depending on the requirements.

It is furthermore possible to set some optional parameters on the Saml2AuthnRequest and Saml2LogoutRequest. On the Saml2AuthnRequest e.g. ForceAuthn is supported, which will force the user to enter login credentials even though an SSO context already exists on the Security Token Service (STS) / Identity Provider (IdP).


Add configuration to the ConfigureServices method in Startup

Configuration using IdP metadata.
services.BindConfig<Saml2Configuration>(Configuration, "Saml2", (serviceProvider, saml2Configuration) =>
{
    saml2Configuration.SigningCertificate = CertificateUtil.Load(AppEnvironment.MapToPhysicalFilePath(
        Configuration["Saml2:SigningCertificateFile"]), Configuration["Saml2:SigningCertificatePassword"]);
    saml2Configuration.AllowedAudienceUris.Add(saml2Configuration.Issuer);

    var httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
    var entityDescriptor = new EntityDescriptor();
    entityDescriptor.ReadIdPSsoDescriptorFromUrlAsync(httpClientFactory, new Uri(Configuration["Saml2:IdPMetadata"])).GetAwaiter().GetResult();
    if (entityDescriptor.IdPSsoDescriptor != null)
    {
        saml2Configuration.AllowedIssuer = entityDescriptor.EntityId;
        saml2Configuration.SingleSignOnDestination = entityDescriptor.IdPSsoDescriptor.SingleSignOnServices.First().Location;
        saml2Configuration.SingleLogoutDestination = entityDescriptor.IdPSsoDescriptor.SingleLogoutServices.First().Location;
        saml2Configuration.SignatureValidationCertificates.AddRange(entityDescriptor.IdPSsoDescriptor.SigningCertificates);
    }
    else
    {
        throw new Exception("IdPSsoDescriptor not loaded from metadata.");
    }
    return saml2Configuration;
});
services.AddSaml2();
services.AddHttpClient();

Configuration without metadata.
services.BindConfig<Saml2Configuration>(Configuration, "Saml2", (serviceProvider, saml2Configuration) =>
{
    saml2Configuration.SigningCertificate = CertificateUtil.Load(AppEnvironment.MapToPhysicalFilePath(
        Configuration["Saml2:SigningCertificateFile"]), Configuration["Saml2:SigningCertificatePassword"]);
    saml2Configuration.AllowedAudienceUris.Add(saml2Configuration.Issuer);

    saml2Configuration.SignatureValidationCertificates.Add(CertificateUtil.Load(AppEnvironment.MapToPhysicalFilePath(
        Configuration["Saml2:SignatureValidationCertificateFile"])));
    return saml2Configuration;
});
services.AddSaml2();   

Login method in the Auth Controller

[Route("Login")]
public IActionResult Login(string returnUrl = null)
{
    var binding = new Saml2RedirectBinding();
    binding.SetRelayStateQuery(new Dictionary<string, string> 
        { { relayStateReturnUrl, returnUrl ?? Url.Content("~/") } });

    return binding.Bind(new Saml2AuthnRequest(config)).ToActionResult();
}

AssertionConsumerService method in the Auth Controller

After a successful or failed login, the ACS method receives the response.
[Route("AssertionConsumerService")]
public async Task<IActionResult> AssertionConsumerService()
{       
    var httpRequest = Request.ToGenericHttpRequest(validate: true);
    var saml2AuthnResponse = new Saml2AuthnResponse(config);

    httpRequest.Binding.ReadSamlResponse(httpRequest, saml2AuthnResponse);
    if (saml2AuthnResponse.Status != Saml2StatusCodes.Success)
    {
        throw new AuthenticationException($"SAML Response status: {saml2AuthnResponse.Status}");
    }
    httpRequest.Binding.Unbind(httpRequest, saml2AuthnResponse);
    await saml2AuthnResponse.CreateSessionAsync(HttpContext,
        claimsTransform: (claimsPrincipal) => Task.FromResult(ClaimsTransform.Transform(claimsPrincipal)));

    var relayStateQuery = httpRequest.Binding.GetRelayStateQuery();
    var returnUrl = relayStateQuery.ContainsKey(relayStateReturnUrl) ? relayStateQuery[relayStateReturnUrl] : Url.Content("~/");
    return Redirect(returnUrl);
}

Logout method in the Auth Controller

[HttpPost("Logout")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
    if (!User.Identity.IsAuthenticated)
    {
        return Redirect(Url.Content("~/"));
    }

    var binding = new Saml2PostBinding();
    var saml2LogoutRequest = await new Saml2LogoutRequest(config, User).DeleteSession(HttpContext);
    return binding.Bind(saml2LogoutRequest).ToActionResult();
}

LoggedOut method in the Auth Controller

After a successful or failed logout, the LoggedOut method receives the response.
[Route("LoggedOut")]
public IActionResult LoggedOut()
{
    var httpRequest = Request.ToGenericHttpRequest(validate: true);
    httpRequest.Binding.Unbind(httpRequest, new Saml2LogoutResponse(config));

    return Redirect(Url.Content("~/"));
}

SingleLogout method in the Auth Controller

Receives a Single Logout request and sends a response.
[Route("SingleLogout")]
public async Task<IActionResult> SingleLogout()
{
    Saml2StatusCodes status;
    var httpRequest = Request.ToGenericHttpRequest(validate: true);
    var logoutRequest = new Saml2LogoutRequest(config, User);
    try
    {
        httpRequest.Binding.Unbind(httpRequest, logoutRequest);
        status = Saml2StatusCodes.Success;
        await logoutRequest.DeleteSession(HttpContext);
    }
    catch (Exception exc)
    {
        // log exception
        Debug.WriteLine("SingleLogout error: " + exc.ToString());
        status = Saml2StatusCodes.RequestDenied;
    }

    var responseBinding = new Saml2PostBinding();
    responseBinding.RelayState = httpRequest.Binding.RelayState;
    var saml2LogoutResponse = new Saml2LogoutResponse(config)
    {
        InResponseToAsString = logoutRequest.IdAsString,
        Status = status,
    };
    return responseBinding.Bind(saml2LogoutResponse).ToActionResult();
}
FoxIDs logo

Developed in Denmark. Hosted in Europe.

Developers

  • Documentation
  • Articles
  • Releases
  • Cloud Status

Product

  • Pricing
  • Services
  • Deployment
  • Compliance
  • Trust Center

Company

  • About
  • SKI Supplier
  • Contact Us

Legal

  • Terms of Service
  • Self-Hosted Terms
  • GDPR and Data Protection Agreement
  • Privacy and Cookie Policy
© 2026 FoxIDs, version 2.15.13
GitHub LinkedIn

Cookie choices

FoxIDs uses necessary cookies to make the website work. Select Accept all to also allow analytics cookies that help us understand and improve the website, or Reject all to use necessary cookies only. You can change your choice at any time in our Privacy and Cookie Policy.

Read the Privacy and Cookie Policy