This is the post you have all been waiting for. I have been working on building my SaaS application for three months and most of this time has been spent going into what seems like uncharted territory. There are three main packages I was trying to get working together: Laravel, Tenancy, and Passport.
Edit — This will work with Laravel 5.7 and Hyn\Multi-tenant 5.3
This article is part of a Series. Check out the others below!
Part 0 — Laravel Multi-Tenant App Setup
Part 1 — Laravel Passport and Hyn\Tenancy←You are Here
Part 2 — VueJS and Laravel API
Part 3 — Laravel Multi-Tenant Testing
Seems easy right? Well… there aren’t many resources that show how to best utilize these packages together.
So for this demo I will be creating a small ticketing system. Once a user signs in they will see a list of tickets and will be able to CRUD operations on the tickets. The Front End will be Vuejs and it will talk to our Laravel API.
Buckle up and hang onto your hats because we are going to cover it all right here.
Also special thanks to Ashok Gelal for writing this series. It helped me tremendously early on trying to figure out the hyn\multi-tenant package. Check it out!
Full-featured multi-tenant Laravel app from scratch
Have you ever wondered where to get started with writing a robust multi-tenant Laravel app? Wondered how to organize…
Here are the docs for the other packages as well.
API Authentication (Passport) - Laravel - The PHP Framework For Web Artisans
Laravel - The PHP framework for web artisans.
The multi tenant saas toolkit for Laravel
Tenancy allows you to easily scaffold a multi-tenant SaaS platform on top of the Laravel framework.
Setting up the environment
We will be creating a REST API that will be consumed by a SPA. Web authentication will be used for signing into the SPA and it will pull data via authenticated API calls.
This code is also hosted on github. Make sure to use the Main branch.
Laravel demo with Passport and Tenancy. Contribute to sadnub/laravel-tenancy-passport-demo development by creating an…
Go ahead and install a tenancy compatible database (MySQL, MariaDB, or Postgre) and get PHP and a web server working. (Not covering that here).
Install Laravel and packages
The hyn/multi-tenant package won’t auto-discover unless the ‘system’ database driver is present. Just copy a connection that is there in
config/database.phpand rename it system. I used the mysql connection.
Publish necessary configuration files and scaffolding
Add this line to your
Then run the following commands to publish the necessary configuration files and migrations.
Setup environment variables
Go ahead and set the environment variables to match your setup. Make sure that
DB_CONNECTION is set to
system. Also note the URLs specified. The
APP_URL will be the URL for the landing page and the
TENANT_BASE_URL is the base URL for the tenant websites. For MySQL users, copy over the
Setting up Tenancy
config/tenancy.php. There are a few changes we need to make.
abort-without-identified-hostname option will allow us to setup a landing page. The landing page won’t be associated with a tenant and would give us a 404 error when we browse to www.itplog.com. The
update-app-url option will update email/notification URLs in Laravel internally so they are the tenant FQDN instead of
APP_URL in the .env file
Enforce Tenant Middleware
I grabbed this from Ashok’s article I linked above. This helps will making Third-Party packages tenant aware.
Then we need to add the middleware ‘tenancy.enforce’ definition to our
App\Http\Kernel.php file as shown below.
Create a folder called tenant in
database/migrations. Move all of the migrations files except for the ones that have ‘tenancy’ in the name.
All of the migrations in the tenant folder with be migrated to the tenant databases. System migrations will be in the migrations folder.
You can now run
php artisan migrate to migrate the system database.
Make sure the database has been created or you will get an error!
This trait needs to be added to all of the models that live in the tenant databases. See my user model below.
This trait needs to be added to all models that are supposed to be in the system database.
Setting up Passport… Tenant Style
Now we are on to setting up passport. This is were I spent most of my time when trying to get all of this to work. The problem will Passport is the models it uses aren’t resolved through the Laravel IoC. This makes it difficult because the implementations can’t be easily overridden without extending the classes or modifying the vendor files. This is were the EnforceTenancy middleware comes in.
Passport Routes and Token Settings
App\Providers\AuthServiceProvider.php and add the passport routes as shown below. Notice how we are able to specify the middleware to enforce a tenant connection. The rest of the settings allow us to call
passport:install from a controller and shorten the lifetime of the passport tokens.
As pointed out in the comments by Saji, the token expire lifetimes setup here don’t apply to Personal Access Tokens that Passport uses with the
createFreshApiToken middleware. The lifetime of the user session will actually be controller by the session lifetime configured in
App\User.php and add the
This will automatically add a cookie with the Personal Access Token once the user logs in. The user will be able to seamlessly access our Passport protected API!
Modify Kernel.php again…
Go ahead and add the
CreateFreshApiToken:class to the web middleware group as shown below.
api.driver setting to
passport as shown below
That is it for Passport! Now we just add the
auth:api middleware to any API route we want to protect.
In my app I needed to split up the authentication routes. This is because some were tenant specific and others weren’t. I also setup routing for the landing page using a domain route group with a catchall at the end. This way tenant URLs won’t work with /register and any other landing page specific routes.
Modifying the Auth Controllers
At this point you should be able to view the registration and login pages, but they won’t work. This is because they are not creating tenants upon registration. We can take care of that with a few modifications.
First lets create a class that handles tenant creation. Most of the functionality is from Ashok’s class, and I modified it to for my needs.
Now when we register a user we can call the create static method to create a save a new tenant. The
app(Environment::class)->tenant($website) line is very important because it actually changes the database connection to tenant. With that all database calls for this request will save to the tenant specific database.
Modifying the Register Controller
With the Register Controller we to override the register method and add the logic to validate a tenant URL and create the tenant. We will then create the user in the new tenant database and redirect to the tenant specific login page.
Adding the Ticket Model and Resource Controller
Now we can create the files required to power the Ticket model. Run the below commands to generate:
Move the ticket migration file to the
database/migrations/tenant folder then add the following lines to it.
Add API Route
Now add this line in your
api.php and make sure you are applying the
Controllers, Resources, Requests, Model, etc
I am using API Resources to return the data. I think it makes the controllers a little cleaner.
Make sure you add the guarded property to the Model. Otherwise you can add
$fillable and write out the attributes.
Modify Auth Views for Tenancy
We can now make a few modification to the default Auth views. I will be overriding these later, but we will at least be able to test everything up till this point.
Add the below field to the form. This will allow us to specify a FQDN during registration.
welcome.blade.php and app.blade.php
In these views we just need to comment out the login links. These won’t work from the www.itplog.com domain and will result in a 404 error.
Whoa! That’s a lot of code. We accomplished everything concerning the back-end of this application. The API should be functional at this point, but logging in doesn’t really do anything yet.
In the next article, I will write some tenant-aware tests, overwrite the default front-end, and create a small SPA using VueJS.
All of the code is available at the github repo I posted at the beginning of this article.