Skip to content

ephort/laravel-data-authorization

Repository files navigation

Add authorization to your spatie/laravel-data objects

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

This package adds authorization to your spatie/laravel-data objects, which is very useful if you want to expose data objects to the frontend (e.g. when using Inertia), but still need to check if the user is allowed to perform certain actions.

Installation

Install the package via composer:

composer require ephort/laravel-data-authorization

Usage

This package is intended to be used with Inertia, but does not require it or depend on it.

To add the authorization checks to your data objects, extend the DataWithAuthorization class.
All the methods of the base Data class are still available.

Next, implement the static getAuthorizations method, which should return an array containing the names of the actions that need to be exposed and checked.

use Ephort\LaravelDataAuthorization\DataWithAuthorization;

class UserData extends DataWithAuthorization
{
    public function __construct(
        public int $id,
        public string $name,
    ) {
    }
    
    public static function getAuthorizations(): array
    {
        return [
            'view',
            'update',
            'delete',
        ];
    }
}

When the data object is transformed, a lazy authorization property is appended to the resulting array.

This property contains a key for each defined policy action and is evaluated by Gate::allows.

{
    "id": 1,
    "name": "Taylor Otwell",
    "authorization": {
        "view": true,
        "update": false,
        "delete": false
    }
}

Avoid processing authorizations

Because the authorization property is lazy, we can exclude it from the data object to avoid calling the gate on every serialization.

UserData::from($user)->exclude('authorization');

Or use the built-in helper method:

UserData::from($user)->withoutAuthorization();

Note when using custom from methods

When using a custom from method, the pipeline that resolves authorizations is not used.

This means you must call the static resolveAuthorizationArray method manually when instantiating your data object:

public static function fromModel(User $user): self
{
    return self::from([
        'id' => $user->id,
        'name' => $user->name,
        'authorization' => static::resolveAuthorizationArray($user),
    ]);
}

You can also wrap the authorization array in a Lazy property if needed:

Lazy::create(fn () => static::resolveAuthorizationArray($user))->defaultIncluded();

TypeScript support

Thanks to Spatie, it's very easy to generate TypeScript interfaces from data objects and enums.
Install the TypeScript Transformer package and publish its configuration file:

composer require spatie/laravel-typescript-transformer
php artisan vendor:publish --tag=typescript-transformer-config

Open config/typescript-transformer.php and add the following collector and transformer:

Ephort\LaravelDataAuthorization\Collectors\DataAuthorizationTypeScriptCollector::class must be the first collector.

'collectors' => [
+   Ephort\LaravelDataAuthorization\Collectors\DataAuthorizationTypeScriptCollector::class,
    Spatie\TypeScriptTransformer\Collectors\DefaultCollector::class,
    Spatie\TypeScriptTransformer\Collectors\EnumCollector::class,
],
'transformers' => [
    Spatie\LaravelTypeScriptTransformer\Transformers\SpatieStateTransformer::class,
    Spatie\TypeScriptTransformer\Transformers\EnumTransformer::class,
    Spatie\TypeScriptTransformer\Transformers\SpatieEnumTransformer::class,
    Spatie\LaravelTypeScriptTransformer\Transformers\DtoTransformer::class,
+   Ephort\LaravelDataAuthorization\Transformers\DataAuthorizationTypeScriptTransformer::class,
],

The above configuration uses a collector provided by this package, which finds data objects that extend DataWithAuthorization and generates typings with their authorizations. This is what powers typed authorization support.

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Credits

The code is primarily copied from the awesome project Hybridly by Enzo Innocenzi, which is a great alternative to Inertia.

License

The MIT License (MIT). Please see License File for more information.