Manually Implementing an Identity Store for Asp.Net Identity

December 09, 2024

I recently had an Asp.Net application that I needed to add an individual account login to. You can get this for free by simply creating a new project and selecting Individual Accounts, but since I already had a user management system for use with a federated Google log-in, I wanted to use that same system.

It turns out it’s actually quite straightforward, but there’s a log going on behind the scenes which, if you’re not sure where all the magic code is coming from, can be confusing to say the least.

In this post, we’ll cover adding the UI pages, but using a custom user store (that is, we can manage the back-end ourselves).

1. Packages

To start with, you’ll need the following NuGet package:

Microsoft.AspNetCore.Identity.UI

I’m in .Net 8 here. We’ll talk a little about what that gives you in the next section, but this is, essentially, the thing that gives you the pages that you need; it works by providing a set of RazorPages - we’ll come back to that later.

2. Signin and the Magic Page

I have a Signin.html. If you’re interested where that came from then have a look at the previous, linked post, but basically, if you have a page that currently manages sign in, you’ll need to add something like this to it:

<div>
    <a class="btn btn-secondary" href="@Url.Page("/Account/Login", new { area = "Identity" })">
        <span>Sign in with Individual Account</span>
    </a>
</div>
<div>
    <a class="btn btn-secondary" href="@Url.Page("/Account/Register", new { area = "Identity" })">
        <span>Register as Individual User</span>
    </a>
</div>

The magic page is _ViewStart.cshtml and it needs to be located somewhere accessible via the routing - the default is: /Areas/Identity/Pages/. It looks like this:

@{
    Layout = "/Views/Shared/_Layout.cshtml";
}

If you don’t believe then the magic won’t happen!

3. UserStore

I had a service repository pattern, whereby by repository was using EF over MySql, and the service almost a pass-through. Here’s the definition of my service:

public class UserService : IUserService

What I changed here was inside the interface:

public interface IUserService : IUserStore<UserModel>, IUserEmailStore<UserModel>

Obviously, this means that you need to implement these new methods. In my case, for example, I changed CreateUser to

public async Task<IdentityResult> CreateAsync(UserModel user, CancellationToken cancellationToken)
{
    . . .
    var result = await _userDataAccess.CreateUser(user);
    . . .
    if (!result) return IdentityResult.Failed();
    return IdentityResult.Success;
}

There are a fair few of these, so this is probably the longest task. Since this is a personal project, I’ve left most of mine with the NotImplementedException for now, and plan to gradually sweep them up as I see errors over time.

4. Program.cs

The final step is the registration of all this stuff.

You first need to register the Identity UI:

builder.Services.AddDefaultIdentity<UserModel>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddUserStore<UserService>()
    .AddDefaultTokenProviders();

Finally, you need to tell it to use RazorPages - so you’ll need to add:

builder.Services.AddRazorPages();

. . .

app.MapRazorPages();

That’s it - you should now have individual log-ins working!

References

https://learn.microsoft.com/en-us/aspnet/identity/overview/extensibility/overview-of-custom-storage-providers-for-aspnet-identity

https://pmichaels.net/integrate-google-sign-in-asp-net/



Profile picture

A blog about one man's journey through code… and some pictures of the Peak District
Twitter

© Paul Michaels 2024