The best way to Authorize user action in laravel

Best Way To Authorize User Action In Laravel

Hi guys,

We’ll look into the best Way To Authorize User Action In Laravel. Laravel. Laravel also offers a straightforward method of authorizing user activities against a specific resource. A user’s activities can be authorized in one of two ways: Policies and Gates

Gates provides a simple, closure-based approach to authorization, and Policies group their logic around a particular model. Gates applies to action that is not dependent on any model or resource. We should use policies when we wish to authorize an operation for a particular model.

Gates:-

Writing gates:

Gates are Closures that determine if a user may perform a given action and are defined inside the AuthServiceProvider class.

  /**
   * Register any authentication/authorization services.
   *
   * @return void
   *
   */

  public function boot()
  {
       $this->registerPolicies(); Gate::define('edit-settings', function ($user) {
          return $user->isAdmin;
       }); 
       Gate::define('update-post', function ($user, $post) {
           return $user->id === $post->user_id;
       });
  }

You can also define using a class@method in the middleware

 /**
  * Register any authentication/authorization services.
  *
  * @return void
  */

 public function boot()
 {
      $this->registerPolicies(); Gate::define('update-post',      'App\Policies\PostPolicy@update');
 }

Authorizing Actions:

If you want to authorize an action using gates, you can use the allows or denies methods. you should not pass the current user authenticated these methods.

  if (Gate::allows('edit-settings')) {
      // The current user can edit settings
  }

  if (Gate::allows('update-post', $post)) {
    // The current user can update the post…
  }

  if (Gate::denies('update-post', $post)) {
    // The current user can't update the post…
  }

We can use the forUser() method from the Gate facade to determine if a particular user is authorized to act.

  if (Gate::forUser($user)->allows('update-post', $post)) {
       // The user can update the post…
  }

  if (Gate::forUser($user)->denies('update-post', $post)) {
     // The user can't update the post…
  }

You may authorize multiple actions at a time with any() or none() methods.

  if (Gate::any(['update-post', 'delete-post'], $post)) {
     // The user can update or delete the post
  }

  if (Gate::none(['update-post', 'delete-post'], $post)) {
    // The user cannot update or delete the post
  }

Authorizing or Throwing Exceptions:

You would like to authorize an action. It will automatically throw an AuthorizationException if a user is not entitled to act.

  Gate::authorize('update-post', $post);

Supplying Additional context:

The gate facade gives a variety of methods (allows, denies, checks, any, none, authorize, can, cannot) to allow users. It also provides blade directives( @can, @cannot, @canany)

  Gate::define('create-post', function ($user, $category, $extraFlag) {
     return $category->group > 3 && $extraFlag === true;
  });

  if (Gate::check('create-post', [$category, $extraFlag])) {
      // The user can create the post…
  }

Gate Responses:-

Before we see gates return boolean values, you want to send more data or error messages as a response. You may do so by adding Response from your gate.

 use Illuminate\Auth\Access\Response;
 use Illuminate\Support\Facades\Gate;

 Gate::define('edit-settings', function ($user) {
      return $user->isAdmin
             ? Response::allow()
             : Response::deny('You must be a super administrator.');
  });

The inspect() method will get the entire authorization response returned by the gate. The allowed method returns a simple boolean value when returning an authorization response.

 $response = Gate::inspect('edit-settings', $post);

 if ($response->allowed()) {
      // The action is authorized.
 } else {
     echo $response->message();
 }

The authorize() method will throw an authorization exception if an action is not approved.

  Gate::authorize('edit-settings', $post);

Intercepting Gate Checks:

You may want to grant all abilities to a specific user. You can use the before method.

  Gate::before(function ($user, $ability) {
     if ($user->isSuperAdmin()) {
        return true;
     }
  });

The before callback returns a non-null result, the result will be considered the result of the check.

The after method is executed after all other authorization checks:

 Gate::after(function ($user, $ability, $result, $arguments) {
       if ($user->isSuperAdmin()) {
          return true;
       }
 });

Creating Policies:-

Generating policies:

