This blog posts shows how you can use MongoDB as persistence for your users and clients in IdentityServer 4.
I’ve used the MVC Sample from the IdentityServer4.Sample repository as a starting point
and replaced the InMemory version of the client store and user store.
I’ve decided to implement one common repository class that takes care of all the interaction with MongoDB and then use this repository from
the various services needed by IdentityServer.
The code snippet below shows how I’ve changed the configuration of services to replace the in-memory implementation
with a custom MongoDB implementation. I’ve commented out the lines where clients and users are added and instead added
my own implementations.
Now that I’ve shown you how to wire up the services, I’ll go ahead and implement them.
Implementing the MongoDB repository
The first thing we’ll do is to implement a repository that will take care of all the communication with MongoDB. For simplicity I’ve decided
to implement everything in the IdSrvHost project, but you could just as well move this into a separate project.
Below you can see the json representation of a user and client the way they will be stored in MongoDB. You probably want to
extend these models with additional properties, but this is a minimum to get you started.
User:
For simplicity I’ve stored ClientSecrets in MongoDB as plain text. You probably want to hash it before persisting it in a real world app.
Client:
To interact with the database, I first define an interface with 4 methods. We need to be able to retrieve a user by username,
retrieve a user by ID, validate the password for a given user and retrieve a client by id. You probably need a few more methods
to handle all relevant CRUD operations, but that should be pretty straight forward.
Below you can see the full implementation of the IRepository. I’m not a MongoDB expert, so there might be better ways to implement this, but it works :)
For this implementation you will need a couple of additional dependencies in project.json.
I’ve chosen to use the password hasher provided by Microsoft.AspNet.Identity.
And of course you need the MongoDB.Driver package to do the actual database queries. The MongoDB.Driver does not support dnxcore50, so you need
to remove this from the frameworks section and only target dnx451.
I’m also IOptions to inject a strongly typed configuration class via the constructor. Make sure to set your MongoDB connection string and database name in appsettings.json or another valid
configuration source.
Now that we have the necessary parts of the repository in place, we can continue implementing the interfaces needed by IdentityServer.
Implementing the User store
There are two interfaces you need to implement in order to have a working user store; IProfileService and IResourceOwnerPasswordValidator.
The MongoDbProfileService is basically just retrieving a user from MongoDB and mapping it to claims which are set on the context.
The next interface we need to implement is the IResourceOwnerPasswordValidator. Again we are simply injecting the IRepository and calling the appropriate methods we implemented earlier.
In order to retrieve the clients from MongoDB, we’ll also implement IClientStore. Nothing fancy here either. We’re just retrieving the
client via our repository and mapping it to a Client object. As I mentioned above, I’m keeping the client secrets as plain text in mongo db. Here you
can see I’m hashing it using the Sha256() extension method before returning it. You probably want to hash it before storing it to MongoDB, and in that case
you should also remove the redundant hashing before returning it by FindClientByIdAsync.
Finally we’ll change the LoginService to use our repository instead of the InMemory users.
You should now have a a working IdentityServer4 where the users and clients are retrieved from MongoDB.