> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Autorización (gates y policies)

> Aprende a gestionar los permisos de las acciones de los usuarios con los gates y las policies de Laravel.

## Diferencia entre autenticación y autorización

La **autenticación** confirma «quién es» un usuario. El inicio de sesión es su ejemplo más habitual.
La **autorización** determina «qué puede hacer» ese usuario. Por ejemplo, permitir que solo edite sus propios posts o que únicamente los administradores modifiquen los ajustes.

Una vez que hayas implementado el inicio de sesión mediante [autenticación](./authentication), el siguiente paso es la autorización.

<Info>
  Las funciones de autorización de Laravel ofrecen dos enfoques: gates y policies. Cuál usar depende de tu caso de uso.
</Info>

**Criterios para decidir**

| Escenario                                                            | Recomendado |
| -------------------------------------------------------------------- | ----------- |
| Comprobaciones sencillas que no están asociadas a un modelo concreto | Gates       |
| Gestión de permisos CRUD sobre un modelo                             | Policies    |
| Control de acceso al panel de administración                         | Gates       |
| Permisos de creación, edición y borrado de posts de un blog          | Policies    |

## Gates

Los gates son comprobaciones sencillas basadas en closures. Son idóneos para permisos que no están ligados a un modelo concreto.

### Flujo de autorización con gates

```mermaid theme={null}
flowchart TD
    A["AppServiceProvider::boot()<br>(App\\Providers\\AppServiceProvider)"] --> B["Gate::define('ability', fn)<br>Registrar la lógica de autorización"]
    C["Comprobación desde el controlador"] --> D{"¿Se ha definido<br>Gate::before()?"}
    D -->|"Sí"| E{"Valor devuelto por before()"}
    E -->|"true / false"| F["Se usa el resultado de before() como decisión final"]
    E -->|"null"| G["Se ejecuta la closure de define()"]
    D -->|"No"| G
    G --> H{"Valor devuelto por la closure"}
    H -->|"true"| I["✓ Acceso permitido"]
    H -->|"false"| J["✗ Acceso denegado"]
    F -->|"true"| I
    F -->|"false"| J
    J -->|"Gate::allows()"| K["Devuelve false<br>→ hay que llamar a abort(403) manualmente"]
    J -->|"Gate::authorize()"| L["AuthorizationException<br>→ 403 Forbidden automático"]
```

### Definir un gate

Los gates se definen en el método `boot` de `App\Providers\AppServiceProvider` con `Gate::define()`.

```php theme={null}
// app/Providers/AppServiceProvider.php

use App\Models\Post;
use App\Models\User;
use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    Gate::define('update-post', function (User $user, Post $post) {
        return $user->id === $post->user_id;
    });
}
```

La closure de un gate recibe siempre como primer argumento el usuario autenticado actualmente. A partir del segundo argumento puedes pasar el modelo objetivo u otros datos.

### Comprobar permisos con gates

Para comprobar permisos desde un controlador utiliza `Gate::allows()` o `Gate::denies()`.

```php theme={null}
use Illuminate\Support\Facades\Gate;

public function update(Request $request, Post $post): RedirectResponse
{
    if (! Gate::allows('update-post', $post)) {
        abort(403);
    }

    // Actualizar el post...

    return redirect('/posts');
}
```

### Lanzar una excepción con Gate::authorize()

Para devolver automáticamente una respuesta 403 cuando el usuario no tiene permiso, utiliza `Gate::authorize()`. Así te ahorras escribir `abort(403)`.

```php theme={null}
public function update(Request $request, Post $post): RedirectResponse
{
    Gate::authorize('update-post', $post);

    // Si hay permiso llegamos aquí
    // Actualizar el post...

    return redirect('/posts');
}
```

<Tip>
  `Gate::authorize()` lanza `Illuminate\Auth\Access\AuthorizationException` cuando el usuario no tiene permiso. Laravel la convierte automáticamente en una respuesta HTTP 403.
</Tip>

### Bypass para administradores (método `before`)

Si quieres conceder todos los permisos a los usuarios administradores, utiliza `Gate::before()`.

```php theme={null}
use App\Models\User;
use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    // $ability contiene el nombre del gate que se intenta ejecutar ('update-post', etc.)
    Gate::before(function (User $user, string $ability) {
        if ($user->isAdministrator()) {
            return true;
        }
    });

    Gate::define('update-post', function (User $user, Post $post) {
        return $user->id === $post->user_id;
    });
}
```

Si la closure de `before` devuelve un valor distinto de `null`, ese valor pasa a ser la decisión definitiva. Si devuelve `null` o no devuelve nada, se continúa con la comprobación habitual del gate.