Policies are organized authorization logic around a particular model or resource. You can generate a policy after executing the below command.

  php artisan make:policy PostPolicy

They place all Policies inside an app/Policies directory. It will create an empty policy class. You want to generate a basic CRUD policy in a class. You may add a –model when executing the command.

  php artisan make:policy PostPolicy --model=Post

Registering Policies:

After creating policies, we must register them inside AuthServiceProvider. It contains policy properties that map Eloquent models to their corresponding policies.

 <?php
 namespace App\Providers;

 use App\Policies\PostPolicy;
 use App\Post;
 use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
 use Illuminate\Support\Facades\Gate;

 class AuthServiceProvider extends ServiceProvider
 {
   /**
    * The policy mappings for the application.
    *
    * @var array
    */
   protected $policies = [
      Post::class => PostPolicy::class,
   ];

   /**
    * Register any application authentication/authorization services.
    *
    * @return void
    */
   public function boot()
   {
        $this->registerPolicies();

        //
   }
 }

Policy Auto-Discovery:

Laravel automatically detects policies and models. You must follow the naming conventions. All policies are placed in a Policies directory, and models may be placed in the app directory. The guessPolicyNamesUsing method provides your policy discovery logic. This method should call from the boot method in AuthServiceProvider.

 use Illuminate\Support\Facades\Gate;

 Gate::guessPolicyNamesUsing(function ($modelClass) {
    // return policy class name…
 });

Writing Policies:-

Policy Methods:

After registering the Policies, you may add a method for each action it authorizes. The update method will receive a user and post instance as its arguments, and it should return true or false.

 <?php

 namespace App\Policies;

 use App\Post;
 use App\User;

 class PostPolicy
 {
   /**
    * Determine if the user can update the post.
    *
    * @param \App\User $user
    * @param \App\Post $post
    * @return bool
    */
   public function update(User $user, Post $post)
   {
       return $user->id === $post->user_id;
   }
 }

Policy Responses:

You can also send data to the view side with Response closure on your policy method.

 use Illuminate\Auth\Access\Response;

 /**
  * Determine if the user can update the post.
  *
  * @param \App\User $user
  * @param \App\Post $post
  * @return \Illuminate\Auth\Access\Response
  */

 public function update(User $user, Post $post)
 {
        return $user->id === $post->user_id
             ? Response::allow()
             : Response::deny('You do not own this post.');
 }

The inspect() method to get the mature authorization response returned by the gate.

 $response = Gate::inspect('update', $post);

 if ($response->allowed()) {
    // The action is authorized
 } else {
    echo $response->message();
 }

Methods without models:

Some policy methods only receive the currently authenticated user and not an instance of a model. This situation is most common when authorizing creation action.

 /**
  * Determine if the given user can create posts.
  * 
  * @param \App\User $user
  * @return bool
  */

  public function create(User $user)
  {  
     //
  }

Guest Users:-

All gates and policies automatically return false. You can bypass the authorization checks using the “optional” method.

 <?php

 namespace App\Policies;

 use App\Post;
 use App\User; 

 class PostPolicy
 {
   /**
    * Determine if the user can update the post.
    *
    * @param \App\User $user
    * @param \App\Post $post
    * @return bool
    */
   public function update(?User $user, Post $post)
   {
       return optional($user)->id === $post->user_id;
   }
 }

Policy Filters:

Before the method is executed any other function executes on policy. It gives a benefit to authorize the action before the intended policy method is performed.

 public function before($user, $ability)
 {
    if ($user->isSuperAdmin()) {
      return true;
    }
 }

Authorizing actions using policies:-

via the User model:

Laravel gives you two methods for authorizing actions: can and can’t. The can method receives the action you wish to allow and the relevant model.

 if ($user->can('update', $post)) {
    //
 }

It registered a policy for a model. The can() method will automatically call the respective rule and return a boolean result. It listed no policy for the model. The can() method will attempt to request the closure-based gate matching the given action name.

Actions that don’t require models:

Create may not require a model instance. In this situation, you may pass a class name to the can method.

 use App\Post;

 if ($user->can('create', Post::class)) {
    // Executes the "create" method on the relevant policy…
 }

