Skip to content

ISamlConfigurationResolver

The ISamlConfigurationResolver interface provides an alternative mechanism for supplying SAML configuration. Rather than configuring SAML at application startup, the resolver returns SAML configuration on demand as it is requested. This approach is intended for highly dynamic, multi-tenant, or frequently changing configurations.

The following code adds the SAML services and specifies a SAML configuration resolver.

// Add SAML services.
builder.Services.AddSaml()
    .AddSamlConfigurationResolver<CustomConfigurationResolver>();

As a convenience, the AbstractSamlConfigurationResolver base class may be extended to avoid implementing interface methods that are not required.

The configurationName parameter identifies the SAML configuration being requested and may be used to support multiple logical configurations or tenants.

Local Service Provider Example

The following is an example of implementing ISamlConfigurationResolver as the service provider.

In practice, these values are typically retrieved from a database or other external configuration store rather than being hard-coded.

using ComponentSpace.Saml2.Configuration.Resolver;

public class CustomConfigurationResolver : AbstractSamlConfigurationResolver
{
    public override Task<bool> IsLocalServiceProviderAsync(string configurationName)
    {
        return Task.FromResult(true);
    }

    public override Task<LocalServiceProviderConfiguration> GetLocalServiceProviderConfigurationAsync(string configurationName)
    {
        var localServiceProviderConfiguration = new LocalServiceProviderConfiguration()
        {
            Name = "https://ExampleServiceProvider",
            Description = "Example Service Provider",
            AssertionConsumerServiceUrl = "https://localhost:44360/SAML/AssertionConsumerService",
            SingleLogoutServiceUrl = "https://localhost:44360/SAML/SingleLogoutService",
            ArtifactResolutionServiceUrl = "https://localhost:44360/SAML/ArtifactResolutionService",
            LocalCertificates = new List<Certificate>()
            {
                new Certificate()
                {
                    FileName = "certificates/sp.pfx",
                    Password = "password"
                }
            }
        };

        return Task.FromResult(localServiceProviderConfiguration);
    }

    public override Task<PartnerIdentityProviderConfiguration> GetPartnerIdentityProviderConfigurationAsync(string configurationName, string partnerName)
    {
        if (partnerName != "https://ExampleIdentityProvider")
        {
            throw new SamlConfigurationException($"The partner identity provider {partnerName} is not configured.");
        }

        var partnerIdentityProviderConfiguration = new PartnerIdentityProviderConfiguration()
        {
            Name = "https://ExampleIdentityProvider",
            Description = "Example Identity Provider",
            SingleSignOnServiceUrl = "https://localhost:44313/SAML/SingleSignOnService",
            SingleLogoutServiceUrl = "https://localhost:44313/SAML/SingleLogoutService",
            ArtifactResolutionServiceUrl = "https://localhost:44313/SAML/ArtifactResolutionService",
            PartnerCertificates = new List<Certificate>()
            {
                new Certificate()
                {
                    FileName = "certificates/idp.cer"
                }
            }
        };

        return Task.FromResult(partnerIdentityProviderConfiguration);
    }

    public override Task<IList<string>> GetPartnerIdentityProviderNamesAsync(string configurationName)
    {
        IList<string> partnerIdentityProviderNames = new List<string> { "https://ExampleIdentityProvider" };

        return Task.FromResult(partnerIdentityProviderNames);
    }
}

Local Identity Provider Example

The following is an example of implementing ISamlConfigurationResolver as the identity provider.

In practice, these values are typically retrieved from a database or other external configuration store rather than being hard-coded.

using ComponentSpace.Saml2.Configuration.Resolver;

public class CustomConfigurationResolver : AbstractSamlConfigurationResolver
{
    public override Task<bool> IsLocalIdentityProviderAsync(string configurationName)
    {
        return Task.FromResult(true);
    }

    public override Task<LocalIdentityProviderConfiguration> GetLocalIdentityProviderConfigurationAsync(string configurationName)
    {
        var localIdentityProviderConfiguration = new LocalIdentityProviderConfiguration()
        {
            Name = "https://ExampleIdentityProvider",
            Description = "Example Identity Provider",
            SingleSignOnServiceUrl = "https://localhost:44313/SAML/SingleSignOnService",
            SingleLogoutServiceUrl = "https://localhost:44313/SAML/SingleLogoutService",
            ArtifactResolutionServiceUrl = "https://localhost:44313/SAML/ArtifactResolutionService",
            LocalCertificates = new List<Certificate>()
            {
                new Certificate()
                {
                    FileName = "certificates/idp.pfx",
                    Password = "password"
                }
            }
        };

        return Task.FromResult(localIdentityProviderConfiguration);
    }

    public override Task<PartnerServiceProviderConfiguration> GetPartnerServiceProviderConfigurationAsync(string configurationName, string partnerName)
    {
        if (partnerName != "https://ExampleServiceProvider")
        {
            throw new SamlConfigurationException($"The partner service provider {partnerName} is not configured.");
        }

        var partnerServiceProviderConfiguration = new PartnerServiceProviderConfiguration()
        {
            Name = "https://ExampleServiceProvider",
            Description = "Example Service Provider",
            AssertionConsumerServiceUrl = "https://localhost:44360/SAML/AssertionConsumerService",
            SingleLogoutServiceUrl = "https://localhost:44360/SAML/SingleLogoutService",
            ArtifactResolutionServiceUrl = "https://localhost:44360/SAML/ArtifactResolutionService",
            PartnerCertificates = new List<Certificate>()
            {
                new Certificate()
                {
                    FileName = "certificates/sp.cer"
                }
            }
        };

        return Task.FromResult(partnerServiceProviderConfiguration);
    }