### @can y @cannot en las plantillas Blade

Las directivas `@can` y `@cannot` son muy útiles para comprobar gates en las plantillas.

```blade theme={null}
@can('update-post', $post)
    <a href="{{ route('posts.edit', $post) }}">Editar</a>
@endcan

@cannot('update-post', $post)
    <p>No tienes permiso para editar este post.</p>
@endcannot
```

## Policies

Las policies agrupan la lógica de autorización relativa a un modelo concreto en una clase. Para gestionar los permisos de creación, lectura, edición y borrado sobre el modelo `Post`, las policies son más adecuadas que los gates.

### Flujo de autorización con policies

```mermaid theme={null}
flowchart TD
    A["Petición HTTP"] --> B["Controlador"]
    B --> C["Gate::authorize('update', $post)"]
    C --> D{"¿Está definido<br>PostPolicy::before()?"}
    D -->|"Sí"| E{"Valor devuelto por before()"}
    E -->|"true / false"| F["Se usa el resultado de before() como decisión final"]
    E -->|"null"| G["Se ejecuta PostPolicy::update(User, Post)"]
    D -->|"No"| G
    G --> H{"¿$user->id === $post->user_id?"}
    H -->|"true"| I["✓ Continúa el proceso"]
    H -->|"false"| J["✗ AuthorizationException<br>→ 403 Forbidden"]
    F -->|"true"| I
    F -->|"false"| J
```

### Generar una policy

Genera una clase de policy con el comando Artisan `make:policy`.

```shell theme={null}
php artisan make:policy PostPolicy
```

Para generar una plantilla con todos los métodos CRUD asociados a un modelo, utiliza la opción `--model`.

```shell theme={null}
php artisan make:policy PostPolicy --model=Post
```

Se creará `app/Policies/PostPolicy.php`.

### Detección automática de modelos y policies

Laravel detecta las policies automáticamente siguiendo la convención de nombres.

* Modelo: `app/Models/Post.php`
* Policy: `app/Policies/PostPolicy.php`

Si respetas esta convención no necesitas registrar manualmente la policy.

<Info>
  Si no sigues la convención de nombres o prefieres registrarla manualmente, utiliza `Gate::policy()` en el método `boot` de `AppServiceProvider`.

  ```php theme={null}
  use App\Models\Post;
  use App\Policies\PostPolicy;
  use Illuminate\Support\Facades\Gate;

  Gate::policy(Post::class, PostPolicy::class);
  ```

  En Laravel 13 también puedes registrarla de forma declarativa añadiendo el atributo `#[UsePolicy]` al modelo.

  ```php theme={null}
  use App\Policies\PostPolicy;
  use Illuminate\Database\Eloquent\Attributes\UsePolicy;

  #[UsePolicy(PostPolicy::class)]
  class Post extends Model {}
  ```
</Info>

### Implementar los métodos de la policy

Las policies generadas con la opción `--model` incluyen métodos para las acciones CRUD estándar.

```php theme={null}
<?php

namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    /**
     * ¿Puede ver el listado de posts?
     */
    public function viewAny(User $user): bool
    {
        return true; // Cualquier usuario autenticado puede verlo
    }

    /**
     * ¿Puede ver un post concreto?
     */
    public function view(User $user, Post $post): bool
    {
        return true; // Cualquier usuario autenticado puede verlo
    }

    /**
     * ¿Puede crear posts?
     */
    public function create(User $user): bool
    {
        return true; // Cualquier usuario autenticado puede publicar
    }

    /**
     * ¿Puede actualizar el post?
     */
    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id; // Solo puede editar los suyos
    }

    /**
     * ¿Puede eliminar el post?
     */
    public function delete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id; // Solo puede eliminar los suyos
    }
}
```

### Bypass para administradores (método `before`)

En las policies también puedes definir un método `before` para conceder todos los permisos a los administradores.

```php theme={null}
/**
 * Comprobación previa que se ejecuta antes que los demás métodos de la policy
 * $ability contiene el nombre del método ('update', 'delete', etc.)
 */
public function before(User $user, string $ability): bool|null
{
    if ($user->isAdministrator()) {
        return true; // El administrador tiene permiso para todo
    }

    return null; // Devolviendo null se pasa al método habitual
}
```

<Warning>
  El método `before` solo se ejecuta si existe el método correspondiente en la clase de la policy. Por ejemplo, si no está definido `update`, tampoco se invocará `before`.
</Warning>

### Orden de ejecución del callback `before`

