Files
fotospiel-app/app/Http/Controllers/Auth/AuthenticatedSessionController.php

298 lines
7.8 KiB
PHP

<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Http\Requests\Auth\LoginRequest;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
use Inertia\Inertia;
use Inertia\Response;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
class AuthenticatedSessionController extends Controller
{
/**
* Show the login page.
*/
public function create(Request $request): Response
{
return Inertia::render('auth/login', [
'canResetPassword' => 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->role === 'super_admin') {
return '/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') || str_starts_with($path, '/api/v1/oauth/authorize')) {
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;
}
}