I find that in many cases application developers need to create web applications that can support not only the web application itself but an API that might be used by a Mobile Application or otherwise. I have seen some methods used in the past to create these two interfaces, everything from home-grown security to the creation of two separate applications that do the same thing. With ASP.NET Core we can use multiple authentication providers so we can easily support various providers and control when each should apply. In this post, I explore the process of supporting multiple authentication providers.
The Goal
Before I get into the specifics of how to accomplish, let's discuss the goal. This post assumes that you have an already working ASP.NET Core 2.0 application using ASP.NET Identity. Your application is working, you have various parts of the application secured, and users can access what they should. You now want to expand your application to have an API for a mobile application. The existing application and controllers should continue to use Identity. However, you would like to use Bearer Tokens to support the mobile API's.
We want this to be in the same project to ensure the best reusability within the application, and we want to do this with the least amount of impact to the existing application.
Supporting Items
Before we get into the "meat" of the code, there are a few supporting objects that we need to create first. Each of these items should be added to your project as individual classes.
CredentialsViewModel.cs
This view model is what will be used to accept authentication credentials from the user asking for a bearer token. This is a simple POCO object with data annotations for validation.
JwtIssuerOptions.cs
This object stores information and options regarding the configuration of the token and how the token will be issued.
IJwtFactory.cs
This interface defines a factory that will actually work to issue needed items for tokens.
JwtFactory.cs
This is the concrete implementation of the factory that will create the token as needed.
TokenHelper.cs
This class contains a helper method that actually creates the token response that will be returned to the user requesting a token.
Configuration Elements
With the foundational items ready for us, we need to set up our configuration elements. These can be added to your appsettings.json file, or to environment variables as needed. The below snippet shows the basic configuration, assuming a local installation.
In each of your environments, you will want to make sure that you have a proper audience value matching your current URL.
Startup.cs Changes
The last piece is to add a few items to the Startup.cs file to actually work this in!
Additions to ConfigureServices()
In this section, we need to configure our options file, as well as set up how the tokens will be verified. This can be done with the following code.
We also need to update our call to AddAuthorization, setting up a policy that outlines the expected claim for a valid API user. This is done with a one-line addition to any existing authorization configuration for policies.
The last change is to set a default authentication scheme as well as to configure how JwtBearer should be used. This is done using the following snippet.
Understandably, there is a bit of code here, but with this all out of the way, we can now focus on the implementation.
Issuing Tokens - AuthController.cs
The final big code addition is the creation of an AuthController API method that will allow users to request a token. The desired process for this is for a user to make an HTTP Post to /api/auth/login with passed credentials if the login is valid the user will receive a token back. The below snippet is a complete implementation of an AuthController, assuming a UserObject of UserProfile.
A user making a request will get a JSON response with their token. that they can then use for all future requests.
Using this New Method
With all of the setup out of the way, we can now focus on the fun part, how do we use this? Since we set a default authentication scheme all existing [Authorize] attributes will attempt to validate based on the cookie-based authentication. When we want to adjust to use the token-based authentication, we can simply use this syntax.
This is all that we need to do! Nice and simple to implement.
Getting the Current Username
Given that we are using claims as part of the token, we do need to use a different method to lookup the current username. Rather than checking the name of the identity, we need to look for the nameidentifier claim. You can do this using the following method.
Conclusion
This post ended up being longer than I had hoped, but it provides you with a 100% working solution to implement both Cookie and Jwt based authentication in your own project. If there is enough demand I can work to try and setup a sample project on GitHub, but for the time being I hope that this helps those of you looking for a method to support both API and website users inside of the same application.