Build an Authenticated GraphQL App with Angular, ASP.NET Core and IdentityServer – Part 1
Whatever end of the software development stack you spend the majority of your time in, if you’re building a modern web or mobile application in 2019, you’ve at least heard of or are actively working with GraphQL in some capacity.
Originally created and open-sourced by Facebook back in 2015 its rapid adoption saw it move to its own foundation in 2018 to be maintained by The Linux Foundation technology consortium.
Its become popular due to its ability to simplify client-server interaction while improving the developer experience and productivity. These benefits are a result of reducing the amount of friction and complexity front-end, and back-end teams face while trying to build critical data integrations between application UIs and backend APIs. With such a high emphasis on flexibility and speed to market in today’s software solutions, GraphQL is taking preference over traditional REST APIs for these reasons.
Last year I did an introductory post on using GraphQL dotnet with ASP.NET Core. That article still generates some questions from folks involving more real-world use cases that require things like user authentication and UI integration with a given front-end web or mobile technology.
With the recent release of ASP.NET Core 3 and continued interest in GraphQL since my original post, it’s a great time to look at a complete example that includes an Angular front-end along with IdentityServer on the back-end to build a fully integrated, modern GraphQL solution.
There’s a bit to cover here, so as you may have gathered from the title, this will be part one of a multi-part series.
To start, we’ll begin the project on the back-end by building out an authentication and identity server using IdentityServer4.
So, grab a refill of your favorite beverage and let’s get to it! ☕️
Posts in This Series
Get notified on new posts
Straight from me, no spam, no bullshit. Frequent, helpful, email-only content.
The Demo App
FullStack Jobs is a simple yet mighty little job board we’re going to build and includes the following functionality:
As of December 2019, this guide was created using the most recent versions of:
Running the Solution
See the repository’s readme for the steps required to build and run the demo application.
Here’s a diagram showing the major pieces of our solution architecture along with the authentication and data flows that tie them together.
We’ll take a detailed look at each of these components and their integrations as we work through building out the application. In this post, we’ll be tackling the Identity / Authorization Server.
Auth Server Requirements
Before we look at the implementation, here are the key responsibilities of the Auth Server:
IdentityServer handles the first two as it is our OpenID Connect and OAuth framework. Functions for creating and managing user accounts are not part of IdentityServer as it is for authorizing users. But, for our purposes, it’s convenient to include the user registration endpoint here as it will share the user database and ASP.NET Core and Entity Framework Core Identity bits we’ll also use with the IdentityServer framework.
To get started, I set up a brand new solution for the AuthServer. Next, I added a new ASP.NET Core Web Application project using the empty template followed by a new .NET Core 3.1 Class Library project that will house the data access and other infrastructure bits for IdentityServer which we’ll be tackling next.
Finally, I created a new empty solution with a solution folder and added the two projects to it. This structure will allow us to add the GraphQL project down the road in the same fashion and make it easier for us to work with and run both backend projects simultaneously.
At this point, the solution looks something like this:
Next, I added a new AppUser class derived from ASP.NET Core’s
IdentityUser entity which will serve as the centerpiece of our user account model.
Pretty simple class, however, because we’re inheriting from
IdentityUser, we’re able to easily extend the default set of properties by adding a custom one for
FullName. Because Identity makes use of an Entity Framework Core data model by default, this property will automatically map to a new database column in the
AspNetUsers table, which we’ll create shortly.
ASP.NET Core Identity provides its own Entity Framework database context in
IdentityDbContext and customization options by deriving from this type.
So, I added a new AppIdentityDbContext class and passed our
AppUser type as a generic argument for the context.
This guy is pretty barebones too. For our demo, he’ll remain that way; however, the additional configuration of Identity model types would go in the
Entity Framework Core Migrations
With a data model and context created, we’re ready to add a migration to translate this model into changes we can apply to the database.
Since our DbContext lives in a class library project, we need to give Entity Framework Core’s Tools a little help in creating it before we can use any of the CLI commands to create and apply a migration. This is done by implementing the
IDesignTimeDbContextFactory<TContext> interface within a class in the same project as the DbContext(s).
I added a new AppIdentityDbContextFactory for the context we created and PersistedGrantDbContextFactory for IdentityServer’s operational data store support.
Finally, I added an appsettings.json file containing the database connection string.
We’re now ready to create and apply the EF migrations using the CLI tools.
From the command prompt, I generated a migration for each context by running the following commands from the root of the
FullStackJobs.AuthServer.Infrastructure project folder.
After running the commands, I see a new Migrations folder in the root of the infrastructure project containing the generated migration files.
Finally, I used the
database CLI command to generate the sql database by applying the migrations for each DbContext.
After they completed, I popped open SQL Server Management Studio and found the newly created
If you have a look at the columns in the
AspNetUsers table you will see a
FullName column which maps to the property we added earlier on the
AuthServer Web Application & API
With the database created, we’re ready to begin adding some real functionality to the web application project we created earlier.
Before we can look at how to authenticate and authorize users, we need a mechanism to create new user accounts using ASP.NET Core’s Identity membership API.
To handle this, I added a new AccountsController to the project and a single action method to handle the HTTP POST action.
The action method expects an incoming SignupRequest model sent as part of the request body.
From there, we initialize a new
AppUser instance with the incoming values from the request object and then pass that as an argument along with the password the to
CreateAsync method of the
UserManager provides various APIs for managing users stored in the configured Identity database.
Finally, if account creation succeeds, we call on the
UserManager again to add some custom claims to the user. We’ll make use of these claim values a little further down the road as we add the authentication and integration layers between the Angular client and GraphQL API.
Writing an Integration Test
ASP.NET Core provides a slick integration test framework that allows you to write simple tests that ensure the different infrastructure components in your application like the database, filesystem, and request-response pipeline are all playing nicely together. The coolest part is that it uses HttpClient to allow you to poke at your API/Razor Page/View “from the outside” in a similar fashion as a mobile, SPA, or browser client would.
I spun up a new xUnit test project with a test class for the AccountsController and then wrote a simple test for the happy path of our account create method.
The real magic behind these tests comes from the WebApplicationFactory which provides a test server to host your application as well as an entry point to override any components or services registered in your app’s
For example, we can use a different database in our tests instead of the app’s configured database. The app’s database context can be swapped in
builder.ConfigureServices to use an in-memory version instead. I wrote a AuthServerWebApplicationFactory to do just that.
Authentication with IdentityServer
With the capability in place to create new users, we’re ready to add a login mechanism to authenticate them. In the next set of steps, we’ll add support for interactive user authentication via the OpenID Connect protocol using the Proof Key for Code Exchange flow (PKCE).
To start setting up IdentityServer, I installed the IdentityServer4.AspNetIdentity package via nuget. As the name implies, this package provides integration bits between ASP.NET Core Identity and IdentityServer.
Our first step when implementing IdentityServer is to configure it in the app’s Startup class.
Note, for development purposes, we’re using a temporary key generated at startup as indicated by
AddDeveloperSigningCredential. However, for production scenarios, you’ll want to replace this with a real certificate. Refer to the docs for more details on how to configure key material.
One of the most powerful features of IdentityServer IMHO is its ability to provide a single, centralized authentication and authorization solution that can be used across multiple applications by simply configuring a client to represent each application we’d like to interact with IdentityServer.
We’ll explore the client configuration further and these settings when we add a new client config to represent our Angular SPA in the next post.
The great thing about IdentityServer is that all the complex protocol support needed for OpenID Connect comes baked in. So, to hook up a login form to perform the authentication step, we’re only on the hook to provide the necessary MVC UI parts and controller support. To make things even easier, the IdentityServer repo contains a very handy Quickstarts section that contains templates for getting off the ground with various clients and OAuth/OIDC flow scenarios.
Using the quickstart samples as a guide, I added a new Login view and associated controller action within the
AccountsController to handle the login form postback. The action method is pretty straightforward. First, IdentityServer validates the incoming authorization request, and then the user’s credentials are validated by the Identity system. If everything checks out, the browser gets redirected back to the client application that initiated the request at the location defined by their client
I got a
200 OK response back and a quick check of the
AspNetUsers table in the database shows the new account!
To test the authentication portion, I pointed my browser to the test client at http://127.0.0.1:8787/test-client/index.html and used the account we made in the previous step to test the login flow between the test client, login view and IdentityServer. It doesn’t do much. Still, it does use the same client library and APIs we’ll implement in the Angular project, so testing in this fashion now sets us up for success later as we know the core functionality of the AuthServer is working as expected. ✔️
With everything in place, I fired up the project and was able to trigger and complete the authentication handshake successfully using the test client and login view.
Automated Integration Test with Puppeteer Sharp and Kestrel
We used ASP.NET Core’s TestServer in our first test to spin up an in-memory application host and database to validate these components worked properly within the account creation endpoint.
This setup works great for simple tests against a single endpoint using a given
HttpRequestMessage to assert the controller generates the expected response. However, TestServer does not open a real network socket, and as such, cannot be used with a browser to simulate more complicated scenarios like the manual test we just did involving Postman and our browser.
Luckily, we have access to a lightweight and fast application server in Kestrel which we can spin up as part of our test context to host the application while using Puppeteer Sharp to control a headless-chrome instance that automates the browser’s behavior.
I added a new
CanLogin test to
In this test, I’m emulating the manual steps we performed above to create a new user in the in-memory database and then using Puppeteer Sharp to initiate and execute the login in the browser.
In the next post, we’ll start looking at the Angular client and work through integrating it with our auth server to begin adding some real functionality to our app, so stay tuned!
Thanks for reading and happy programming!
Straight from me, no spam, no bullshit. Frequent, helpful, email-only content.
This content was originally published here.