    public override Task<IList<string>> GetPartnerServiceProviderNamesAsync(string configurationName)
    {
        IList<string> partnerServiceProviderNames = new List<string> { "https://ExampleServiceProvider" };

        return Task.FromResult(partnerServiceProviderNames);
    }
}

Database Configuration Resolver

SamlDatabaseConfigurationResolver is an implementation of the ISamlConfigurationResolver interface that retrieves SAML configuration stored in an Entity Framework database.

The following code adds the SAML services and specifies the SAML database configuration resolver.

// Add SAML services.
builder.Services.AddSaml().AddSamlDatabaseConfigurationResolver();

The SamlDatabaseConfigurationResolver is included in the separate ComponentSpace.Saml2.Configuration.Database NuGet package.

SQL Server

SamlDatabaseConfigurationResolver may be configured to use SQL Server to store the SAML configuration.

The following example connection string in appsettings.json specifies the SAML configuration database.

"SamlConfigurationConnection": "Server=localhost;Database=SamlConfiguration;Trusted_Connection=True;MultipleActiveResultSets=true"

The following code adds the SamlConfigurationContext using this configuration.

// Add the SAML configuration database context.
builder.Services.AddDbContext<SamlConfigurationContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("SamlConfigurationConnection"),
        builder => builder.MigrationsAssembly("DatabaseServiceProvider")));

Note

The MigrationsAssembly method specifies that migrations will be in the application's assembly rather than in the SamlConfigurationContext's assembly.

SQLite

SamlDatabaseConfigurationResolver may be configured to use SQLite to store the SAML configuration.

The following example connection string in appsettings.json specifies the SAML configuration database.

"SamlConfigurationConnection": "Data Source=SamlConfiguration.db"

The following code adds the SamlConfigurationContext using this configuration.

// Add the SAML configuration database context.
builder.Services.AddDbContext<SamlConfigurationContext>(options =>
    options.UseSqlite(Configuration.GetConnectionString("SamlConfigurationConnection"),
        builder => builder.MigrationsAssembly("DatabaseServiceProvider")));

Note

The MigrationsAssembly method specifies that migrations will be in the application's assembly rather than in the SamlConfigurationContext's assembly.

Initial Migration

The following example Visual Studio Package Manager Console command creates the initial migration.

Add-Migration InitialCreate -Context SamlConfigurationContext -Project DatabaseServiceProvider -StartupProject DatabaseServiceProvider -OutputDir Data\Migrations\SamlConfiguration

Database Creation

The following example Visual Studio Package Manager Console command creates the database.

Update-Database -Context SamlConfigurationContext -Project DatabaseServiceProvider -StartupProject DatabaseServiceProvider

Cached Configuration Resolver

SamlCachedConfigurationResolver is an implementation of the ISamlConfigurationResolver interface that caches, in memory, SAML configuration that's retrieved from a backing configuration resolver.

This is particularly useful when configuration is stored in a database and does not change frequently.

The following code adds the SAML services and specifies the SAML cached configuration resolver with SamlDatabaseConfigurationResolver as the backing configuration resolver.

// Add SAML services.
builder.Services.AddSaml().AddSamlCachedDatabaseConfigurationResolver();

The following code adds the SAML services and specifies the SAML cached configuration resolver with CustomConfigurationResolver as the backing configuration resolver.

// Add SAML services.
builder.Services.AddSaml()
    .AddSamlCachedConfigurationResolver<CustomConfigurationResolver>();

Configuration Name Resolution

The ISamlConfigurationNameResolver interface maps SAML message issuer names to partner configuration names.

A default implementation performs a one-to-one mapping which is appropriate for most use cases.

The following code adds the SAML services and specifies a SAML configuration name resolver.

// Add SAML services.
builder.Services.AddSaml()
    .AddSamlConfigurationNameResolver<CustomConfigurationNameResolver>();

Regular Expression Configuration Name Resolution

RegexSamlConfigurationNameResolver maps SAML message issuer names to partner configuration names using a regular expression.

The following code adds the SAML services and specifies the regular expression SAML configuration name resolver.

// Add SAML services.
builder.Services.AddSaml()
    .AddSamlRegexConfigurationNameResolver<CustomConfigurationNameResolver>();

Using the regular expression configuration name resolver, the following example SAML partner identity provider configuration matches any tenant in an Entra ID multi-tenant configuration.

The Name "https://sts.windows.net/" is used as a regular expression pattern and will match SAML message issuer names such as "https://sts.windows.net/f2f933ec-d7c9-433f-8926-d3a0732a7dcf/".

"PartnerIdentityProviderConfigurations": [
  {
    "Name": "https://sts.windows.net/",
    "Description": "Entra ID (Azure AD) multitenant",
  }
]