stage 2 of oauth removal, switch to sanctum pat tokens completed, docs updated

This commit is contained in:
Codex Agent
2025-11-07 07:46:53 +01:00
parent 776da57ca9
commit 67affd3317
41 changed files with 124 additions and 2148 deletions

View File

@@ -1,34 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('refresh_token_audits', function (Blueprint $table) {
$table->id();
$table->string('refresh_token_id');
$table->string('tenant_id')->index();
$table->string('client_id')->nullable()->index();
$table->string('event', 64)->index();
$table->json('context')->nullable();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->foreignId('performed_by')->nullable()->constrained('users')->nullOnDelete();
$table->timestamp('created_at')->useCurrent();
$table->foreign('refresh_token_id')
->references('id')
->on('refresh_tokens')
->cascadeOnDelete();
});
}
public function down(): void
{
Schema::dropIfExists('refresh_token_audits');
}
};

View File

@@ -1,34 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('refresh_tokens', function (Blueprint $table) {
if (! Schema::hasColumn('refresh_tokens', 'last_used_at')) {
$table->timestamp('last_used_at')->nullable()->after('expires_at');
}
if (! Schema::hasColumn('refresh_tokens', 'revoked_reason')) {
$table->string('revoked_reason', 64)->nullable()->after('revoked_at');
}
});
}
public function down(): void
{
Schema::table('refresh_tokens', function (Blueprint $table) {
if (Schema::hasColumn('refresh_tokens', 'last_used_at')) {
$table->dropColumn('last_used_at');
}
if (Schema::hasColumn('refresh_tokens', 'revoked_reason')) {
$table->dropColumn('revoked_reason');
}
});
}
};

View File

@@ -33,7 +33,6 @@ class DatabaseSeeder extends Seeder
SuperAdminSeeder::class,
DemoTenantSeeder::class,
DemoEventSeeder::class,
OAuthClientSeeder::class,
]);
if (app()->environment(['local', 'development', 'demo'])) {

View File

@@ -1,53 +0,0 @@
<?php
namespace Database\Seeders;
use App\Models\OAuthClient;
use App\Models\Tenant;
use Illuminate\Database\Seeder;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
class OAuthClientSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$serviceConfig = config('services.oauth.tenant_admin', []);
$clientId = $serviceConfig['id'] ?? 'tenant-admin-app';
$tenantId = Tenant::where('slug', 'demo-tenant')->value('id')
?? Tenant::query()->orderBy('id')->value('id');
$redirectUris = Arr::wrap($serviceConfig['redirects'] ?? []);
if (empty($redirectUris)) {
$redirectUris = [
'http://localhost:5173/event-admin/auth/callback',
'http://localhost:8000/event-admin/auth/callback',
];
}
$scopes = [
'tenant:read',
'tenant:write',
];
$client = OAuthClient::firstOrNew(['client_id' => $clientId]);
if (!$client->exists) {
$client->id = (string) Str::uuid();
}
$client->fill([
'client_secret' => null, // Public client, no secret needed for PKCE
'tenant_id' => $tenantId,
'redirect_uris' => $redirectUris,
'scopes' => $scopes,
'is_active' => true,
]);
$client->save();
}
}

View File

@@ -5,7 +5,6 @@ namespace Database\Seeders;
use App\Models\Event;
use App\Models\EventPackage;
use App\Models\EventType;
use App\Models\OAuthClient;
use App\Models\Package;
use App\Models\PackagePurchase;
use App\Models\Tenant;
@@ -125,7 +124,6 @@ class DemoLifecycleSeeder extends Seeder
]);
$this->createTenantAdmin($tenant, 'storycraft-owner@demo.fotospiel');
$this->ensureOAuthClientForTenant($tenant, 'demo-tenant-admin-storycraft');
}
private function seedActiveTenant(Package $standard, Package $premium, EventType $weddingType, EventType $corporateType): void
@@ -139,12 +137,7 @@ class DemoLifecycleSeeder extends Seeder
'is_active' => true,
]);
OAuthClient::query()
->where('client_id', config('services.oauth.tenant_admin.id', 'tenant-admin-app'))
->update(['tenant_id' => $tenant->id]);
$this->createTenantAdmin($tenant, 'hello@lumen-moments.demo');
$this->ensureOAuthClientForTenant($tenant, 'demo-tenant-admin-lumen');
$purchase = PackagePurchase::create([
'tenant_id' => $tenant->id,
@@ -210,7 +203,6 @@ class DemoLifecycleSeeder extends Seeder
]);
$this->createTenantAdmin($tenant, 'team@viewfinder.demo');
$this->ensureOAuthClientForTenant($tenant, 'demo-tenant-admin-viewfinder');
$tenantPackage = TenantPackage::create([
'tenant_id' => $tenant->id,
@@ -280,7 +272,6 @@ class DemoLifecycleSeeder extends Seeder
]);
$this->createTenantAdmin($tenant, 'support@pixelco.demo', role: 'member');
$this->ensureOAuthClientForTenant($tenant, 'demo-tenant-admin-pixel');
}
private function createTenantAdmin(Tenant $tenant, string $email, string $role = 'tenant_admin'): User
@@ -379,30 +370,4 @@ class DemoLifecycleSeeder extends Seeder
];
}
private function ensureOAuthClientForTenant(Tenant $tenant, string $clientId): void
{
$redirectUris = config('services.oauth.tenant_admin.redirects', []);
if (empty($redirectUris)) {
$redirectUris = [
'http://localhost:5173/event-admin/auth/callback',
url('/event-admin/auth/callback'),
];
}
$client = OAuthClient::firstOrNew(['client_id' => $clientId]);
if (! $client->exists) {
$client->id = (string) Str::uuid();
}
$client->fill([
'client_secret' => null,
'tenant_id' => $tenant->id,
'redirect_uris' => $redirectUris,
'scopes' => ['tenant:read', 'tenant:write'],
'is_active' => true,
]);
$client->save();
}
}