Files
crewli/api/app/Http/Controllers/Api/V1/Traits/SetAuthCookie.php
bert.hausmans 28727f246b chore: remove admin SPA and update to two-app production setup
Remove apps/admin/ entirely — platform admin functionality now lives
in apps/app/ under /platform/* routes for super_admin users.

Production URL scheme changed:
- Organizer app: crewli.app (was app.crewli.app)
- Portal: portal.crewli.app (unchanged)
- API: api.crewli.app (unchanged)
- admin.crewli.app and app.crewli.app retired

Backend:
- Removed FRONTEND_ADMIN_URL config and admin cookie (crewli_admin_token)
  from SetAuthCookie, CookieBearerToken, cors.php, app.php
- Updated .env and .env.example (two origins, no port 5173)
- Updated cookie test: admin origin test → unknown origin fallback test

Infrastructure:
- Makefile: removed admin target
- deploy/nginx: updated CSP comment, removed admin vhost
- Updated README.md, CLAUDE.md, and all dev-docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 08:44:10 +02:00

82 lines
2.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Http\Controllers\Api\V1\Traits;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Cookie;
trait SetAuthCookie
{
private const COOKIE_MAP = [
'app' => 'crewli_app_token',
'portal' => 'crewli_portal_token',
];
private const COOKIE_TTL_MINUTES = 60 * 24 * 7; // 7 days
protected function resolveCookieName(Request $request): string
{
$origin = $request->headers->get('Origin')
?? $request->headers->get('Referer')
?? '';
$appUrl = config('app.frontend_app_url', 'http://localhost:5174');
$portalUrl = config('app.frontend_portal_url', 'http://localhost:5175');
if ($this->originMatches($origin, $appUrl)) {
return self::COOKIE_MAP['app'];
}
if ($this->originMatches($origin, $portalUrl)) {
return self::COOKIE_MAP['portal'];
}
return self::COOKIE_MAP['app'];
}
protected function makeAuthCookie(string $cookieName, string $token): Cookie
{
return new Cookie(
name: $cookieName,
value: $token,
expire: now()->addMinutes(self::COOKIE_TTL_MINUTES),
path: '/',
domain: config('session.domain'),
secure: config('app.env') === 'production',
httpOnly: true,
sameSite: 'Strict',
);
}
protected function forgetAuthCookie(string $cookieName): Cookie
{
return new Cookie(
name: $cookieName,
value: '',
expire: now()->subMinute(),
path: '/',
domain: config('session.domain'),
secure: config('app.env') === 'production',
httpOnly: true,
sameSite: 'Strict',
);
}
private function originMatches(string $origin, string $configuredUrl): bool
{
if ($origin === '' || $configuredUrl === '') {
return false;
}
// Parse to compare host+port, ignoring trailing slashes and paths
$originHost = parse_url($origin, PHP_URL_HOST);
$originPort = parse_url($origin, PHP_URL_PORT);
$configHost = parse_url($configuredUrl, PHP_URL_HOST);
$configPort = parse_url($configuredUrl, PHP_URL_PORT);
return $originHost === $configHost && $originPort === $configPort;
}
}