```mermaid theme={null}
flowchart TD
    A["Comienza la comprobación de autorización<br>authorize('update', $post)"] --> B["Se ejecuta PostPolicy::before()"]
    B --> C{"¿El valor devuelto es null?"}
    C -->|"Distinto de null (true / false)"| D["Se usa el resultado de before() como decisión final"]
    C -->|"null"| E["Se ejecuta el método normal de la policy<br>update() / delete() etc."]
    E --> F{"Valor devuelto por el método"}
    F -->|"true"| G["✓ Acceso permitido"]
    F -->|"false"| H["✗ Acceso denegado → 403"]
    D -->|"true (bypass de administrador)"| G
    D -->|"false"| H
```

## Usar policies en el controlador

### Método `authorize()`

Los controladores de Laravel disponen de un helper `authorize()` (si no usas el controlador base, utiliza `Gate::authorize()`).

<Tabs>
  <Tab title="Gate::authorize()">
    ```php theme={null}
    <?php

    namespace App\Http\Controllers;

    use App\Models\Post;
    use Illuminate\Http\RedirectResponse;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Gate;

    class PostController extends Controller
    {
        public function update(Request $request, Post $post): RedirectResponse
        {
            Gate::authorize('update', $post);

            // Actualizar el post...

            return redirect()->route('posts.index');
        }

        public function store(Request $request): RedirectResponse
        {
            Gate::authorize('create', Post::class);

            // Crear el post...

            return redirect()->route('posts.index');
        }

        public function destroy(Post $post): RedirectResponse
        {
            Gate::authorize('delete', $post);

            $post->delete();

            return redirect()->route('posts.index');
        }
    }
    ```
  </Tab>

  <Tab title="User::can() / cannot()">
    ```php theme={null}
    <?php

    namespace App\Http\Controllers;

    use App\Models\Post;
    use Illuminate\Http\RedirectResponse;
    use Illuminate\Http\Request;

    class PostController extends Controller
    {
        public function update(Request $request, Post $post): RedirectResponse
        {
            if ($request->user()->cannot('update', $post)) {
                abort(403);
            }

            // Actualizar el post...

            return redirect()->route('posts.index');
        }
    }
    ```
  </Tab>
</Tabs>

### Registrar policies RESTful con `authorizeResource()`

Llamando a `authorizeResource()` en el constructor, cada acción del controlador queda asociada automáticamente al método correspondiente de la policy.

```php theme={null}
<?php

namespace App\Http\Controllers;

use App\Models\Post;

class PostController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(Post::class, 'post');
    }

    // Los métodos index(), show(), create(), store(), edit(), update() y destroy()
    // aplican automáticamente el método correspondiente de la policy
}
```

Correspondencia entre acciones del controlador y métodos de la policy:

| Acción del controlador | Método de la policy |
| ---------------------- | ------------------- |
| `index`                | `viewAny`           |
| `show`                 | `view`              |
| `create` / `store`     | `create`            |
| `edit` / `update`      | `update`            |
| `destroy`              | `delete`            |

<Tip>
  Con `authorizeResource()` ya no necesitas llamar a `authorize()` en cada acción. Resulta especialmente práctico con los resource controllers RESTful.
</Tip>

## Autorización en middleware

Para autorizar a nivel de ruta utiliza el middleware `can`.

```php theme={null}
use App\Models\Post;

// Solo pueden acceder los usuarios con permiso para actualizar el post
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->middleware('can:update,post');

// Solo pueden acceder los usuarios con permiso para crear posts
Route::post('/posts', [PostController::class, 'store'])
    ->middleware('can:create,App\Models\Post');
```

Una sintaxis más concisa mediante el método `can`:

```php theme={null}
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->can('update', 'post');
```

## Usar policies en plantillas Blade

Cuando la policy está registrada, las directivas `@can` y `@cannot` la utilizan automáticamente.

```blade theme={null}
{{-- El botón de editar solo se muestra a los usuarios autorizados --}}
@can('update', $post)
    <a href="{{ route('posts.edit', $post) }}" class="btn">Editar</a>
@endcan

{{-- Lo mismo con el botón de eliminar --}}
@can('delete', $post)
    <form method="POST" action="{{ route('posts.destroy', $post) }}">
        @csrf
        @method('DELETE')
        <button type="submit">Eliminar</button>
    </form>
@endcan

{{-- Botón para crear un nuevo post --}}
@can('create', App\Models\Post::class)
    <a href="{{ route('posts.create') }}">Nuevo post</a>
@endcan
```

## Ejemplo práctico: permisos de los posts de un blog

Ejemplo de implementación en una aplicación de blog donde «solo el autor puede editar y borrar sus posts».

