Using Cosmos DB as Identity Provider in ASP.NET Core 5 with EntityFramework Core
Background Story
I had an ASP.NET Core 5.0 Web Application based on the Angular SPA template and I needed to use a Cosmos DB database in the backend, but at the same time I wanted to use the Identity and Identity Server feature provided by ASP.NET Core.
And since this is not supported natively on Cosmos DB I decided to implement my own storage provider using the official EF Core Azure Cosmos DB Provider.
Fortunately the ASP.NET Core Identity ecosystem has a good architecture design and allows for customization/extension quite easily.
The NuGet Package
This task has been challenging and fun at the same time and the result was quite satisfying.
At the same time, I found very frustrating that Microsoft doesn’t provide an official Cosmos DB integration for Identity… after all Cosmos DB is a product owned by Microsoft.
These considerations made me decide to move the implementation of the storage provider to a NuGet package, in order to make it freely available to anyone.
GitHub Repository
If you’re interested to know how the provider is implemented in detail, you can head to its GitHub Repository.
It is MIT licenced, that means you can do whatever you want with the code: copy, edit, redistribute, whatever.
BUT…
If you’re really interested in making modifications to this library, I suggest you to think about contributing directly to the repository.
Installation (NuGet)
To install the required package you can use the Package Manager UI in Visual Studio or execute the following command directly in the Package Manager Console inside Visual Studio.
PM> Install-Package PieroDeTomi.EntityFrameworkCore.Identity.Cosmos
Please note that, to date, this package references version 5.0.3 of the ASP.NET Core Identity ecosystem packages, so you’ll be able to use it only if your project is targeting .NET 5.0.
Integration Steps
Project Requirements
The following steps assume that you have an ASP.NET Core 5 Web Application project that uses Identity and/or IdentityServer features.
Cosmos DB Requirements
➥ Database
Just as with EF Core on SQL Server, you have to manually create a database in your Cosmos DB instance to be able to operate.
➥ Containers
Since migrations are NOT supported when using EF Core on Cosmos DB, you’ll have to manually create the following containers in your database:
DbContext
You have to create a DbContext that implements the provided CosmosIdentityDbContext
type.
To start off you can create just an empty DbContext class that satisfies the above requirement:
public class MyDbContext : CosmosIdentityDbContext<IdentityUser>
{
public MyDbContext(DbContextOptions dbContextOptions, IOptions<OperationalStoreOptions> options)
: base(dbContextOptions, options) { }
}
Later in your development you’ll likely add some entities to your application: you’ll update the DbContext class adding the DbSet<T>
properties and overriding the OnModelCreating()
method for entity mappings:
public class MyDbContext : CosmosIdentityDbContext<IdentityUser>
{
public DbSet<SampleEntity> SampleEntities { get; set; }
public DbSet<OtherSampleEntity> OtherSampleEntities { get; set; }
public MyDbContext(DbContextOptions dbContextOptions, IOptions<OperationalStoreOptions> options)
: base(dbContextOptions, options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
// DO NOT REMOVE THIS LINE. If you do, your context won't work as expected.
base.OnModelCreating(builder);
// TODO: Add your own fluent mappings
}
}
As specified in the above code snippet, when overriding the OnModelCreating()
method it is crucial to not remove the base.OnModelCreating(builder)
call: if you do so, the identity configuration mappings won’t be applied and the application won’t work properly.
Configurations in Startup.cs File
➥ Remove the Default Identity Provider
Remove the line where the default/current identity provider is added/configured.
If you just created a new project, this line should be something like:
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
➥ Remove Default DbContext Configuration
Remove the line where the SqlServer DbContext is configured.
It should be something like:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
➥ Add Cosmos DB Identity Provider
Now add the Cosmos DB provider:
services.AddCosmosIdentity<MyDbContext, IdentityUser, IdentityRole>(
// Auth provider standard configuration (e.g.: account confirmation, password requirements, etc.)
options => ...,
// Cosmos DB configuration options
options => options.UseCosmos(
"your_cosmos_db_URL",
"your_cosmos_db_key",
databaseName: "your_db"
),
// If true, AddDefaultTokenProviders() method will be called on the IdentityBuilder instance
addDefaultTokenProviders: false
);
➥ Update IdentityServer Configuration (If Applicable)
If your project is using IdentityServer, update the related configuration in order to use your new DbContext implementation:
// Note that we're using MyDbContext as the second type parameter here...
services.AddIdentityServer().AddApiAuthorization<IdentityUser, MyDbContext>();
This provider & Identity UI
This provider is also compatible with Identity UI.
You can either use the default Identity UI (e.g.: in Startup.cs
there’s a call to AddDefaultUI()
method) or use the scaffolded version of Identity UI.
Both scenarios should work out of the box without having to do anything else.
Finally you can also use your own implementation of Identity UI, as long as you use the Identity services (e.g.: UserManager
and SignInManager
).
Available Services
This library registers a basic Cosmos DB EntityFramework repository implementation in the application service collection.
You can resolve an instance of this repository in your constructors requiring the IRepository
interface.
An example:
public class MyClass {
private readonly IRepository _repo;
public MyClass(IRepository repo) {
_repo = repo;
}
// ... Use the _repo instance methods to query the database
}
IRepository methods
Just for your information, here is a summary of the available methods in the IRepository
interface:
Table<TEntity>()
GetById<TEntity>(string id)
TryFindOne<TEntity>(Expression<Func<TEntity, bool>> predicate)
Find<TEntity>(Expression<Func<TEntity, bool>> predicate)
Add<TEntity>(TEntity entity)
Update<TEntity>(TEntity entity)
DeleteById<TEntity>(string id)
Delete<TEntity>(TEntity entity)
Delete<TEntity>(Expression<Func<TEntity, bool>> predicate)
SaveChangesAsync()
Conclusion
We’ve seen how to install and configure the Cosmos DB provider. It’s a simple task, isn’t it?
I hope you’ll find it useful, I’m currently using it for my project!
See you on the next article.