Tyro Documentation
Complete guide to Authentication, Authorization, and Role & Privilege Management for Laravel 12
Introduction
Tyro is the ultimate Authentication, Authorization, and Role & Privilege Management solution for Laravel 12. Think of it as a Swiss Army knife that handles everything from user authentication and role-based access control to user suspension workflows—whether you're building an API, a traditional web application, or both.
With Sanctum integration, 40+ powerful CLI commands, Blade directives, ready-made middleware, and optional REST API endpoints, Tyro saves you weeks of development time while providing enterprise-grade security features.
Why Choose Tyro?
Tyro isn't just a package; it's a complete auth and access control toolkit. Here is why developers choose Tyro:
Complete Auth System
Full authentication with Sanctum, role-based access control, privileges, and Laravel Gate integration. Works for APIs and web apps.
Secure by Default
Built on standard Laravel security. Assign roles and permissions to users with simple, readable code.
40+ CLI Commands
Manage users, roles, privileges, and tokens from the terminal. Perfect for automation, CI/CD, and incident response.
Grows With You
Start small and scale up without rewriting your core authentication logic. Tyro adapts to your project size.
Blade Directives
Use @hasrole, @hasprivilege, and more in your Blade templates. Clean, readable views without PHP logic clutter.
Optional REST API
Need API endpoints? They're included. Don't need them? Disable with one config flag. Zero lock-in.
Requirements
Before installing Tyro, ensure your environment meets these requirements:
- PHP: 8.2 or higher
- Laravel: 12.0 or higher
- Laravel Sanctum: 4.0 or higher
- Database: MySQL, PostgreSQL, SQLite, or SQL Server
Installation
Step 1: Install the Package
Install Tyro via Composer:
composer require hasinhayder/tyro
Step 2: Run the Installer
Run the all-in-one installer command:
php artisan tyro:install
This command automatically:
- Calls Laravel's
install:apito set up Sanctum - Runs database migrations
- Seeds default roles and privileges
- Prepares your User model with required traits
--force flag in production environments.
Step 3: Verify Installation
Check that everything is set up correctly:
php artisan tyro:version
Quick Start
After installation, you have a complete authentication and authorization system. Here's what you can do immediately:
1. Login with Default Admin
curl -X POST http://localhost/api/login \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"email":"admin@tyro.project","password":"tyro"}'
2. Use the Token
Save the token from the response and use it in subsequent requests:
curl http://localhost/api/me \
-H "Authorization: Bearer YOUR_TOKEN_HERE"
3. Check Permissions in Code
Use Tyro's intuitive API anywhere in your application:
// Check roles
if ($user->hasRole('admin')) { ... }
// Check privileges
if ($user->can('reports.run')) { ... }
// Get all role slugs
$roles = $user->tyroRoleSlugs(); // ['admin', 'editor']
4. Use Blade Directives
Conditionally render content in your views:
@hasrole('admin')
<p>Welcome, Admin!</p>
@endhasrole
@hasprivilege('edit-posts')
<button>Edit Post</button>
@endhasprivilege
5. Protect Routes
Use middleware to protect your routes:
Route::middleware(['auth:sanctum', 'role:admin'])
->get('/admin/dashboard', DashboardController::class);
Roles & Privileges
Tyro uses a flexible role-privilege system where roles (like "admin", "editor") can have multiple privileges (like "reports.run", "billing.view"). Users are assigned roles and inherit all privileges from those roles.
Default Roles
Tyro seeds these roles by default:
| Role | Slug | Description |
|---|---|---|
| Super Admin | super-admin |
Full system access with all privileges |
| Administrator | admin |
Administrative access to manage users and roles |
| Editor | editor |
Content management privileges |
| User | user |
Default role for new registrations |
| Customer | customer |
Customer-specific access |
Working with Privileges
Create and attach privileges to roles:
# Create a new privilege
php artisan tyro:add-privilege reports.run --name="Run Reports"
# Attach it to a role
php artisan tyro:attach-privilege reports.run editor
Authentication
Tyro uses Laravel Sanctum for API authentication. Tokens automatically include all role and privilege slugs as abilities.
Generating Tokens via CLI
# Generate token with password prompt
php artisan tyro:login --user=admin@tyro.project
# Quick token without password (development only)
php artisan tyro:quick-token --user=1
Token Inspection
Inspect what a token contains:
php artisan tyro:me
Then paste your token when prompted to see the user and abilities.
Middleware
Tyro provides powerful middleware for protecting your routes:
| Middleware | Usage | Description |
|---|---|---|
ability |
ability:admin,reports.run |
Require ALL listed abilities |
abilities |
abilities:admin,editor |
Allow ANY of the listed abilities |
role |
role:admin |
Require ALL listed roles |
roles |
roles:admin,editor |
Allow ANY of the listed roles |
privilege |
privilege:reports.run |
Require ALL listed privileges |
privileges |
privileges:billing.view |
Allow ANY of the listed privileges |
tyro.log |
tyro.log |
Log request/response pairs for auditing |
Example Usage
use Illuminate\Support\Facades\Route;
// Require admin role
Route::middleware(['auth:sanctum', 'role:admin'])
->get('/admin/dashboard', DashboardController::class);
// Allow either admin or editor
Route::middleware(['auth:sanctum', 'roles:admin,editor'])
->post('/articles', ArticleController::class);
// Require specific privilege
Route::middleware(['auth:sanctum', 'privilege:reports.run'])
->get('/reports', ReportController::class);
Blade Directives
Tyro provides custom Blade directives for checking user roles and privileges directly in your views. All directives automatically return false if no user is authenticated.
@usercan
Checks if the current user has a specific role or privilege (uses the can() method):
@usercan('admin')
<div class="admin-panel">
<h2>Admin Dashboard</h2>
<p>Welcome to the admin area!</p>
</div>
@endusercan
@usercan('edit-posts')
<button class="btn btn-primary">Edit Post</button>
@endusercan
@hasrole
Checks if the current user has a specific role:
@hasrole('admin')
<p>Welcome, Admin!</p>
@endhasrole
@hasrole('editor')
<a href="/dashboard/editor" class="nav-link">Editor Dashboard</a>
@endhasrole
@hasanyrole
Checks if the current user has any of the provided roles:
@hasanyrole('admin', 'editor', 'moderator')
<div class="management-tools">
<h3>Management Tools</h3>
<p>You have access to management features</p>
</div>
@endhasanyrole
@hasroles
Checks if the current user has all of the provided roles:
@hasroles('admin', 'super-admin')
<div class="super-admin-panel">
<p>You have both admin and super-admin privileges</p>
<button class="btn-danger">Critical Actions</button>
</div>
@endhasroles
@hasprivilege
Checks if the current user has a specific privilege:
@hasprivilege('delete-users')
<button class="btn btn-danger" onclick="deleteUser()">
Delete User
</button>
@endhasprivilege
@hasprivilege('view-reports')
<a href="/reports" class="nav-link">
<i class="icon-reports"></i> View Reports
</a>
@endhasprivilege
@hasanyprivilege
Checks if the current user has any of the provided privileges:
@hasanyprivilege('edit-posts', 'delete-posts', 'publish-posts')
<div class="post-actions">
<h4>Post Management</h4>
@hasprivilege('edit-posts')
<button>Edit</button>
@endhasprivilege
@hasprivilege('delete-posts')
<button>Delete</button>
@endhasprivilege
</div>
@endhasanyprivilege
@hasprivileges
Checks if the current user has all of the provided privileges:
@hasprivileges('create-invoices', 'approve-invoices')
<button class="btn btn-success" onclick="createAndApproveInvoice()">
Create and Approve Invoice
</button>
@endhasprivileges
@hasprivileges('view-reports', 'export-reports')
<div class="reports-section">
<a href="/reports">View Reports</a>
<button onclick="exportReport()">Export</button>
</div>
@endhasprivileges
Combining Directives
You can nest and combine directives for complex authorization logic:
@hasrole('admin')
<div class="admin-section">
<h2>Admin Controls</h2>
@hasprivilege('manage-users')
<a href="/admin/users">Manage Users</a>
@endhasprivilege
@hasanyprivilege('view-reports', 'export-data')
<a href="/admin/reports">Reports</a>
@endhasanyprivilege
</div>
@endhasrole
@hasanyrole('editor', 'author')
<div class="content-tools">
@hasprivilege('publish-posts')
<button>Publish</button>
@else
<button disabled>Publish (requires approval)</button>
@endhasprivilege
</div>
@endhasanyrole
User Suspension
Tyro includes first-class user suspension support to freeze accounts without deleting them.
Suspending Users
# Via CLI
php artisan tyro:suspend-user --user=user@example.com --reason="Policy violation"
# Via Code
$user->suspend('Policy violation');
Unsuspending Users
# Via CLI
php artisan tyro:unsuspend-user --user=user@example.com
# Via Code
$user->unsuspend();
Checking Suspension Status
if ($user->isSuspended()) {
$reason = $user->getSuspensionReason();
return response()->json(['error' => $reason], 423);
}
HasTyroRoles Trait Reference
The HasTyroRoles trait gives your User model a complete API for roles, privileges, and suspensions. These methods are the same ones used by Tyro's routes and CLI commands.
Role Methods
| Method | Description |
|---|---|
roles(): BelongsToMany |
Returns the eager-loadable relationship for roles |
assignRole(Role $role): void |
Attaches a role without detaching existing ones |
removeRole(Role $role): void |
Detaches the given role from the user |
hasRole(string $role): bool |
Checks if user has the specified role slug (supports wildcard *) |
hasRoles(array $roles): bool |
Returns true only if user holds every role in the array |
tyroRoleSlugs(): array |
Returns array of all role slugs for the user (cached) |
Privilege Methods
| Method | Description |
|---|---|
privileges(): Collection |
Returns all unique privileges inherited through user's roles |
hasPrivilege(string $privilege): bool |
Checks if user has a specific privilege |
hasPrivileges(array $privileges): bool |
Returns true only if user has all specified privileges |
tyroPrivilegeSlugs(): array |
Returns array of all privilege slugs for the user (cached) |
can($ability, $args = []): bool |
Checks privilege, then role, then falls back to Laravel Gate |
Suspension Methods
| Method | Description |
|---|---|
suspend(?string $reason = null): void |
Suspends user, stores optional reason, revokes all tokens |
unsuspend(): void |
Clears suspension without touching roles or privileges |
isSuspended(): bool |
Returns true if user is currently suspended |
getSuspensionReason(): ?string |
Returns the stored suspension reason (or null) |
config/tyro.php settings.
Setup & Installation Commands
php artisan tyro:install
php artisan tyro:prepare-user-model
php artisan tyro:seed --force
php artisan tyro:publish-config --force
php artisan tyro:publish-migrations --force
User Management Commands
php artisan tyro:create-user
php artisan tyro:users
php artisan tyro:suspend-user --user=5 --reason="Manual review"
php artisan tyro:unsuspend-user --user=user@example.com
php artisan tyro:user-privileges 1
Role & Privilege Commands
php artisan tyro:roles
php artisan tyro:roles-with-privileges
php artisan tyro:create-role --name="Manager" --slug="manager"
php artisan tyro:assign-role --user=5 --role=editor
php artisan tyro:add-privilege reports.run --name="Run Reports"
php artisan tyro:attach-privilege reports.run editor
php artisan tyro:detach-privilege reports.run editor
Token Management Commands
php artisan tyro:login --user=admin@tyro.project
php artisan tyro:quick-token --user=1
php artisan tyro:me
php artisan tyro:logout-all-users --force
Checking Permissions
Check User Roles
use Illuminate\Http\Request;
class ArticleController
{
public function store(Request $request)
{
// Check single role
if (!$request->user()->hasRole('admin')) {
abort(403, 'Admin access required');
}
// Check multiple roles (requires ALL)
if (!$request->user()->hasRoles(['admin', 'editor'])) {
abort(403, 'Insufficient permissions');
}
// Create article...
}
}
Check User Privileges
class ReportController
{
public function index(Request $request)
{
// Check single privilege using can()
if (!$request->user()->can('reports.run')) {
abort(403, 'Missing reports privilege');
}
// Check multiple privileges (requires ALL)
if (!$request->user()->hasPrivileges(['reports.run', 'billing.view'])) {
abort(403, 'Missing required privileges');
}
// Generate report...
}
}
Get User's Roles and Privileges
// Get role slugs
$roleSlugs = $request->user()->tyroRoleSlugs();
// Get privilege slugs
$privilegeSlugs = $request->user()->tyroPrivilegeSlugs();
// Get full role models with relationships
$roles = $request->user()->roles()->with('privileges')->get();
Managing Roles Programmatically
Assign Roles to Users
use HasinHayder\Tyro\Models\Role;
use App\Models\User;
$user = User::find(1);
$editorRole = Role::where('slug', 'editor')->first();
// Assign a single role
$user->assignRole($editorRole);
// Or use the relationship directly
$user->roles()->attach($editorRole->id);
Remove Roles from Users
// Remove a single role
$user->removeRole($editorRole);
// Or use the relationship
$user->roles()->detach($editorRole->id);
// Remove all roles
$user->roles()->detach();
Manage Privileges on Roles
use HasinHayder\Tyro\Models\Privilege;
$role = Role::where('slug', 'editor')->first();
$privilege = Privilege::where('slug', 'reports.run')->first();
// Attach privilege to role
$role->privileges()->attach($privilege->id);
// Detach privilege from role
$role->privileges()->detach($privilege->id);
// Sync privileges (replaces all existing)
$role->privileges()->sync([
$privilege1->id,
$privilege2->id,
]);
Check Role Privileges
$role = Role::where('slug', 'editor')->first();
// Check if role has a privilege
if ($role->hasPrivilege('reports.run')) {
// Role has the privilege
}
// Check if role has all specified privileges
if ($role->hasPrivileges(['reports.run', 'billing.view'])) {
// Role has both privileges
}
Protecting API Routes
Basic Route Protection
use Illuminate\Support\Facades\Route;
// Require authentication
Route::middleware(['auth:sanctum'])
->get('/profile', ProfileController::class);
// Require specific role
Route::middleware(['auth:sanctum', 'role:admin'])
->get('/admin/dashboard', AdminDashboardController::class);
// Allow any of multiple roles
Route::middleware(['auth:sanctum', 'roles:admin,editor'])
->post('/articles', ArticleController::class);
Privilege-Based Protection
// Require specific privilege
Route::middleware(['auth:sanctum', 'privilege:reports.run'])
->get('/reports', ReportController::class);
// Allow any of multiple privileges
Route::middleware(['auth:sanctum', 'privileges:billing.view,reports.run'])
->get('/analytics', AnalyticsController::class);
Mixed Abilities (Roles + Privileges)
// Require ALL abilities (roles and/or privileges)
Route::middleware(['auth:sanctum', 'ability:admin,reports.run'])
->post('/reports/export', ReportExportController::class);
// Allow ANY ability
Route::middleware(['auth:sanctum', 'abilities:admin,super-admin,reports.run'])
->get('/reports/advanced', AdvancedReportController::class);
With Audit Logging
// Add tyro.log middleware for request/response logging
Route::middleware(['auth:sanctum', 'ability:billing.view', 'tyro.log'])
->get('/billing/statements', BillingStatementController::class);
API Endpoints Reference
Complete list of available API endpoints in Tyro.
Public Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/login |
Authenticate user and retrieve token |
| POST | /api/users |
Register a new user |
| GET | /api/tyro |
Get Tyro package information |
| GET | /api/tyro/version |
Get installed Tyro version |
Authenticated User Endpoints
Requires auth:sanctum middleware.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/me |
Get current authenticated user profile |
| PUT/PATCH | /api/users/{user} |
Update own profile (requires user ability) |
Admin Endpoints
Requires auth:sanctum and admin abilities.
User Management
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/users |
List all users |
| GET | /api/users/{user} |
Get specific user details |
| DELETE | /api/users/{user} |
Delete a user |
| POST | /api/users/{user}/suspend |
Suspend a user account |
| DELETE | /api/users/{user}/suspend |
Unsuspend a user account |
Role & Privilege Management
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/roles |
List all roles |
| POST | /api/roles |
Create a new role |
| GET | /api/roles/{role} |
Get role details |
| PUT/PATCH | /api/roles/{role} |
Update a role |
| DELETE | /api/roles/{role} |
Delete a role |
| GET | /api/privileges |
List all privileges |
| POST | /api/privileges |
Create a new privilege |
| GET | /api/privileges/{privilege} |
Get privilege details |
| PUT/PATCH | /api/privileges/{privilege} |
Update a privilege |
| DELETE | /api/privileges/{privilege} |
Delete a privilege |
Assignments
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/users/{user}/roles |
List roles assigned to user |
| POST | /api/users/{user}/roles |
Assign role to user |
| DELETE | /api/users/{user}/roles/{role} |
Remove role from user |
| GET | /api/roles/{role}/privileges |
List privileges in role |
| POST | /api/roles/{role}/privileges |
Attach privilege to role |
| DELETE | /api/roles/{role}/privileges/{privilege} |
Detach privilege from role |
Configuration
Publish the configuration file to customize Tyro:
php artisan tyro:publish-config --force
Key Configuration Options
| Option | Default | Description |
|---|---|---|
guard |
sanctum | Authentication guard for protected routes |
route_prefix |
api | URL prefix for all Tyro routes |
default_user_role_slug |
user | Role assigned to new registrations |
cache.enabled |
true | Enable role/privilege caching |
disable_api |
false | Disable built-in API routes |
disable_commands |
false | Disable artisan commands |
Environment Variables
# Disable commands in production
TYRO_DISABLE_COMMANDS=true
# Disable API routes
TYRO_DISABLE_API=true
Frequently Asked Questions
How do I change the default admin credentials?
After installation, immediately change the default admin password using:
php artisan tyro:update-user --user=admin@tyro.project --password=new_secure_password
Can I use Tyro with an existing Laravel project?
Yes! Tyro is designed to integrate seamlessly with existing projects. Just run tyro:install and it will set up alongside your existing code without conflicts.
How do I disable Tyro's built-in routes?
Set TYRO_DISABLE_API=true in your .env file to disable all built-in routes and implement your own.
What happens when I suspend a user?
Suspending a user sets a suspended_at timestamp, stores an optional reason, and immediately revokes all their Sanctum tokens. They cannot log in until unsuspended.
How do I add custom roles and privileges?
Use the artisan commands or create them programmatically:
# Via CLI
php artisan tyro:create-role --name="Manager" --slug="manager"
php artisan tyro:add-privilege custom.action --name="Custom Action"
# Via Code
Role::create(['name' => 'Manager', 'slug' => 'manager']);
Privilege::create(['name' => 'Custom Action', 'slug' => 'custom.action']);
Is Tyro production-ready?
Yes! Tyro is battle-tested and includes comprehensive security features, caching, and performance optimizations. It's used in production applications.
How do I get help or report bugs?
Visit the GitHub repository to open issues, read the full README, or contribute to the project.