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.




