One of the most common features in any web application is authentication, which allows a user to log in or log out of an application to securely manage their information. However, many a time an application might also want to restrict access to certain aspects of the system to a certain type of users. This second level of screening is known as Access Control. I recently had to implement a very advanced and granular level of access control in a Laravel API based Application. So let me show you how you can implement access control using permissions and roles in a Vue SPA with a Laravel 7 back-end.

This tutorial is the first of the two-part series that will cover this entire topic in detail.

Vue SPA – Laravel 7 Access Control Overview

We will build a Laravel 7 API based application with a standalone Vue SPA front-end. The app has three types of roles, namely, Super Admin, User Manager, and Role Manager. These roles, in turn, grant the User a set of permissions. To clarify, each permission is mapped to one action. For example, View Users, Edit users, etc. Therefore, based upon the User’s permission, the front-end dashboard is dynamically populated. Likewise, the same check is present on the back-end as well so that the user only has access to the APIs that they have permissions to access.

Laravel 7 API - Vue SPA Role Based Access Control Flow

Laravel API Application Setup

If you have already installed and set up your Laravel application, including a database, and also set up authentication, then you can skip to the “Composer Package Setup” section.

Create a new Laravel Application

We will start by updating our composer and pulling in the Laravel installer. After that, we will create a new Laravel 7 Application.

composer update
composer global require laravel/installer
laravel new laravel-api-role-permissions
cd laravel new laravel-api-role-permissions

Generate new application key using Artisan

The application key is used to secure User sessions and cookies in Laravel. This command generates a key and sets it as APP_KEY in your .env file. Subsequently, if you want to learn more about what this key is used for, check this helpful article.

php artisan key:generate

Set up a database for your application

Here, I have created a MySQL database named laravel_api and set the connection details in my .env file:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_api
DB_USERNAME=root
DB_PASSWORD=

Make sure to set your database credentials accordingly.

Install Laravel’s UI package and generate authentication scaffolding

We will need this package in order to start using Laravel’s default authentication module. So let’s pull that into our application.

composer require laravel/ui

After that, let’s generate the authentication views and scaffolding.

php artisan ui vue --auth
npm install && npm run dev

Composer Package Setup

We will make use of an excellent package called laravel-permissions to implement a granular, permission-based access control in our application.

Install laravel-permissions package using Composer

This command will pull in the latest version of this package from Packagist:

composer require spatie/laravel-permission

Register the package’s service provider

This will make sure the package’s functionality is bootstrapped along with our Laravel application. Additionally, you can learn more about service providers in Laravel documentation.

'providers' => [
    // ...
    Spatie\Permission\PermissionServiceProvider::class,
];

Publish the package’s migrations and configuration file

This package comes with migrations that create tables such as roles and permissions. In addition, a permission.php file is also pushed to the config directory.

php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"

Run your migrations

Finally, we complete the setup by running our migrations. After this, our authentication and roles/permission related tables will be created in the database.

php artisan migrate

Prepare User Model for Access Control

We will start by adding Spatie\Permission\Traits\HasRoles trait to our User model in User.php.

...

class User extends Authenticatable
{
    use Notifiable, HasRoles;
    ..

After that, your User model should now be associated with the laravel-permissions package. To clarify, this establishes relationships such as Roles and Permissions to the User model.

Create Permissions and Roles

Now is the time to decide what kind of permissions and roles our application will have. Further, to show you guys a good example, I will set up the following set of permissions and roles.

Permissions

  1. View All Users: A permission that enables access to /api/users API. The authenticated user will be able to view all users registered in the system.
  2. Edit All Users: A permission that enables access to /api/user/{id}/edit API. Subsequently, the user will be able to edit all users registered in the system.
  3. Assign Role: A permission that will enable access to assign one or more roles to a User in the system.
  4. Unassign Role: A permission that will enable access to remove one or more roles assigned to a User in the system.
  5. View All Permissions: A permission that enables access to the /api/permissions API.
  6. View All Roles: A permission that enables access to the /api/roles API.

Roles

  1. Super Admin: This role will have all the permissions listed above.
  2. User Manager: This role will have View All Users and Edit All Users permissions.
  3. Role Manager: This role will responsible for assigning and unassigning roles to a user.

Create artisan command

We will create an artisan command which will generate the permissions and roles listed above. This command should be run just once during the initial setup of your application. After that, we will also assign permissions to these commands.

php artisan make:command RolePermissionBootstrap

Add the following logic to app/Console/Commands/RolePermissionBootstrap.php that creates permissions and roles mentioned above. Subsequently, we assign proper permissions to each role.

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;

class RolePermissionBootstrap extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'laravel_api:bootstrap';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Create roles and permissions';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
 public function handle()
    {

        $roles = ["Super Admin", "User Manager","Role Manager"];

        $permissions = [
            "View All Users", 
            "Edit All Users", 
            "Assign Role", 
            "Unassign Role", 
            "View All Permissions",
            "View All Roles"];


        $this->line('------------- Setting Up Roles:');

        foreach ($roles as $role) {
            $role = Role::updateOrCreate(['name' => $role, 'guard_name' => 'api']);
            $this->info("Created " . $role->name . " Role");
        }

        $this->line('------------- Setting Up Permissions:');

        $superAdminRole = Role::where('name', "Super Admin")->first();

        foreach ($permissions as $perm_name) {
            $permission = Permission::updateOrCreate(['name' => $perm_name,
                'guard_name' => 'api']);

            $superAdminRole->givePermissionTo($permission);

            $this->info("Created " . $permission->name . " Permission");
        }

        $this->info("All permissions are granted to Super Admin");
        $this->line('------------- Application Bootstrapping is Complete: \n');
    }
}

Run the Artisan command

Run laravel_api:bootstrap to generate roles and permissions and persist them into the database.

php artisan laravel_api:bootstrap

The output looks something like this:

role_permission_artisan_command

Define APIs for Access Control

Now that we have our roles and permissions in place, let’s define our base APIs in routes/api.php to assign or remove any role from a user. However, for the sake of brevity, I will be implementing only the assign/unassigning of a role here as the view/edit user logic will be specific to your application.

Note: If you still want to see my User view/edit logic, then I will be attaching a link to one of my projects at the end of this tutorial series. However, that project demonstrates access control at a more complex level than the scenario covered here. Therefore, you should read through this entire tutorial before you can make sense of that project.

<?php

Route::post('register', 'AuthController@register')
    ->name('register');

Route::post('login', 'AuthController@login')
    ->name('login');

Route::group([
    'middleware' => 'auth:api'
], function () {

    Route::get('logout', 'AuthController@logout')->name('logout');

    Route::get('/permissions', 'RoleManager@permissionsIndex')
        ->name('permissions.index')
        ->middleware('permission:View All Permissions');

    Route::get('/roles', 'RoleManager@rolesIndex')
        ->name('roles.index')
        ->middleware('permission:View All Roles');

    Route::post('/roles/{role}/assign/{user}', 'RoleManager@rolesAddUser')
        ->name('roles.addUser')
        ->middleware('permission:Assign Role');

    Route::post('/roles/{role}/unassign/{user}', 'RoleManager@rolesRemoveUser')
        ->name('roles.removeUser')
        ->middleware('permission:Unassign Role');
});

As you can see above, I’m using the permission: middleware to control who can access those APIs. So only those with appropriate permissions will be able to invoke the API without getting a 405, Not Allowed HTTP response.

Create RoleManager Controller

php artisan make:controller RoleManager --api

Now let’s define the logic for assigning a role to a user and un-assigning it. To clarify, we mainly use the assignRole() and removeRole() methods provided by the laravel-permissions package.

class RoleManager extends Controller
{

    public function permissionsIndex()
    {
        return Permission::all()->paginate(100);
    }


    public function rolesIndex()
    {
        return Role::all()->paginate(100);
    }



    public function rolesAddUser(Request $request, Role $role, User $user)
    {

        $user->assignRole($role);

        return response()->json([
            "message" => $role->name . " Role successfully assigned to User!"
        ], 200);
    }


    public function rolesRemoveUser(Request $request, Role $role, User $user)
    {
        $user->removeRole($role);

        return response()->json([
            "message" => $role->name . " Role successfully removed from User"
        ], 200);
    }
}

That’s it! Our Laravel API is ready. And we have come halfway in implementing access control using permissions and roles in a Vue SPA with a Laravel 7 back-end.

In the next part of this tutorial, we will see how to integrate these APIs into a standalone Vue Single Page Application.