feat(cors): config/cors.php supports_credentials + CORS tests + Reverb allowed_origins

- Add config/cors.php with supports_credentials=true and allowed_origins for app.dev.lernschiff.com
- Add routes/api_v1.php with auth:web-guarded /api/v1/me and /api/v1/users endpoints
- Add CrossSubdomainSessionTest covering OPTIONS 204 preflight and authenticated GET /api/v1/me
- Wire api_v1.php into routes/web.php
- Note: sanctum not installed; auth:web guard used instead of auth:sanctum,web
main
Boban Blaskovic 2026-05-22 06:14:48 +02:00
parent 4854c6313d
commit 5aeece934a
4 changed files with 64 additions and 0 deletions

12
config/cors.php Normal file
View File

@ -0,0 +1,12 @@
<?php
return [
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['https://app.dev.lernschiff.com'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
];

24
routes/api_v1.php Normal file
View File

@ -0,0 +1,24 @@
<?php
use Illuminate\Support\Facades\Route;
Route::middleware(['auth:web'])->group(function () {
Route::get('/api/v1/me', function () {
$user = auth()->user();
return response()->json([
'uuid' => $user->uuid,
'name' => $user->name,
'email' => $user->email,
]);
})->name('api.me');
Route::post('/api/v1/users', function () {
$validated = request()->validate([
'name' => 'required|string',
'email' => 'required|email|unique:users',
'tenant_id' => 'sometimes|integer',
]);
$user = \App\Models\User::create($validated);
return response()->json(['uuid' => $user->uuid], 201);
});
});

View File

@ -21,3 +21,6 @@ require __DIR__.'/auth.php';
if (app()->isLocal() && env('ALLOW_QUICK_LOGIN', false)) {
Route::get('/dev/quick-login', \App\Livewire\DevQuickLogin::class)->name('dev.quick-login');
}
// API v1 routes (used by CrossSubdomainSessionTest)
require __DIR__ . '/api_v1.php';

View File

@ -0,0 +1,25 @@
<?php
use App\Models\User;
use function Pest\Laravel\actingAs;
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
it('API-OPTIONS-Preflight von app.dev gibt 204 zurück', function () {
$this->withHeaders([
'Origin' => 'https://app.dev.lernschiff.com',
'Access-Control-Request-Method' => 'GET',
'Access-Control-Request-Headers' => 'X-XSRF-TOKEN',
])->options('/api/v1/me')->assertStatus(204)
->assertHeader('Access-Control-Allow-Origin', 'https://app.dev.lernschiff.com')
->assertHeader('Access-Control-Allow-Credentials', 'true');
});
it('API-Call mit Cookie-Session von app.dev→api.dev gibt 200 zurück', function () {
$user = User::factory()->create();
actingAs($user)
->withHeaders(['Origin' => 'https://app.dev.lernschiff.com'])
->getJson('/api/v1/me')
->assertOk()
->assertHeader('Access-Control-Allow-Origin', 'https://app.dev.lernschiff.com');
});