بنقرة واحدة
authentication-patterns
Use when implementing authentication, authorization, or access control. Covers Laravel Sanctum, OAuth 2.0, RBAC with Spatie Permissions, multi-tenant auth, API token management, MFA, and session security.
القائمة
Use when implementing authentication, authorization, or access control. Covers Laravel Sanctum, OAuth 2.0, RBAC with Spatie Permissions, multi-tenant auth, API token management, MFA, and session security.
Use when managing Laravel Forge servers, sites, deployments, SSL, databases, queue workers, scheduled jobs, security rules, and server provisioning. Covers Forge CLI, API, deployment scripts, Nginx templates, daemon management, and zero-downtime deployments.
Project-specific Ansible operations template — customize with your inventory, playbook catalog, deployment workflows, and host group details
Use when implementing or auditing accessibility features. Covers WCAG 2.1 AA/AAA compliance, ARIA roles and attributes, keyboard navigation, screen reader support, color contrast, focus management, and accessible component patterns.
Use when evaluating AI/LLM systems — benchmark design, automated evaluation pipelines, human evaluation protocols, A/B testing, hallucination detection, factuality checking, bias testing, safety evaluation (red teaming), latency/cost metrics, eval datasets, regression testing for prompts, and model comparison frameworks.
Use when building KPI frameworks, designing dashboards, analyzing marketing performance, setting up attribution models, or creating automated reports. Covers Google Analytics 4, social media analytics, funnel analysis, cohort analysis, A/B test analysis, ROI calculation, competitive benchmarking, and stakeholder reporting.
Use when designing or implementing REST APIs. Covers RESTful conventions, versioning, pagination, filtering, error handling, rate limiting, HATEOAS, OpenAPI documentation, and API security best practices.
| name | authentication-patterns |
| description | Use when implementing authentication, authorization, or access control. Covers Laravel Sanctum, OAuth 2.0, RBAC with Spatie Permissions, multi-tenant auth, API token management, MFA, and session security. |
| user-invocable | false |
| allowed-tools | ["Read","Write","Bash","Grep","Glob"] |
All patterns enforce multi-tenant isolation via branch_id scoping.
| Approach | Use When |
|---|---|
| Sanctum (SPA cookies) | First-party SPA on same domain |
| Sanctum (API tokens) | First-party mobile or third-party with simple scopes |
| Passport (OAuth 2.0) | Third-party apps needing authorization code flow |
| JWT (raw) | Stateless cross-service communication only |
// config/sanctum.php
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,localhost:3000')),
'expiration' => 120,
// Login endpoint
Route::post('/login', function (Request $request) {
$request->validate(['email' => ['required', 'email'], 'password' => ['required']]);
if (! Auth::attempt($request->only('email', 'password'))) {
throw ValidationException::withMessages(['email' => ['Invalid credentials.']]);
}
$request->session()->regenerate();
return response()->json(['data' => new UserResource(Auth::user())]);
});
$token = $user->createToken('api-client', ['invoices:read', 'invoices:write', 'reports:read']);
Route::middleware('auth:sanctum')->group(function () {
Route::get('/invoices', [InvoiceController::class, 'index'])->middleware('ability:invoices:read');
Route::post('/invoices', [InvoiceController::class, 'store'])->middleware('ability:invoices:write');
});
// Prune expired tokens daily
$schedule->command('sanctum:prune-expired --hours=48')->daily();
// Seed permissions by module
$permissions = [
'invoices.view', 'invoices.create', 'invoices.edit', 'invoices.delete', 'invoices.approve',
'reports.view', 'reports.export', 'users.view', 'users.create', 'users.edit', 'settings.manage',
];
foreach ($permissions as $p) { Permission::firstOrCreate(['name' => $p, 'guard_name' => 'web']); }
$admin = Role::firstOrCreate(['name' => 'admin']);
$admin->givePermissionTo(Permission::all());
$accountant = Role::firstOrCreate(['name' => 'accountant']);
$accountant->givePermissionTo(['invoices.view', 'invoices.create', 'invoices.edit', 'reports.view']);
class InvoicePolicy
{
public function view(User $user, Invoice $invoice): bool {
return $user->hasPermissionTo('invoices.view') && $user->branch_id === $invoice->branch_id;
}
public function update(User $user, Invoice $invoice): bool {
return $user->hasPermissionTo('invoices.edit')
&& $user->branch_id === $invoice->branch_id
&& $invoice->status !== 'approved';
}
public function approve(User $user, Invoice $invoice): bool {
return $user->hasPermissionTo('invoices.approve')
&& $user->branch_id === $invoice->branch_id
&& $user->id !== $invoice->created_by; // segregation of duties
}
}
class EnsureBranchScope
{
public function handle(Request $request, Closure $next): Response {
if (Auth::check()) {
app()->instance('current_branch_id', Auth::user()->branch_id);
if ($request->has('branch_id') && (int) $request->branch_id !== Auth::user()->branch_id) {
abort(403, 'Cross-tenant access denied.');
}
}
return $next($request);
}
}
public function switchBranch(Request $request): JsonResponse {
$branch = Branch::findOrFail($request->validate(['branch_id' => 'required|exists:branches,id'])['branch_id']);
abort_unless($request->user()->company_id === $branch->company_id, 403);
abort_unless($request->user()->hasRole('company-admin'), 403);
$request->user()->update(['branch_id' => $branch->id]);
$request->session()->regenerate();
return response()->json(['data' => new UserResource($request->user()->fresh())]);
}
// Enable: generate secret, return QR URL
public function enableMfa(Request $request): JsonResponse {
$secret = (new Google2FA())->generateSecretKey();
$request->user()->update(['mfa_secret' => encrypt($secret), 'mfa_enabled' => false]);
return response()->json([
'qr_url' => (new Google2FA())->getQRCodeUrl(config('app.name'), $request->user()->email, $secret),
]);
}
// Confirm: verify code, generate backup codes
public function confirmMfa(Request $request): JsonResponse {
$valid = (new Google2FA())->verifyKey(decrypt($request->user()->mfa_secret), $request->validate(['code' => 'required|digits:6'])['code']);
abort_unless($valid, 422, 'Invalid MFA code.');
$backupCodes = Collection::times(8, fn () => Str::random(8))->toArray();
$request->user()->update(['mfa_enabled' => true, 'mfa_backup_codes' => encrypt(json_encode($backupCodes))]);
return response()->json(['backup_codes' => $backupCodes]);
}
// Enforcement middleware: require MFA for admin roles
class RequireMfa {
public function handle(Request $request, Closure $next): Response {
$user = $request->user();
if ($user->hasRole('admin') && ! $user->mfa_enabled) return response()->json(['message' => 'MFA required.'], 403);
if ($user->mfa_enabled && ! session('mfa_verified')) return response()->json(['message' => 'MFA verification required.'], 403);
return $next($request);
}
}
// Password rules
'password' => ['required', 'confirmed', Password::min(12)->letters()->mixedCase()->numbers()->symbols()->uncompromised()],
// Session config essentials
'lifetime' => 120, 'encrypt' => true, 'secure' => true, 'http_only' => true, 'same_site' => 'lax',
// Regenerate on login, invalidate on logout, regenerateToken for CSRF rotation
export const useAuthStore = defineStore('auth', {
state: (): { user: User | null; isAuthenticated: boolean } => ({ user: null, isAuthenticated: false }),
actions: {
async login(email: string, password: string) {
await api.get('/sanctum/csrf-cookie');
await api.post('/login', { email, password });
await this.fetchUser();
},
async fetchUser() { this.user = (await api.get('/api/user')).data; this.isAuthenticated = true; },
can(permission: string): boolean { return this.user?.permissions?.includes(permission) ?? false; },
},
});
// Router guard
router.beforeEach(async (to, from, next) => {
const auth = useAuthStore();
if (to.meta.requiresAuth && !auth.isAuthenticated) return next({ name: 'login', query: { redirect: to.fullPath } });
if (to.meta.permission && !auth.can(to.meta.permission as string)) return next({ name: 'forbidden' });
next();
});
class SecurityHeaders {
public function handle(Request $request, Closure $next): Response {
$response = $next($request);
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
$response->headers->set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
return $response;
}
}