<Steps>
  <Step title="Generar la policy">
    ```shell theme={null}
    php artisan make:policy PostPolicy --model=Post
    ```
  </Step>

  <Step title="Implementar los métodos de la policy">
    ```php theme={null}
    <?php

    namespace App\Policies;

    use App\Models\Post;
    use App\Models\User;

    class PostPolicy
    {
        /**
         * Concede todos los permisos a los administradores
         */
        public function before(User $user, string $ability): bool|null
        {
            if ($user->is_admin) {
                return true;
            }

            return null;
        }

        public function viewAny(User $user): bool
        {
            return true;
        }

        public function view(User $user, Post $post): bool
        {
            return true;
        }

        public function create(User $user): bool
        {
            return true;
        }

        public function update(User $user, Post $post): bool
        {
            return $user->id === $post->user_id;
        }

        public function delete(User $user, Post $post): bool
        {
            return $user->id === $post->user_id;
        }
    }
    ```
  </Step>

  <Step title="Añadir authorizeResource() al controlador">
    ```php theme={null}
    <?php

    namespace App\Http\Controllers;

    use App\Models\Post;
    use Illuminate\Http\RedirectResponse;
    use Illuminate\Http\Request;
    use Illuminate\View\View;

    class PostController extends Controller
    {
        public function __construct()
        {
            $this->authorizeResource(Post::class, 'post');
        }

        public function index(): View
        {
            $posts = Post::latest()->paginate(10);
            return view('posts.index', compact('posts'));
        }

        public function store(Request $request): RedirectResponse
        {
            $post = $request->user()->posts()->create(
                $request->validate([
                    'title' => ['required', 'string', 'max:255'],
                    'body'  => ['required', 'string'],
                ])
            );

            return redirect()->route('posts.show', $post);
        }

        public function update(Request $request, Post $post): RedirectResponse
        {
            $post->update(
                $request->validate([
                    'title' => ['required', 'string', 'max:255'],
                    'body'  => ['required', 'string'],
                ])
            );

            return redirect()->route('posts.show', $post);
        }

        public function destroy(Post $post): RedirectResponse
        {
            $post->delete();

            return redirect()->route('posts.index');
        }
    }
    ```
  </Step>

  <Step title="Añadir comprobaciones de permisos a la plantilla Blade">
    ```blade theme={null}
    {{-- resources/views/posts/show.blade.php --}}
    <h1>{{ $post->title }}</h1>
    <p>{{ $post->body }}</p>
    <p>Autor: {{ $post->user->name }}</p>

    @can('update', $post)
        <a href="{{ route('posts.edit', $post) }}">Editar</a>
    @endcan

    @can('delete', $post)
        <form method="POST" action="{{ route('posts.destroy', $post) }}">
            @csrf
            @method('DELETE')
            <button type="submit">Eliminar</button>
        </form>
    @endcan
    ```
  </Step>
</Steps>

## Resumen

<AccordionGroup>
  <Accordion title="Cuándo usar gates y cuándo policies">
    * **Gates**: comprobaciones simples que no están asociadas a un modelo. Acceso al panel de administración, permisos globales, etc.
    * **Policies**: gestión de permisos CRUD sobre modelos. Crea una policy por recurso (`Post`, `Order`, `Comment`, etc.).
  </Accordion>

  <Accordion title="APIs más habituales">
    ```php theme={null}
    // Comprobar mediante gate
    Gate::allows('update-post', $post);      // true/false
    Gate::denies('update-post', $post);      // true/false
    Gate::authorize('update-post', $post);   // Lanza excepción si falla

    // Comprobar desde el modelo User
    $user->can('update', $post);     // true/false
    $user->cannot('update', $post);  // true/false

    // Comprobar en un controlador
    Gate::authorize('update', $post);        // 403 si falla

    // Comprobar en Blade
    @can('update', $post) ... @endcan
    @cannot('update', $post) ... @endcannot
    ```
  </Accordion>

  <Accordion title="Comandos Artisan habituales">
    ```shell theme={null}
    # Generar una policy vacía
    php artisan make:policy PostPolicy

    # Generarla con los métodos CRUD correspondientes al modelo
    php artisan make:policy PostPolicy --model=Post
    ```
  </Accordion>
</AccordionGroup>


## Related topics

- [Laravel Telescope](/es/telescope.md)
- [Laravel Horizon](/es/horizon.md)
- [Cifrado (Encryption)](/es/encryption.md)
- [Laravel Sentinel — Investigación del middleware de protección de rutas](/es/blog/sentinel-introduction.md)
- [Manejo de errores](/es/error-handling.md)
