TenantAtlas/app/Services/Auth/PostLoginRedirectResolver.php
ahmido c5fbcaa692 063-entra-signin (#76)
Key changes

Adds Entra OIDC redirect + callback endpoints under /auth/entra/* (token exchange only there).
Upserts tenant users keyed by (entra_tenant_id = tid, entra_object_id = oid); regenerates session; never stores tokens.
Blocks disabled / soft-deleted users with a generic error and safe logging.
Membership-based post-login routing:
0 memberships → /admin/no-access
1 membership → tenant dashboard (via Filament URL helpers)
>1 memberships → /admin/choose-tenant
Adds Filament pages:
/admin/choose-tenant (tenant selection + redirect)
/admin/no-access (tenantless-safe)
Both use simple layout to avoid tenant-required UI.
Guards / tests

Adds DbOnlyPagesDoNotMakeHttpRequestsTest to enforce DB-only render/hydration for:
/admin/login, /admin/no-access, /admin/choose-tenant
with Http::preventStrayRequests()
Adds session separation smoke coverage to ensure tenant session doesn’t access system and vice versa.
Runs: vendor/bin/sail artisan test --compact tests/Feature/Auth

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #76
2026-01-27 16:38:53 +00:00

50 lines
1.1 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services\Auth;
use App\Filament\Pages\TenantDashboard;
use App\Models\Tenant;
use App\Models\User;
use Illuminate\Support\Collection;
class PostLoginRedirectResolver
{
public function resolve(User $user): string
{
$tenants = $this->getActiveTenants($user);
if ($tenants->isEmpty()) {
return '/admin/no-access';
}
if ($tenants->count() === 1) {
/** @var Tenant $tenant */
$tenant = $tenants->first();
return TenantDashboard::getUrl(tenant: $tenant);
}
return '/admin/choose-tenant';
}
/**
* @return Collection<int, Tenant>
*/
private function getActiveTenants(User $user): Collection
{
if ($user->isPlatformSuperadmin()) {
return Tenant::query()
->where('status', 'active')
->orderBy('name')
->get();
}
return $user->tenants()
->where('status', 'active')
->orderBy('name')
->get();
}
}