Capture Tenant in Asp.Net Core 2.0 Web Api

Let’s now capture the Tenant in our code.  Here I will be using the domain name of the web site the app runs on to determine who the Tenant is.  Normally you would capture domains names like “customerA.apothecaric.com” and “customerB.apothecaric.com” and you then determine which Tenant the user belongs to.

Set Up Data For Development

In our immediate case we are running the Api locally so I will go insert a Tenant for our use.  I will also add the new Tenant’s Id to the user that we created earlier using the temporary “Register” Api endpoint.

Now we have a Tenant in our database and a user that is tied to that Tenant.

On To The Code

Now it’s time to write some .Net Core middleware.  I have added a folder called “Middleware” and a class in that new folder called “TenantProviderMiddleware”.

Set Up A Middleware Class

In the TenantProviderMiddleware class, add the following boilerplate code for a middleware component.

Next we need to add the Invoke method:

Here notice that I have also added our DbContext to be injected in so that we can check the database for a proper Tenant.

Inside the Invoke method, let’s first add

to grab the Host (Domain Name) from the Http Request.

Next, let’s check that we did, in fact, get a domain name from the Http Request.

If we do get a domain name, I want to make sure to strip out any port numbers if they are present

Now that we have a good domain name (host), let’s hit the database with our new information and see if we have a registered Tenant with that same domain name.

Here is a check on the Tenant object.  If we don’t have one, I want to throw an error.  This request should not be made to our system without a Tenant…and that’s final!!

The last bit is to add to the HttpContext’s Items key/value pairs.  I have added the key “TENANT” with the value of the Tenant object that we grabbed from the database.

Last, we end the middleware and pass control to back to the app or the next registered middleware.

Here is the whole class listing:

Register The Custom Middleware

We now must register our middleware with the application in order to run the code on http requests.  So back again to the Configure method of the Startup class.

We need to add the line:

Update Custom User

We then need to revisit our ApothecaricUser class and add a couple of items.  We need to add the TenantId column that the Migration so graciously created for us in the database.  We also need to add the Tenant object that the user will belong to.

But How Will We Use It?

When a request comes into our controllers, I would like to grab the Tenant.  On the CreateToken method that we created in the last post, remember that we make 2 calls to the Asp.Net Identity database to authenticate the user.

and

I want to change the first one.  I would like to check that the user exits by the user’s name AND by the Tenant the the user should belong to.  How about a FindByNameAndTenantAsyc call.  But wait, that doesn’t exist in Asp.Net Core’s UserManager.  How would we do this?

Create Our Own UserManager Of Course!

In the Data folder, I created a new ApothecaricUserManager class.

When creating our new Custom UserManager, we need to inherit from Core’s UserManager and also create the proper constructor for our new class.

One helluva constructor, no?!

Now we just need to add one more method here to get at a User and their Tenant.  Here is that method:

In keeping with the constructs, I will keep this method asynchronous.  The NormalizedUserName is just the user’s email that is all uppercase.  Makes for easier comparisons.  And here, we also check for the Tenant by using the Tenant’s Id that we captured in the middleware.

Register, Register

We then need to go back (again!) to our Startup class and register our new ApothecaricUserManger with the Identity middleware.  Find our AddIentity call in the ConfigureServices method and add on the AddUserManger registration.

Use It In The Controller

In our AccountController, change the:

to

And the constructor to signature to:

and last, the call to:

should now be:

But Wait A Minute!!!

Where are we going to get that Tenant information from?  I know the middleware grabbed it and put it in the HttpContext’s Items key/value parings….but how do access that?

The last step here is to create a base Controller class and have our Controller’s inherit from a new MultiTenantController instead of the built-in Controller class.  Create a new controller named “MultiTenantController” and add that to the Controllers folder.

Make sure the controller inherits from the base Controller class.

This new controller will just have a Property in it.  That’s it. The property is…..

You guessed it!  Tenant!

Here we grab that Tenant from the HttpContext’s Items list that our middleware placed there for us.  How nice!

Now go back in inherit the AccountController from MultiTenantController instead of just Controller.

Now magically change:

to this:

Try It Out!

Open Postman again and try the Token call.  This time you are both making sure the user is in your system but also that the user is coming from the correct Tenant they are assigned to.  This way you can use the same app for all your customers and, the same database if you wish and keep the data separated for each Tenant using your application.

Next Steps

Next time I would like to add some Validation Filtering to your endpoints to make sure the data coming into our endpoints is exactly what we are expecting.

Hope to see you then!

3 Comments

  1. Eric, this is a great series! You’re hitting a lot of the things I needed to learn to do this. First thing I noticed was your background image, I’m a Milwaukee guy, keep it up!

    Also, your design was great in that it used domains and modified identity, but it was limited to one tenant per user. Not sure if you’ve seen these but Carl Rippon addresses the multi-tenancy from a slightly different angle, adding a “kid” to the JWT, and how you can get the kid back to figure out the tenant and put it through to SQL Server via the connection string so you don’t have to deal with the “where tenantid = @sometenant” on thousands of queries.

    https://www.carlrippon.com/asp-net-core-web-api-multi-tenant-jwts/
    https://www.carlrippon.com/creating-a-multi-tenant-asp-net-core-web-api-with-sql-server-rls/

    Take care, keep it up!
    Scott

  2. @Scott, thanks for sharing! While I was learning and researching this subject, I did not come across Carl’s blog. That is an angle that I have not even thought about. We can use that even if you use url forwarding! I love it.

Leave a Reply

Your email address will not be published. Required fields are marked *