tipoff/authorization
Opinionated Laravel authorization package enabling users with multiple email addresses and unique usernames. Includes User and EmailAddress models, policies, and auth guard/provider configuration. Requires Laravel Nova. Deprecated/archived; no longer actively maintained.
Installation
composer require tipoff/authorization
Publish the config (if needed):
php artisan vendor:publish --provider="Tipoff\Authorization\AuthorizationServiceProvider"
Basic Usage
Define a policy for a model (e.g., Post):
use Tipoff\Authorization\Contracts\Policy;
class PostPolicy implements Policy
{
public function view($user, Post $post)
{
return $user->isAdmin() || $post->user_id === $user->id;
}
}
Register Policy
Bind the policy in AuthServiceProvider:
public function boot()
{
$this->registerPolicies();
Authorization::registerPolicy(Post::class, PostPolicy::class);
}
First Use Case Check authorization in a controller:
public function show(Post $post)
{
if (!Authorization::can('view', $post)) {
abort(403);
}
// ...
}
Group Policies by Resource
Organize policies in app/Policies (e.g., PostPolicy, UserPolicy).
Use traits for shared logic:
trait CanManageOwnContent {
public function update($user, $model) { return $model->user_id === $user->id; }
}
Policy Composition Combine policies for complex rules:
class AdminPolicy implements Policy
{
public function __invoke($user, $ability)
{
return $user->isAdmin();
}
}
Gate Integration Use Laravel’s gates alongside the package:
Authorization::gate('update-post', function ($user, $post) {
return $post->user_id === $user->id;
});
Middleware for Routes Protect routes with middleware:
Route::get('/posts/{post}', [PostController::class, 'show'])
->middleware('can:view,post');
Dynamic Abilities Pass dynamic abilities to policies:
if (Authorization::can('edit', $post, ['soft_delete' => true])) {
// ...
}
Extend Existing Policies
Override default policies in AuthServiceProvider:
Authorization::extend('custom', function ($app) {
return new CustomPolicy();
});
Policy Caching Cache policies for performance (if using Laravel’s cache):
Authorization::cachePolicies(true);
Policy Registration Order
Ensure policies are registered after AuthServiceProvider boots.
Fix: Move Authorization::registerPolicy() to boot() or use service provider events.
Missing User Context Always pass the authenticated user to policies:
// ❌ Avoid
Authorization::can('view', $post);
// ✅ Correct
Authorization::can('view', $post, auth()->user());
Overly Granular Policies
Avoid creating policies for trivial checks (e.g., isActive()). Use gates instead.
Enable Logging
Set debug to true in config/authorization.php to log denied attempts:
'debug' => env('APP_DEBUG', false),
Policy Dump List all registered policies:
php artisan authorization:list-policies
Custom Policy Resolvers Extend the resolver to support non-model subjects:
Authorization::extend('custom-resolver', function ($app, $user, $subject) {
return new CustomPolicy($user, $subject);
});
Event Listeners
Listen for authorization events (e.g., Authorizing):
Authorization::authorizing(function ($user, $ability, $subject) {
logger()->info("Checking $ability on $subject for user {$user->id}");
});
Policy Testing Test policies in isolation:
public function test_view_policy()
{
$user = User::factory()->create();
$post = Post::factory()->create(['user_id' => $user->id]);
$this->assertTrue(Authorization::can('view', $post, $user));
}
Default Driver
The package uses Laravel’s default gate driver by default. Switch to policy in config/authorization.php:
'default_driver' => 'policy',
Ability Naming
Use kebab-case for abilities (e.g., view-post instead of viewPost). The package auto-converts snake_case to kebab-case.
How can I help you explore Laravel packages today?