There is no documentation at all.
I know I have to implement my own IUser, my own IUserSTore and somehow register them in the startup.cs. I removed all references to EntityFramework as I want to use a NoSQL backend.
The "convention" philosophy is very nice as long as it is documented and public.
Any hint?
Short answer - no, it's for sure possible, but not reasonable. MongoDB is document database and not support any physical relations between collections. EF is a good fit for relational databases like SQL, MySQL, etc. MongoDB works faster with embedded documents.
There is no official MongoDb provider implementation for EF core at this time, I did not see any mention of MongoDb in the . net core 7 (the next version) roadmap as of now.
Yes, you can access the database!
I just did a custom implementation of Identity 2.0, and as you said, I didn't find any useful documentation. But fortunately I managed to achieve my goal.
I'll answer your question assuming that you are using a N layered architecture, isolating your views from your business logic and your business logic from your data access layer. And assuming as well there's a dependency injection container being used such as Unity.
I'll explain the steps you must follow to use the Identity framework with a custom data access:
First you have to declare your domain class, implementing IUser and, if you want, adding custom properties to it:
//This class is implementing IUser with Guid as type because
//I needed to use Guid as default Id.
public class CustomUser : IUser<Guid>
{
public string CustomProperty { get; set; }
}
Then, in your business logic layer, you should have a class that handles all tasks related to the user authorization, login, password recovery, among others. This class must inherit from UserManager. The result would be something as follows:
// Business layer class must inherit from UserManager with
// CustomUser and Guid as types
public AuthorizationManager : UserManager<CustomUser, Guid>, IAuthorizationManager
{
private readonly ICustomUserMongoRepository repository;
private readonly ICustomEmailService emailService;
private readonly ICustomTokenProvider tokenProvider;
// Parameters being injected by Unity.
// container.RegisterType<ICustomUserMongoRepository, CustomUserMongoRepository>();
// ..
// ..
public AuthorizationManager(
ICustomUserMongoRepository repository,
ICustomEmailService emailService,
ICustomTokenProvider tokenProvider
)
// calling base constructor passing
// a repository which implements
// IUserStore, among others.
: base(repository)
{
this.repository = repository;
// this.EmailService is a property of UserManager and
// it has to be set to send emails by your class
this.EmailService = emailService;
// this.UserTokenProvider is a property of UserManager and
// it has to be set to generate tokens for user password
// recovery and confirmation tokens
this.UserTokenProvider = tokenProvider;
}
}
When inheriting from UserManager, it will provide a series of methods used by Identity and it will force your class to call the base constructor passing a repository, but not any repository, it's mandatory for the repository to implement the interfaces: IUserStore, IPasswordStore, depending on your requirements.
Here is when cool stuff happens. In your data access layer you must have your custom implementation of the repository pattern connecting to a NoSQL database (let's assume it's Mongo). So, your ICustomUserMongoRepository should look something like this:
public interface ICustomUserMongoRepository : IUserPasswordStore<CustomUser, Guid>, IUserEmailStore<CustomUser, Guid>, IUserRoleStore<CustomUser, Guid>
{
}
And your Mongo repository should be something like this
public CustomUserMongoRepository : MongoRepository<CustomUser>, ICustomUserMongoRepository
{
// Here you must have your custom implementation (using Mongo) of
// ICustomUserRepository which is requesting your class to
// implement IUserPasswordStore methods as well
public Task CreateAsync(CustomUser user)
{
//Custom Mongo implementation
}
public Task DeleteAsync(CustomUser user)
{
//Custom Mongo implementation
}
public Task GetEmailAsync(CustomUser user)
{
//Custom Mongo implementation
}
public Task GetEmailConfirmedAsync(CustomUser user)
{
//Custom Mongo implementation
}
// ...
}
Then your controller would look something like this:
public class AuthController : Controller
{
private readonly IAuthorizationManager manager;
// Manager being injected by Unity.
// container.RegisterType<IAuthorizationManager, AuthorizationManager>();
public AuthController(IAuthorizationManager manager)
{
this.manager = manager;
}
// Receives a LogInViewModel with all data needed to allow users to log in
[HttpPost]
public async Task<ActionResult> LogIn(LogInViewModel viewModel)
{
// FindAsync it's a method inherited from UserManager, that's
// using the Mongo repository passed to the base class
// in the AuthorizationManager constructor
var user = this.manager.FindAsync(viewModel.Email, viewModel.Password);
if(user != null){ // Log in user and create user session }
else { // Wrong username or password }
}
}
IMPORTANT!
The interface IAuthorizationManager is used to deliver quality software, based on SOLID principles. And if you look closer and put a deep thought into it, you'll notice that this interface must have all UserManager's methods to allow the AuthController to call all of the inherited methods by the AuthorizationManager from the UserManager class
Sorry for the long post. It's pretty hard to explain this whole process in few lines. I hope it helps. If you have any doubts or questions, comment this answer and I'll reply back as soon as possible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With