IdentityManager is an open source project that offers a modern alternative to the ASP.NET WebSite Administration tool that used to come bundled with Visual Studio. IdentityManager offered a simple user interface that allowed developers to bootstrap a new user store with users and role data and saw considerable popularity despite never being intended for production. IdentityManager was designed for ASP.NET & OWIN, supporting ASP.NET Identity 2 and Membership Reboot, which brings us to the topic of this article.
Introducing IdentityManager II
And that’s not the usual hyped up title. I'm running this project; therefore I get to introduce/announce it and use a silly font on my website.
IdentityManager2 is currently a straight port of IdentityManager but now using ASP.NET Core and ASP.NET Core Identity (aka ASP.NET Identity 3). My employer, Rock Solid Knowledge, is taking ownership of the IdentityManager project from Brock Allen and will be the primary maintainers from here on. IdentityManager1 will remain a read-only project. For more details on the reasoning of the change in ownership see the announcement post from Brock and Rock Solid Knowledge.
IdentityManager2 will be a side project for me. Contributors are welcome, major or minor. However, if you want to tackle something meaty, please don’t stealth it. Get in contact, and I’ll be happy to talk through and help plan any major pull requests. Initial migration of the core repository was done by Paul Glasson; I just got to cherry pick the fun bits.
IdentityManager2 will remain a tool for developers. It will never offer production level user management or offer features outside of user management. For production level user management, see AdminUI.
Setting up IdentityManager2 with ASP.NET Core Identity
Creating an IdentityManager2 project is fairly simple. First, create a new ASP.NET Core Web Application, using the “Empty” template (empty isn’t necessary, just cleaner for a demo or a standalone project).
dotnet new web
We then need to install IdentityManager2:
dotnet add package IdentityManager2
We then need to register the IdentityManager2 dependencies within our ConfigureServices
method:
services.AddIdentityManager();
And then register IdentityManager2 in our pipeline by using a Configure
method that looks something like the following:
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseIdentityManager();
app.UseEndpoints(builder => builder.MapDefaultControllerRoute());
}
That’s the basics of IdentityManager2 done.
All we need to do now is to tell it how to interact with our user store, by registering an implementation of IIdentityManagerService
.
An out of the box an implementation for ASP.NET Core Identity is available, so let’s use that:
dotnet add package IdentityManager2.AspNetIdentity -pre
We can then register this IdentityManagerService
by chaining our AddIdentityManager
call with:
services.AddIdentityManager()
.AddIdentityMangerService<AspNetCoreIdentityManagerService<IdentityUser, string, IdentityRole, string>>();
And then to get ASP.NET Core Identity working, we need to register some stores. Let's use Entity Framework for this example:
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.InMemory
Using the default IdentityUser
and IdentityDbContext
, we can add the following registrations:
services.AddDbContext<IdentityDbContext>(opt => opt.UseInMemoryDatabase("test"));
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<IdentityDbContext>()
.AddDefaultTokenProviders();
You should now have a working user & role management UI!
Protecting IdentityManager2 with IdentityServer4
Out of the box, IdentityManager2 allows anyone running it locally full access. You can, however, change this to your own authentication method, including an OpenID Provider such as IdentityServer4. Let’s take a look at how we can protect IdentityManager2 with IdentityServer4.
First, let’s configure our hosting app to use cookies for storing identity data, and OpenID Connect to get identity data:
services.AddAuthentication()
.AddCookie("cookie")
.AddOpenIdConnect("oidc", opt =>
{
opt.Authority = "http://localhost:5001";
opt.ClientId = "identitymanager2";
// default: openid & profile
opt.Scope.Add("roles");
opt.RequireHttpsMetadata = false; // dev only
opt.SignInScheme = "cookie";
});
Now we need to configure IdentityManager2 to use these authentication schemes for authenticating and challenging users:
services.AddIdentityManager(opt =>
opt.SecurityConfiguration =
new SecurityConfiguration
{
HostAuthenticationType = "cookie",
HostChallengeType = "oidc",
AdditionalSignOutType = "oidc"
});
Within IdentityServer, our client will look something like:
new Client
{
ClientId = "identitymanager2",
ClientName = "IdentityManager2",
AllowedGrantTypes = GrantTypes.Implicit,
RedirectUris = {"http://localhost:5000/signin-oidc"},
AllowedScopes = {"openid", "profile", "roles"}
};
Where the role scope, simply allows IdentityManager to receive the role claim type for use in its authorization decision:
new IdentityResource("roles", new List<string> {"role"});
Authorization
For authorization, IdentityManager will automatically look for an identity role of type “role” with a value of “IdentityManagerAdministrator”.
If your user does not have this claim, then they will receive a 403 Forbidden after authentication.
Both the claim type and value used for authorization can be modified using the RoleClaimType
and AdminRoleName
properties on IdentityManager’s SecurityConfiguration
options class.
By default, the Microsoft OpenID Connect middleware will transform claims from IdentityServer with the role
claim type to a claim type of http://schemas.microsoft.com/ws/2008/06/identity/claims/role
.
So, either set IdentityManager’s RoleClaimType
to this or simply do the following ceremony to prevent the map from happening:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
For API authorization, IdentityManager2 currently uses a hacky port of the old OWIN OAuth middleware. I have long-running plans to update this, which you track here.
Creating your own IIdentityManagerService
If you have an existing IIdentityManagerService
from IdentityManager1, then migration shouldn’t be too large of a task.
Just be aware that some methods now have asynchronous alternatives that you may find useful.
Take a look at the IdentityManager2.AspNetIdentity library for inspiration.
If you have a custom user store, then you will need to create your own IIdentityManagerService
as an adaptor between the two systems.
Again, check out the IdentityManager2.AspNetIdentity library for inspiration.