via Middleware:

A middleware can authorize actions before the incoming request even reaches your routes. The Authorize middleware is assigned the can key in the kernel class.

 use App\Post;

 Route::put('/post/{post}', function (Post $post) {
      // The current user may update the post…
 })->middleware('can:update,post');

We can pass two arguments to the can key. The first argument is the route name, and the second argument passes to the policy method. For create() method does not require a model instance. In this case, you may give the class name to it.

 Route::post('/post', function () {
    // The current user may create posts…
 })->middleware('can:create,App\Post');

via controller helpers:

Similar to the can() method, Laravel provides the authorize() method to any controllers. This method accepts the name of an action to be allowed and the respective model. If the operation is not allowed, it will throw an AuhtorizationException.

 <?php

 namespace App\Http\Controllers;

 use App\Http\Controllers\Controller;
 use App\Post; 
 use Illuminate\Http\Request;

 class PostController extends Controller
 {
    /**
     * Update the given blog post.
     *
     * @param Request $request
     * @param Post $post
     * @return Response
     * @throws \Illuminate\Auth\Access\AuthorizationException
     */
    public function update(Request $request, Post $post)
    {
         $this->authorize('update', $post);
         // The current user can update the blog post
    }
 }

For create() method does not require a model instance. Instead, you can pass class names like below:

 /**
  * Create a new blog post.
  * @param Request $request
  * @return Response
  * @throws \Illuminate\Auth\Access\AuthorizationException
  */

  public function create(Request $request)
  {
      $this->authorize('create', Post::class); // The current user can create blog posts
  }

Authorizing resource controllers
You are using a resource controller. You may use the authorizeResource method in the controllers` constructor. It accepts a model name as the first argument, the name of the route as the second argument.

 <?php

 namespace App\Http\Controllers;

 use App\Http\Controllers\Controller;
 use App\Post;
 use Illuminate\Http\Request;

 class PostController extends Controller
 {
    public function __construct()
    {
       $this->authorizeResource(Post::class, 'post');
    }
 }
Controller MethodPolicy Method
indexviewAny
showview
createcreate
storecreate
editupdate
updateupdate
destroydelete

via blade templates:

To display a portion of the page only if a user is authorized to perform a given action, you use the @can and @cannot family of directives.

 @can('update', $post)

 @elsecan('create', App\Post::class)

 @endcan

 @cannot('update', $post)

 @elsecannot('create', App\Post::class)

 @endcannot

You can also use the can() method to check the user’s authorization to perform this action like the below:

 @if (Auth::user()->can('update', $post))

 @endif

 @unless (Auth::user()->can('update', $post))

 @endunless

You may also determine if a user has any authorization ability from a given list of abilities. To accomplish this, use the the canany() directive:

 @canany(['update', 'view', 'delete'], $post)
   // The current user can update, view, or delete the post
 @elsecanany(['create'], \App\Post::class)
   // The current user can create a post
 @endcanany

you may pass a class name to the can and cannot directives if the action does not require a model instance:

 @can('create', App\Post::class)

 @endcan

 @cannot('create', App\Post::class)

 @endcannot

Supply Additional Context:-

You may pass an array as a second argument to authorize the action. The first element in the array determines it should invoke policy. Pass rest elements are a pass for authorization decisions.

 /**
  * Update the given blog post.
  *
  * @param Request $request
  * @param Post $post
  * @return Response
  * @throws \Illuminate\Auth\Access\AuthorizationException
  */

 public function update(Request $request, Post $post)
 {
       $this->authorize('update', [$post, $request->input('category')]); // The current user can update the blog post
 }

I hope this article (Best Way To Authorize User Action In Laravel) helps you better understand the authorized user action in laravel, so it helps you to check whether the user has permission to do a particular action. For this article, I followed this link. If you have questions, please leave a comment and I will respond as soon as possible.

Thank you for reading this article. Please share this article with your friend circle. That’s it for the day. Stay Connected!


Cheers,

Loading

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top