Route::has('password.request'), 'status' => $request->session()->get('status'), ]); } /** * Handle an incoming authentication request. */ public function store(LoginRequest $request): SymfonyResponse { try { $request->authenticate(); } catch (\Illuminate\Validation\ValidationException $e) { $request->session()->flash('error', __('auth.login_failed')); return redirect()->route('login')->withErrors($e->errors()); } Log::info('Login attempt', ['login' => $request->login, 'authenticated' => Auth::check()]); $request->session()->regenerate(); $request->session()->flash('success', __('auth.login_success')); $user = Auth::user(); if ($user && $user->email_verified_at === null) { return Inertia::location(route('verification.notice')); } $intended = $this->resolveIntended($request); if ($intended !== null) { $this->rememberTenantAdminTarget($request, $intended); return Inertia::location($intended); } $returnTo = $this->resolveReturnTo($request); if ($returnTo !== null) { $this->rememberTenantAdminTarget($request, $returnTo); return Inertia::location($returnTo); } $default = $this->defaultAdminPath(); $this->rememberTenantAdminTarget($request, $default); return Inertia::location($default); } /** * Destroy an authenticated session. */ public function destroy(Request $request): RedirectResponse { Auth::guard('web')->logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); return redirect('/'); } private function resolveReturnTo(Request $request): ?string { $encoded = $request->string('return_to')->trim(); if ($encoded === '') { return null; } return $this->normalizeTenantAdminTarget( $this->decodeReturnTo($encoded, $request), $request ); } private function resolveIntended(Request $request): ?string { $intended = $request->session()->pull('url.intended'); if (! is_string($intended)) { return null; } $trimmed = trim($intended); if ($trimmed === '') { return null; } return $this->normalizeTenantAdminTarget( $this->decodeReturnTo($trimmed, $request), $request ); } private function decodeReturnTo(string $value, Request $request): ?string { $candidate = $this->decodeBase64Url($value) ?? $value; $candidate = trim($candidate); if ($candidate === '') { return null; } if (str_starts_with($candidate, '/')) { return $candidate; } $targetHost = parse_url($candidate, PHP_URL_HOST); $scheme = parse_url($candidate, PHP_URL_SCHEME); if (! $scheme || ! $targetHost) { return null; } $appHost = parse_url($request->getSchemeAndHttpHost(), PHP_URL_HOST); if ($appHost && ! Str::endsWith($targetHost, $appHost)) { return null; } return $candidate; } private function defaultAdminPath(): string { $user = Auth::user(); // Block users with 'user' role - redirect to package selection if ($user && $user->role === 'user') { return '/packages'; } // Super admins go to Filament superadmin panel if ($user && $user->isSuperAdmin()) { return '/super-admin'; } // Tenant admins go to their PWA dashboard if ($user && $user->role === 'tenant_admin') { return '/event-admin/dashboard'; } // Fallback: redirect to packages (for users with no role) return '/packages'; } private function normalizeTenantAdminTarget(?string $target, Request $request): ?string { $user = Auth::user(); if (! $user || $user->role !== 'tenant_admin') { return $target; } if ($target === null || $target === '') { return '/event-admin/dashboard'; } $parsed = parse_url($target); $path = $target; $hasScheme = false; if ($parsed !== false) { $hasScheme = isset($parsed['scheme']); $host = $parsed['host'] ?? null; $scheme = $parsed['scheme'] ?? null; $requestHost = parse_url($request->getSchemeAndHttpHost(), PHP_URL_HOST); if ($scheme && $host && $requestHost && ! Str::endsWith($host, $requestHost)) { return '/event-admin/dashboard'; } if (isset($parsed['path'])) { $path = $parsed['path']; if (isset($parsed['query'])) { $path .= '?'.$parsed['query']; } if (isset($parsed['fragment'])) { $path .= '#'.$parsed['fragment']; } } } if (! str_starts_with($path, '/')) { $path = '/'.$path; } if (str_starts_with($path, '/event-admin')) { return $hasScheme ? $target : $path; } return '/event-admin/dashboard'; } private function decodeBase64Url(string $value): ?string { if ($value === '') { return null; } $padded = str_pad($value, strlen($value) + ((4 - (strlen($value) % 4)) % 4), '='); $normalized = strtr($padded, '-_', '+/'); $decoded = base64_decode($normalized, true); if ($decoded === false) { return null; } return $decoded; } private function rememberTenantAdminTarget(Request $request, ?string $target): void { $user = Auth::user(); if (! $user || $user->role !== 'tenant_admin') { return; } if (! is_string($target) || $target === '') { return; } $normalized = $this->normalizeTenantAdminTarget($target, $request); if (! is_string($normalized) || $normalized === '') { return; } $path = $this->extractTenantAdminPath($normalized); if ($path === null) { return; } $request->session()->put('tenant_admin.return_to', $path); } private function extractTenantAdminPath(string $target): ?string { $value = trim($target); if ($value === '') { return null; } if (str_starts_with($value, '/event-admin')) { return $value; } $parsed = parse_url($value); if ($parsed === false) { return null; } $path = $parsed['path'] ?? ''; if ($path === '' || ! str_starts_with($path, '/event-admin')) { return null; } if (isset($parsed['query'])) { $path .= '?'.$parsed['query']; } if (isset($parsed['fragment'])) { $path .= '#'.$parsed['fragment']; } return $path; } }