Introduction
In addition to providing authentication services out of the box, Laravel also provides a simple way to organize authorization logic and control access to resources. There are a variety of methods and helpers to assist you in organizing your authorization logic, and we'll cover each of them in this document.
Note: Authorization was added in Laravel 5.1.11, please refer to the upgrade guide before integrating these features into your application.
Defining Abilities
The simplest way to determine if a user may perform a given action is
to define an "ability" using the
Illuminate\Auth\Access\Gate
class. The
AuthServiceProvider
which ships with Laravel serves as a
convenient location to define all of the abilities for your application.
For example, let's define an update-post
ability which
receives the current User
and a Post
model. Within our ability, we will determine if
the user's id
matches the post's user_id
:
<?php
namespace App\Providers;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* Register any application authentication / authorization services.
*
* @param \Illuminate\Contracts\Auth\Access\Gate $gate
* @return void
*/
public function boot(GateContract $gate)
{
$this->registerPolicies($gate);
$gate->define('update-post', function ($user, $post) {
return $user->id === $post->user_id;
});
}
}
Note that we did not check if the given $user
is not
NULL
. The Gate
will automatically return
false
for all abilities when there is not
an authenticated user or a specific user has not been specified using
the forUser
method.
Class Based Abilities
In addition to registering Closures
as authorization
callbacks, you may register class methods by passing a string containing
the class name and the method. When needed, the class will be resolved
via the service container:
$gate->define('update-post', 'Class@method');
Intercepting Authorization Checks
Sometimes, you may wish to grant all abilities to a specific user.
For this situation, use the before
method to define a
callback that is run before all other authorization checks:
$gate->before(function ($user, $ability) {
if ($user->isSuperAdmin()) {
return true;
}
});
If the before
callback returns a non-null result that
result will be considered the result of the check.
You may use the after
method to define a callback to be
executed after every authorization check. However, you may not modify
the result of the authorization check from an after
callback:
$gate->after(function ($user, $ability, $result, $arguments) {
//
});
Checking Abilities
Via The Gate Facade
Once an ability has been defined, we may "check" it in a variety of
ways. First, we may use the check
, allows
, or
denies
methods on the Gate
facade. All of these methods receive the name of
the ability and the arguments that should be passed to the ability's
callback. You do not need to pass the current user to
these methods, since the Gate
will automatically prepend
the current user to the arguments passed to the callback. So, when
checking the update-post
ability we defined earlier, we
only need to pass a Post
instance to the
denies
method:
<?php
namespace App\Http\Controllers;
use Gate;
use App\User;
use App\Post;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
/**
* Update the given post.
*
* @param int $id
* @return Response
*/
public function update($id)
{
$post = Post::findOrFail($id);
if (Gate::denies('update-post', $post)) {
abort(403);
}
// Update Post...
}
}
Of course, the allows
method is simply the inverse of
the denies
method, and returns true
if the
action is authorized. The check
method is an alias of the
allows
method.
Checking Abilities For Specific Users
If you would like to use the Gate
facade to check if a
user other than the currently authenticated user has a
given ability, you may use the forUser
method:
if (Gate::forUser($user)->allows('update-post', $post)) {
//
}
Passing Multiple Arguments
Of course, ability callbacks may receive multiple arguments:
Gate::define('delete-comment', function ($user, $post, $comment) {
//
});
If your ability needs multiple arguments, simply pass an array of
arguments to the Gate
methods:
if (Gate::allows('delete-comment', [$post, $comment])) {
//
}
Via The User Model
Alternatively, you may check abilities via the User
model instance. By default, Laravel's App\User
model uses
an Authorizable
trait which provides two methods:
can
and cannot
. These methods may be used
similarly to the allows
and denies
methods
present on the Gate
facade. So, using our previous example,
we may modify our code like so:
<?php
namespace App\Http\Controllers;
use App\Post;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
/**
* Update the given post.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return Response
*/
public function update(Request $request, $id)
{
$post = Post::findOrFail($id);
if ($request->user()->cannot('update-post', $post)) {
abort(403);
}
// Update Post...
}
}
Of course, the can
method is simply the inverse of the
cannot
method:
if ($request->user()->can('update-post', $post)) {
// Update Post...
}
Within Blade Templates
For convenience, Laravel provides the @can
Blade
directive to quickly check if the currently authenticated user has a
given ability. For example:
<a href="/post/{{ $post->id }}">View Post</a>
@can('update-post', $post)
<a href="/post/{{ $post->id }}/edit">Edit Post</a>
@endcan
You may also combine the @can
directive with
@else
directive:
@can('update-post', $post)
<!-- The Current User Can Update The Post -->
@else
<!-- The Current User Can't Update The Post -->
@endcan
Within Form Requests
You may also choose to utilize your Gate
defined
abilities from a form
request's authorize
method. For example:
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
$postId = $this->route('post');
return Gate::allows('update', Post::findOrFail($postId));
}
Policies
Creating Policies
Since defining all of your authorization logic in the
AuthServiceProvider
could become cumbersome in large
applications, Laravel allows you to split your authorization logic into
"Policy" classes. Policies are plain PHP classes that group
authorization logic based on the resource they authorize.
First, let's generate a policy to manage authorization for our
Post
model. You may generate a policy using the
make:policy
artisan command. The
generated policy will be placed in the app/Policies
directory:
php artisan make:policy PostPolicy
Registering Policies
Once the policy exists, we need to register it with the
Gate
class. The AuthServiceProvider
contains a
policies
property which maps various entities to the
policies that manage them. So, we will specify that the
Post
model's policy is the PostPolicy
class:
<?php
namespace App\Providers;
use App\Post;
use App\Policies\PostPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
Post::class => PostPolicy::class,
];
/**
* Register any application authentication / authorization services.
*
* @param \Illuminate\Contracts\Auth\Access\Gate $gate
* @return void
*/
public function boot(GateContract $gate)
{
$this->registerPolicies($gate);
}
}
Writing Policies
Once the policy has been generated and registered, we can add methods
for each ability it authorizes. For example, let's define an
update
method on our PostPolicy
, which will
determine if the given User
can "update" a
Post
:
<?php
namespace App\Policies;
use App\User;
use App\Post;
class PostPolicy
{
/**
* Determine if the given post can be updated by the user.
*
* @param \App\User $user
* @param \App\Post $post
* @return bool
*/
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
}
You may continue to define additional methods on the policy as needed
for the various abilities it authorizes. For example, you might define
show
, destroy
, or addComment
methods to authorize various Post
actions.
Note: All policies are resolved via the Laravel service container, meaning you may type-hint any needed dependencies in the policy's constructor and they will be automatically injected.
Intercepting All Checks
Sometimes, you may wish to grant all abilities to a specific user on
a policy. For this situation, define a before
method on the
policy. This method will be run before all other authorization checks on
the policy:
public function before($user, $ability)
{
if ($user->isSuperAdmin()) {
return true;
}
}
If the before
method returns a non-null result that
result will be considered the result of the check.
Checking Policies
Policy methods are called in exactly the same way as
Closure
based authorization callbacks. You may use the
Gate
facade, the User
model, the
@can
Blade directive, or the policy
helper.
Via The Gate Facade
The Gate
will automatically determine which policy to
use by examining the class of the arguments passed to its methods. So,
if we pass a Post
instance to the denies
method, the Gate
will utilize the corresponding
PostPolicy
to authorize actions:
<?php
namespace App\Http\Controllers;
use Gate;
use App\User;
use App\Post;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
/**
* Update the given post.
*
* @param int $id
* @return Response
*/
public function update($id)
{
$post = Post::findOrFail($id);
if (Gate::denies('update', $post)) {
abort(403);
}
// Update Post...
}
}
Via The User Model
The User
model's can
and
cannot
methods will also automatically utilize policies
when they are available for the given arguments. These methods provide a
convenient way to authorize actions for any User
instance
retrieved by your application:
if ($user->can('update', $post)) {
//
}
if ($user->cannot('update', $post)) {
//
}
Within Blade Templates
Likewise, the @can
Blade directive will utilize policies
when they are available for the given arguments:
@can('update', $post)
<!-- The Current User Can Update The Post -->
@endcan
Via The Policy Helper
The global policy
helper function may be used to
retrieve the Policy
class for a given class instance. For
example, we may pass a Post
instance to the
policy
helper to get an instance of our corresponding
PostPolicy
class:
if (policy($post)->update($user, $post)) {
//
}
Controller Authorization
By default, the base App\Http\Controllers\Controller
class included with Laravel uses the AuthorizesRequests
trait. This trait provides the authorize
method, which may
be used to quickly authorize a given action and throw a
HttpException
if the action is not authorized.
The authorize
method shares the same signature as the
various other authorization methods such as Gate::allows
and $user->can()
. So, let's use the
authorize
method to quickly authorize a request to update a
Post
:
<?php
namespace App\Http\Controllers;
use App\Post;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
/**
* Update the given post.
*
* @param int $id
* @return Response
*/
public function update($id)
{
$post = Post::findOrFail($id);
$this->authorize('update', $post);
// Update Post...
}
}
If the action is authorized, the controller will continue executing
normally; however, if the authorize
method determines that
the action is not authorized, a HttpException
will
automatically be thrown which generates a HTTP response with a
403 Not Authorized
status code. As you can see, the
authorize
method is a convenient, fast way to authorize an
action or throw an exception with a single line of code.
The AuthorizesRequests
trait also provides the
authorizeForUser
method to authorize an action on a user
that is not the currently authenticated user:
$this->authorizeForUser($user, 'update', $post);
Automatically Determining Policy Methods
Frequently, a policy's methods will correspond to the methods on a
controller. For example, in the update
method above, the
controller method and the policy method share the same name:
update
.
For this reason, Laravel allows you to simply pass the instance
arguments to the authorize
method, and the ability being
authorized will automatically be determined based on the name of the
calling function. In this example, since authorize
is
called from the controller's update
method, the
update
method will also be called on the
PostPolicy
:
/**
* Update the given post.
*
* @param int $id
* @return Response
*/
public function update($id)
{
$post = Post::findOrFail($id);
$this->authorize($post);
// Update Post...
}