Identity Server: Using ASP.NET Core Identity
This post is a continuation of a series of posts that follow my initial looking into using IdentityServer4 in ASP.NET Core with an API and an Angular front end. The following are the related posts.
Identity Server: Introduction
Identity Server: Sample Exploration and Initial Project Setup
Identity Server: Interactive Login using MVC
Identity Server: From Implicit to Hybrid Flow
Identity Server: Using ASP.NET Core Identity (this post)
Identity Server: Using Entity Framework Core for Configuration Data
Identity Server: Usage from Angular
This post is going to cover using ASP.NET Core Identity instead of an in-memory user store like the previous examples. As I write this I am working through the Using ASP.NET Core Identity quick start from the docs. This isn’t going to differ a whole lot from the official docs, but I still want to document it to help solidify everything in my head. The starting point of the code for this post can be found here.
Identity Application
The Identity Application will be where the bulk of the changes happen. Since it is much easier to add IdentityServer to a project than it is to add ASP.NET Core Identity we are going to delete the existing Identity Application project and re-create it with Identity from the start. Right click on the IdentityApp project and click remove.
This removes the project from the solution, but the files also need to be deleted off of disk or use a different name. I chose to rename the old project folder on disk so I could still grab any files I might need.
Create a new Identity Application
Right-click on the solution and select Add > New Project.
On the Add New Project dialog under Visual C# > .NET Core select ASP.NET Core Web Application and enter the name of the project (IdentityApp in this example) and click OK.
On the next dialog select the Web Application template.
Next, click the Change Authentication button and select the Individual User Accounts option.
Click OK on the Change Authentication dialog and then click OK on the template dialog. After a few seconds, the solution will contain a new IdentityApp that is using ASP.NET Core Identity with Entity Framework Core.
Adding Identity Server to the Identity App Project
Using NuGet install the IdentityServer4.AspNetIdentity package which will also install IdentityServer4 which the old project was using. Next, copy the Config class from the old IdentityApp project and delete the GetUsers function.
Startup Changes
In the Startup class at the end of ConfigureServices function add the following.
services.AddIdentityServer() .AddTemporarySigningCredential() .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()) .AddAspNetIdentity<ApplicationUser>();
The only difference between this and the one used with the previous posts is instead of AddTestUsers being used to pull a hard coded list of uses our of the Config class users are pulled from the database using ASP.NET Core Identity using this AddAspNetIdentity<ApplicationUser>() call. Identity Server is very flexible and this is only of the option for an identity store.
Next, in the Configure function add app.UseIdentityServer() after app.UseIdentity().
app.UseStaticFiles(); app.UseIdentity(); app.UseIdentityServer(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });
Database
There are a couple of ways to make sure the database is created and migrated when changes happen. One is via the command line in the project directory using the following command.
dotnet ef database update
The way I normally us when at this stage in development is to add code to the DB context to automatically apply migrations. The following is the full ApplicationDbContext class modified to automatically run migrations when the context is constructed.
public sealed class ApplicationDbContext : IdentityDbContext<ApplicationUser> { private static bool _migrated; public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { if (_migrated) return; Database.Migrate(); _migrated = true; } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); } }
Now throw that all away
The above is good to go through to know how things work, but I got to this point and wanted the functionality to be on par with the previous entries. The docs made this sound simple, but it was not simple at all I had a list of at least 30 points of files to be moved and changes made to existing files. I am going to spare you all those details and recommend that you just pull the Identity Application from my GitHub repo for this project instead. I did basically the same thing out of the official samples repo to get things working as they should with contents, errors, and log out.
If you want the auto migrations then make sure and keep the version of the ApplicationDbContext from above.
Client Application
No changes are actually required to the client application, but as in the official docs, I made changes to show hitting the API Application using both a user access token and client credentials. The following is the index action on the IdentityController which has been changed to call two functions one for each type of API access.
[Authorize] public async Task<IActionResult> Index() { var apiCallUsingUserAccessToken = await ApiCallUsingUserAccessToken(); ViewData["apiCallUsingUserAccessToken"] = apiCallUsingUserAccessToken.IsSuccessStatusCode ? await apiCallUsingUserAccessToken.Content.ReadAsStringAsync() : apiCallUsingUserAccessToken.StatusCode.ToString(); var clientCredentialsResponse = await ApiCallUsingClientCredentials(); ViewData["clientCredentialsResponse"] = clientCredentialsResponse.IsSuccessStatusCode ? await clientCredentialsResponse.Content.ReadAsStringAsync() : clientCredentialsResponse.StatusCode.ToString(); return View(); }
The following is the function to access the API Application using a user access token.
private async Task<HttpResponseMessage> ApiCallUsingUserAccessToken() { var accessToken = await HttpContext.Authentication.GetTokenAsync("access_token"); var client = new HttpClient(); client.SetBearerToken(accessToken); return await client.GetAsync("http://localhost:5001/api/identity"); }
Now the function to access the API Application using client credentials.
private async Task<HttpResponseMessage> ApiCallUsingClientCredentials() { var tokenClient = new TokenClient("http://localhost:5000/connect/token", "mvc", "secret"); var tokenResponse = await tokenClient.RequestClientCredentialsAsync("apiApp"); var client = new HttpClient(); client.SetBearerToken(tokenResponse.AccessToken); return await client.GetAsync("http://localhost:5001/api/identity"); }
Finally, Index.cshtml found in the Views/Identity directory has the following change.
Replace: @ViewData["apiResult"] With: <dt>api response called with user access token</dt> <dd>@ViewData["apiCallUsingUserAccessToken"]</dd> <dt>api response called with client credentials</dt> <dd>@ViewData["clientCredentialsResponse"]</dd>
Wrapping up
Now the Identity Application is using ASP.NET Core Identity with Entity Framework Core to store users in the database. The next post will cover moving the items now in the Config class into the database. The completed version of the code can be found here.
Identity Server: Using ASP.NET Core Identity Read More »