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:
Development Environment
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.
Solution Overview
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.
Liftoff 🚀
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:
User Model
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 OnModelCreating
method.
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 FullStackJobs
database.
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 AppUser
model.
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
class. 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 Startup.ConfigureServices
.
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).
In my previous post exploring Angular authentication with IdentityServer we used the Implicit authentication flow. However, today, the recommendation from the OAuth working group when it comes to JavaScript-based browser applications is to use PKCE instead as it does not expose tokens in the client URL. For more information please check out Brock Allen’s assessment of the changes to Implicit flow in OAuth2.
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.
Startup Configuration
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.
I added a test client in Config as a placeholder for now which we’ll use shortly with a bare-bones JavaScript client to validate our implementation.
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 RedirectUris
setting.
At this point, I set up a tiny JavaScript test-client to test things out using the oidc-client library to handle all of the necessary protocol interactions with our OpenID Connect Provider. We’ll explore the oidc-client in more detail when we implement it in the Angular project. For now, we’ll use this simple test client to help validate our AuthServer’s configuration.
Manual Test
Using Postman and our JavaScript test client, we can now validate the user creation and login functionality together. I fired up the project and then used Postman to issue a POST request to the action method we created earlier.
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.
In the integration test project, I added a new xUnit fixture responsible for starting up the Kestrel web server. I then installed the PuppeteerSharp library from nuget to automate the JavaScript test client in a browser.
I added a new CanLogin
test to AccountsControllerIntegrationTests
.
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.
Wrapping Up
In this post, we walked through the process to build out an ASP.NET Core-based backend using Entity Framework Core and IdentityServer that is capable of creating and authenticating user accounts using the authorization code flow with Proof-Key for Code Exchange (PKCE). We also added a simple JavaScript client based on the oidc-client library and some automated integration tests to validate all of our hard work.
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.