credentials(); /** @var User|null $user */ $user = User::query()->when( isset($credentials['email']), fn ($query) => $query->where('email', $credentials['email']), fn ($query) => $query->where('username', $credentials['username'] ?? null) )->first(); if (! $user || ! Hash::check($credentials['password'], (string) $user->password)) { throw ValidationException::withMessages([ 'login' => [trans('auth.failed')], ]); } $this->ensureUserCanAccessPanel($user); if ($user->email_verified_at === null) { throw ValidationException::withMessages([ 'login' => [trans('auth.unverified')], ]); } [$token, $abilities] = $this->issueTokenFor($user); return response()->json([ 'token' => $token, 'token_type' => 'Bearer', 'abilities' => $abilities, 'user' => [ 'id' => $user->id, 'email' => $user->email, 'name' => $user->name, 'role' => $user->role, 'tenant_id' => $user->tenant_id, ], ]); } public function destroy(Request $request): JsonResponse { $user = $request->user(); $token = $user?->currentAccessToken(); if ($token) { $token->delete(); } return response()->json(['ok' => true]); } public function me(Request $request): JsonResponse { $user = $request->user(); /** @var Tenant|null $tenant */ $tenant = $request->attributes->get('tenant'); if (! $tenant && $user?->tenant_id) { $tenant = Tenant::query()->find($user->tenant_id); } $tenantPayload = null; if ($tenant) { $tenantPayload = [ 'id' => $tenant->id, 'tenant_id' => $tenant->id, 'name' => $tenant->name, 'slug' => $tenant->slug, 'features' => $tenant->features, ]; } return response()->json([ 'user' => $user ? Arr::only($user->toArray(), [ 'id', 'name', 'email', 'role', 'tenant_id', ]) : null, 'tenant' => $tenantPayload, 'abilities' => $user?->currentAccessToken()?->abilities ?? [], ]); } public function legacyTenantMe(Request $request): JsonResponse { /** @var Tenant|null $tenant */ $tenant = $request->attributes->get('tenant') ?? $request->user()?->tenant; if (! $tenant) { return response()->json([ 'error' => 'tenant_not_found', 'message' => 'Tenant context missing.', ], 404); } $tenant->loadMissing('activeResellerPackage'); $user = $request->user(); $abilities = $user?->currentAccessToken()?->abilities ?? []; $fullName = null; if ($user) { $first = trim((string) ($user->first_name ?? '')); $last = trim((string) ($user->last_name ?? '')); $fullName = trim($first.' '.$last) ?: null; } $activePackage = $tenant->activeResellerPackage; return response()->json([ 'id' => $tenant->id, 'tenant_id' => $tenant->id, 'name' => $tenant->name, 'slug' => $tenant->slug, 'email' => $tenant->contact_email, 'fullName' => $fullName, 'active_reseller_package_id' => $activePackage?->id, 'remaining_events' => $activePackage?->remaining_events ?? 0, 'package_expires_at' => $activePackage?->expires_at, 'features' => $tenant->features ?? [], 'scopes' => $abilities, ]); } public function exchange(Request $request): JsonResponse|Response { /** @var User|null $user */ $user = Auth::guard('web')->user(); if (! $user) { return response()->noContent(); } $this->ensureUserCanAccessPanel($user); if ($user->email_verified_at === null) { return response()->json([ 'error' => 'unverified', 'message' => trans('auth.unverified'), ], 422); } [$token, $abilities] = $this->issueTokenFor($user); return response()->json([ 'token' => $token, 'token_type' => 'Bearer', 'abilities' => $abilities, 'user' => [ 'id' => $user->id, 'email' => $user->email, 'name' => $user->name, 'role' => $user->role, 'tenant_id' => $user->tenant_id, ], ]); } /** * @return array */ private function resolveTokenAbilities(User $user): array { $abilities = ['tenant-member']; if ($user->tenant_id) { $abilities[] = 'tenant:'.$user->tenant_id; } if (in_array($user->role, ['tenant_admin', 'admin', 'super_admin'], true)) { $abilities[] = 'tenant-admin'; } if ($user->role === 'super_admin') { $abilities[] = 'super-admin'; } return $abilities; } /** * @return array{0: string, 1: array} */ private function issueTokenFor(User $user): array { $user->tokens()->where('name', 'tenant-admin')->delete(); $abilities = $this->resolveTokenAbilities($user); $token = $user->createToken('tenant-admin', $abilities); return [$token->plainTextToken, $abilities]; } private function ensureUserCanAccessPanel(User $user): void { if (in_array($user->role, ['tenant_admin', 'admin', 'super_admin'], true)) { return; } if ($user->role === 'member' && $this->userHasCollaboratorMembership($user)) { return; } throw ValidationException::withMessages([ 'login' => [trans('auth.not_authorized')], ]); } private function userHasCollaboratorMembership(User $user): bool { if (! $user->tenant_id) { return false; } return EventMember::query() ->where('tenant_id', $user->tenant_id) ->where(function ($query) use ($user) { $query->where('user_id', $user->id) ->orWhere('email', $user->email); }) ->whereIn('status', ['active', 'invited']) ->exists(); } }