Skip to content

Integrating Thinktecture IdentityServer database with an existing database

February 3, 2013

This is post 3 in a short 3 part series on describing the database support in v2 of Thinktecture.IdentityServer. The parts of this series are:

  1. Database support in Thinktecture IdentityServer.
  2. EF migrations in Thinktecture IdentityServer.
  3. Integrating Thinktecture IdentityServer database with an existing database (this post).

With the default configuration, the database that contains the configuration information for IdentityServer is its own separate database. It’s understandable that you might want that configuration database to be merged with an existing database. This post provides an approach to solving this requirement.

If you are not using EF code first already for the existing database, then there’s little to do other than to create the schema needed for IdentityServer. This can be done with the migrations discussed in the last post. But if you are using an existing DbContext class and want to merge the IdentityServer schema into the same database then you will run into the “The model backing the ‘IdentityServerConfigurationContext’ context has changed since the database was created. Consider using Code First Migrations to update the database” error. The problem is that the two different DbContext classes want to “own” the schema and so the solution is to merge them.

To get started in understanding the solution, it’s helpful to inspect the code that revolves around the IdentityServer DbContext class, which is IdentityServerConfigurationContext. If you open the solution and locate the “Repositories” project and then open IdentityServerConfigurationContext.cs you will see the class. This is the gist of it:

public class IdentityServerConfigurationContext : DbContext
{
    public DbSet<GlobalConfiguration> GlobalConfiguration { get; set; }
    public DbSet<WSFederationConfiguration> WSFederation { get; set; }
    public DbSet<KeyMaterialConfiguration> Keys { get; set; }
    public DbSet<WSTrustConfiguration> WSTrust { get; set; }
    public DbSet<FederationMetadataConfiguration> FederationMetadata { get; set; }
    public DbSet<OAuth2Configuration> OAuth2 { get; set; }
    public DbSet<SimpleHttpConfiguration> SimpleHttp { get; set; }
    public DbSet<DiagnosticsConfiguration> Diagnostics { get; set; }

    public DbSet<ClientCertificates> ClientCertificates { get; set; }
    public DbSet<Delegation> Delegation { get; set; }
    public DbSet<RelyingParties> RelyingParties { get; set; }
    public DbSet<IdentityProvider> IdentityProviders { get; set; }
    public DbSet<Client> Clients { get; set; }

    public static Func<IdentityServerConfigurationContext> FactoryMethod { get; set; }

    public IdentityServerConfigurationContext()
        : base("name=IdentityServerConfiguration")
    { }

    public static IdentityServerConfigurationContext Get()
    {
        if (FactoryMethod != null) return FactoryMethod();

        return new IdentityServerConfigurationContext();
    }
}

So there are some 13 tables that contain various configuration information. The rest of IdentityServer then uses the repository pattern on these tables to provides access to the rest of the engine. The repositories locate the IdentityServerConfigurationContext via the Get method from the code snippet above.

Given the number of tables and static dependency on this class from within IdentityServer and its Get method, there are two approaches that come to mind to integrate IdentityServer’s DbContext with another DbContext class:

  1. Merge the other DbContext‘s tables into IdentityServer’s DbContext, or
  2. Extend IdentityServer’s DbContext class and put the new tables into the derived class

For the first suggestion this would entail using IdentityServerConfigurationContext as your primary DbContext and then the rest of your other code would use whatever approach desired to instantiate IdentityServerConfigurationContext for use at runtime. This approach means changing the code in IdentityServer and thus is brittle when there are future updates.

The second suggestion requires no code changes to IdentityServer and thus is the most adaptive when new changes are released. The trick with this approach is that IdentityServerConfigurationContext provides a factory API to create the context (notice the FactoryMethod property above). I’ll illustrate how to implement this approach.

The first step is to create a new context that inherits IdentityServerConfigurationContext and inside define any additional tables needed:

public class CustomContext : IdentityServerConfigurationContext
{
    public DbSet<SomeEntityClass> SomeTable { get; set; }
}

Next in global.asax we need to register the custom context with IdentityServer’s factory delegate and change the database initializer for the custom context:

protected void Application_Start()
{
    IdentityServerConfigurationContext.FactoryMethod = delegate()
    {
        return new CustomContext();
    };

    Database.SetInitializer<CustomContext>(new ConfigurationDatabaseInitializer());
    // this was the old one, so we can comment it our (or remove it)
    //Database.SetInitializer(new ConfigurationDatabaseInitializer());

   ...
}

And that’s it. Anytime IdentityServer needs an IdentityServerConfigurationContext it will be using your derived implementation with its additional tables.

HTH

4 Comments leave one →
  1. October 22, 2014 3:50 pm

    Targetting the AspNetIdentity implementation, I’ve created two seperate web applications (IdentityManager v3 & IdentityServer v3), in this order, that use the same SQLServer database.

    Using EF code first with migrations, within IdentityManager, I created the database with “InitialCreate” migration, added some new tables and relationships and then also changed the table names from “AspNetUsers” to “Users”, etc., by updating the OnModelCreating member of the “ApplicationDbContext : IdentityDbContext class Fluent-API.

    Now that I’ve finished the IdentityManager app and returned to the IdentityServer app, I cannot login via IdentityServer any longer. I’m struggling to understand how and where I should specify that the table “Users” should be used for authenticating as opposed to “AspNetUsers”.

    Should I extend IdentityDbContext into ApplicationDbContext on the IdentityServer as well and do the same migrations there or what should be the correct way of approaching this.

    Just to re-iterate, I did NOT change the schema of any tables, only the names and added new tables and relationships.

    • October 23, 2014 7:56 am

      Please open an issue on the github issue tracker for support questions.

  2. April 8, 2016 12:24 pm

    Do you know if this post is still valid with recent configurations of Entity Server?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: