Implement Laravel Authorization using Gates from Scratch with Examples

What are Gates in Laravel?

The Laravel Authorization using Gates provides a simple way of writing authorization logic by passing a closure. The gates act like routes allowing users to validate based on their actions.

For example: If only a teacher can add exam marks to a student. The student can only view the marks given by his teacher and cannot make any modifications or updates to them.
For this particular case, we can define a gate and check whether a student can be allowed to update the marks based on his role.

And also there is another case where only that particular teacher who has added marks to students can only update student’s marks except the principal. For this, we first have to check the user who has added marks to the student and if that returns false then check if the user who is updating the student marks has a role of principal only then we have to allow them to update student marks.

Defining and Registering Gates

The gates are defined within the boot() method of class app\Providers\AuthServiceProvider. To define gate use Gate::define() method.

The Gate::define() the method takes two arguments:

  • $name: This is a unique name or key provided which will be called for authorization.
  • $callback_function: A closure function that takes the User instance as the first argument and the second argument as the model and returns a boolean value.

THere is also a post on How to use Multiple Users Authentication Guards and Login Throttling in Laravel where you’ll learn to work with Laravel Guards.

Create Model

In app\Models\StudentMarkModel.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class StudentMarkModel extends Model
{
    protected $table = 'student_marks';
    protected $primaryKey = 'student_mark_id';
    protected $fillable = ['student_id', 'user_id', 'marks' ];

}

Writing Gates

You can define Authorization Gates in app\Providers\AuthServiceProvider.php under boot() method.

use Illuminate\Support\Facades\Gate;

Gate::define( 'can_add_marks', function( User $user ){
    return $user->role == "teacher";
});

Gate::define( 'can_update_marks', function( User $user, StudentMarkModel $studentMark ){
    return $studentMark->user_id == $user->id;
});

Login User

In controller app\Http\Controllers\Test.php
create a method that takes the user id and logs him into the system. This is just for demonstration purposes.

public function login( Request $request ){
    Auth::loginUsingId( $request->id );
    $request->session()->regenerate();
}

Routes

Route::get( 'test/login/{id}',  [ Test::class, 'login' ] );

Route::get( 'test/student-marks-form',  [ Test::class, 'studentMarksForm' ] )->name( 'student-marks-form' );
Route::post( 'test/student-marks-form-save',  [ Test::class, 'studentMarksFormSave' ] )->name( 'student-marks-form-save' );

Controller

public function studentMarksForm( Request $request ){

    $obj = StudentMarkModel::find( $request->student_mark_id );

    $info = [
        'student_id' => ( $obj )? $obj->student_id : null,
        'user_id' => ( $obj )? $obj->user_id : null,
        'marks' => ( $obj )? $obj->marks : null,
    ];

    return view( 'authorization.student-marks-form', $info );
}

Render Form

In resource at resources\views\authorization\student-marks-form.blade.php

<form action="{{ route('student-marks-form-save') }}" method="post">
    @csrf

    <p>Student: </p>
    <input type="text" name="student_id" value="{{ $student_id }}" >

    <p>Marks: </p>
    <input type="text" name="marks" value="{{ $marks }}" >

    <br>
    <br>
    <input type="submit" value="Submit" >
</form>

Authorizing Gates

In controller app\Http\Controllers\Test.php class.

public function studentMarksFormSave( Request $request ){
    if( !Gate::allows('can_add_marks') ){
        abort( 403, "Only teachers can add marks" );
    }

    if( !Gate::allows('can_update_marks') ){
        abort( 403 );
    }

    $cond = [
        'student_id' => $request->student_id,
        'user_id' => $request->user()->id,
    ];

    $query = [
        'marks' => $request->marks
    ];

    StudentMarkModel::updateOrCreate( $cond, $query );

}

Other ways of authorizing

One line shorter way can be used for authorization.
In controller app\Http\Controllers\Test.php

$this->authorize('can_add_marks' );

//OR

Gate::authorize('can_add_marks' );

The above method on failure will automatically send a 403 response message with the message specified while defining the gate.

Other useful methods for authorizing gates

inspect( $name, $arguments )

The Gate::inspect( $name, $arguments ) will return Response object which consist message, code and allowed attributes.

before( $callback_function)

This method will give full access to a particular before running other gates.

after( $callback_function)

This gate will run after all the other gates.

forUser()

Used for authorizing users excluding a particular user. You can also call allows and denies on this object.

any( iterable $names, )

If any actions match from the array then will allow the user to perform that action and the opposite to this is none( iterable $names ).

Returning custom response message

There are many ways of displaying messages on the gate. The easiest is to pass a string as the second parameter to abort() function.

abort( $code, $message );

You can also send response message directly where gate logic is defined using Illuminate\Auth\Access\Response the class has two methods Response::allow() called when authorized and Response::deny() when unauthorized.

Gate::define( 'can_add_marks', function( User $user ){
    return ( $user->role == "teacher" )
                ? Response::allow()
                : Response::deny("Only Teachers are allowed to add marks");
});

Authorizing in Blade Template

For blade template directives such as @can, @cannot, @unless and @canany etc are available. In addition, you can also use the conditional statement to perform the authorization logic.

@can('can_add_marks', $studentMarkModelObject )
    <!-- then let user add marks -->
@endcan


@cannot('can_add_marks', $studentMarkModelObject )
    <!-- cannot add the marks -->
@endcannot


@if ( Auth::user()->can( 'can_add_marks', $studentMarkModelObject ) )
    <!-- then let user add marks -->
@endif

<!-- using auth() helper function to acheive the above results -->
@if ( auth()->user()->can( 'can_add_marks', $studentMarkModelObject ) )
    <!-- then let user add marks -->
@endif

Conclusion

Laravel Authorization using Gates Provide a fluent and clean interface to allow and make better use of permissions in your Laravel Project. You can also go beyond and look for the packages which provide the specific functionality related to permission and one of the packages is spatie laravel permission which deals with the advanced functionality of permissions such as associating the user with roles and dynamic permission system all this is possible via this package.

Summary
Review Date
Reviewed Item
Implement Laravel Authorization using Gates from Scratch with Examples
Author Rating
5
1star
1star1star1star1star
Software Name
Laravel Framework
Software Name
Windows Os, Mac Os, Ubuntu Os
Software Category
Web Development