The quickest way to set up an OpenIddict server with the Rock Solid Knowledge Saml component is to use the available template.
Prerequisites
Before you can get started you will need:
- The .NET 6 SDK installed.
- A license for the SAML component.
Obtaining a license
If you do not already have a license for the SAML component, you can obtain a demo license valid for 30 days from here.
Installing the template
To install the OpenIddict+SAML template, invoke the following command at a command prompt:
dotnet new install Rsk.Saml.OpenIddict.Template
Updating the template
From time-to-time, new versions of the template may be released, to update the latest version, run the following command at a command prompt:
dotnet new update Rsk.Saml.OpenIddict.Template
Removing the template
If you would like to remove the template, you can do this by executing the following at the command line:
dotnet new uninstall Rsk.Saml.OpenIddict.Template
Creating a project from the installed template
Once you have installed the template, you can create a new OpenIddict server with the Saml component included. The template requires a --dbprovider
parameter, this parameter controls which database provider to use when generating the project. The available databases are SqlServer
, MySql
, PostgreSql
and Sqlite
.
For example, to create a new project with the Saml+OpenIddict template with PostGres as the database provider, run the following command in the command line in the directory of your choice.
dotnet new Saml+OpenIddict –-dbprovider PostgreSql
Once the project has been created, it is necessary to replace the "add-your-connection-string-here" text in appsettings.json with the connection string you wish to use.
If the connection string in this file is invalid according to your database provider, the created project will fail to start with an exception.
Adding your license information
Before you can run the newly created project, you will need to add the license information you obtained in the previous step.
To do this, open the project in your IDE of choice and find the LicenseKey
class.
This class contains a Licensee constant which should be changed to the licensee value you were provided with. This will be DEMO
if you are using a demo license key.
The Key
constant should be changed to the license key value.
Failing to add a valid license will cause an exception to be thrown when the project first starts.
Generating EntityFramework migrations
If you are using a persistent provider you will need to generate EntityFramework migrations.
The template uses 4 DbContexts, and each DbContext requires its own migrations, to generate migrations you can run the following commands at the command-line in the folder in which your project was created:
- dotnet ef migrations add InitialApplicationMigration --context ApplicationDbContext
- dotnet ef migrations add InitialSamlMessageMigration --context OpenIddictSamlMessageDbContext
- dotnet ef migrations add InitialSamlConfigurationMigration --context SamlConfigurationDbContext
- dotnet ef migrations add InitialSamlArtifactMigration --context SamlArtifactDbContext
For more information on migrations see Microsoft's documentation
Running EntityFramework Migrations
After you have generated migrations, you need to update the database you wish to use to reflect the schema required by our SAML component.
To do this, update the database with each migration you created earlier as follows: - dotnet ef database update InitialApplicationMigration --context ApplicationDbContext - dotnet ef database update InitialSAMLMessageMigration --context OpenIddictSamlMessageDbContext - dotnet ef database update InitialSAMLConfigurationMigration --context SamlConfigurationDbContext - dotnet ef database update InitialSAMLArtifactMigration --context SamlArtifactDbContext
Integrating with Quartz
The template uses the Quartz.Net scheduler to remove unwanted data. You will need to set up a Quartz job store for this to work correctly.
Configuring a quartz job store
Quarts by default uses a job store that stores all data in memory. While this will work for getting started, we do not recommend you use this for production workloads.
For production, we recommend you use the AdoJobStore.
To learn how to set this up, please refer to the Quartz tutorial here.
Initializing a database for the Quartz AdoJobStore.
If you wish to use the AdoJobStore, you will need to run a database script to initialize the required tables.
The script you will need depends on the database provider you wish to use.
The below table provides links to the SQL scripts required for each provider.
If you are using MySql, the quartz SQL script will place the tables in a database called quartznet. Alternatively, if you are using SQL Server, you will need to specify the database you wish to use. Before running the script it is necessary to replace the "enter_db_name_here" text with an existing database. This could be the same database being used for Openiddict but this does not have to be the case.
Seeding Test Data
To seed data the template project uses a hosted service. The StartAsync
is run during startup and by default will seed a test user if TestUsername
, TestUserEmail
, TestUserPassword
are provided. The worker will also create scopes representing the default OIDC scopes to allow for claims mapping.
public async Task StartAsync(CancellationToken cancellationToken)
{
await using var scope = _serviceProvider.CreateAsyncScope();
await CreateTestUser(scope);
await CreateOIDCScopes(scope);
await SaveAllChanges(scope);
}
To quickly create additional Clients, ServiceProviders, Scopes and Users to test with, you can use the following methods in the worker class.
CreateClientIfNotExist
CreateServiceProviderIfNotExists
CreateScopeIfNotExists
CreateUserIfNotExists
Creating a Test SAML ServiceProvider
To create a ServiceProvider you will need to create both a ServiceProvider and an OpenIddict Application where both the OIDC ClientId
and Saml EntityId
match. The OIDC client will also need permission to access scopes to generate Saml assertions. For more information on how to configure a ServiceProvider see our documentation
await CreateClientIfNotExists(scope, "https://localhost:5001/saml", x =>
{
x.ClientId = "https://localhost:5001/saml";
x.Permissions.UnionWith(new [] { Permissions.Scopes.Email });
});
await CreateServiceProviderIfNotExists(scope, "https://localhost:5001/saml", x =>
{
x.EntityId = "https://localhost:5001/saml";
x.AssertionConsumerServices.Add(new Service(SamlConstants.BindingTypes.HttpPost, "https://localhost:5001/signin-saml-openIddict"));
x.SingleLogoutServices.Add(new Service(SamlConstants.BindingTypes.HttpRedirect, "https://localhost:5001/signout-saml"));
x.SigningCertificates = new List<X509Certificate2> { new X509Certificate2("Resources/SigningCertificate.cer") };
});