string('id', 255)->primary(); $table->string('client_id', 255)->unique(); $table->string('client_secret', 255)->nullable(); $table->text('redirect_uris')->nullable(); $table->text('scopes')->default('tenant:read tenant:write'); $table->boolean('is_active')->default(true); // From add_is_active $table->foreignId('tenant_id')->nullable()->after('client_secret')->constrained('tenants')->nullOnDelete(); // From add_tenant_id $table->timestamp('created_at')->useCurrent(); $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); $table->index('tenant_id'); }); } else { if (!Schema::hasColumn('oauth_clients', 'is_active')) { Schema::table('oauth_clients', function (Blueprint $table) { $table->boolean('is_active')->default(true)->after('scopes'); }); } if (!Schema::hasColumn('oauth_clients', 'tenant_id')) { Schema::table('oauth_clients', function (Blueprint $table) { $table->foreignId('tenant_id')->nullable()->after('client_secret')->constrained('tenants')->nullOnDelete(); $table->index('tenant_id'); }); } } // Refresh Tokens if (!Schema::hasTable('refresh_tokens')) { Schema::create('refresh_tokens', function (Blueprint $table) { $table->string('id', 255)->primary(); $table->string('tenant_id', 255)->index(); $table->string('client_id', 255)->nullable()->index(); // From add_client_id $table->string('token', 255)->unique()->index(); $table->string('access_token', 255)->nullable(); $table->timestamp('expires_at')->nullable(); $table->text('scope')->nullable(); $table->string('ip_address', 45)->nullable(); $table->text('user_agent')->nullable(); $table->timestamp('created_at')->useCurrent(); $table->timestamp('revoked_at')->nullable(); $table->index('expires_at'); }); } else { if (!Schema::hasColumn('refresh_tokens', 'client_id')) { Schema::table('refresh_tokens', function (Blueprint $table) { $table->string('client_id', 255)->nullable()->after('tenant_id')->index(); }); } } // Tenant Tokens if (!Schema::hasTable('tenant_tokens')) { Schema::create('tenant_tokens', function (Blueprint $table) { $table->string('id', 255)->primary(); $table->string('tenant_id', 255)->index(); $table->string('jti', 255)->unique()->index(); $table->string('token_type', 50)->index(); $table->timestamp('expires_at'); $table->timestamp('revoked_at')->nullable(); $table->timestamp('created_at')->useCurrent(); $table->index('expires_at'); }); } // OAuth Codes if (!Schema::hasTable('oauth_codes')) { Schema::create('oauth_codes', function (Blueprint $table) { $table->string('id', 255)->primary(); $table->string('client_id', 255); $table->string('user_id', 255); $table->string('code', 255)->unique()->index(); $table->string('code_challenge', 255); $table->string('state', 255)->nullable(); $table->string('redirect_uri', 255)->nullable(); $table->text('scope')->nullable(); $table->timestamp('expires_at'); $table->timestamp('created_at')->useCurrent(); $table->index('expires_at'); $table->foreign('client_id')->references('client_id')->on('oauth_clients')->onDelete('cascade'); }); } } public function down(): void { if (app()->environment('local', 'testing')) { if (Schema::hasTable('oauth_codes')) { Schema::table('oauth_codes', function (Blueprint $table) { $table->dropForeign(['client_id']); }); Schema::dropIfExists('oauth_codes'); } if (Schema::hasColumn('refresh_tokens', 'client_id')) { Schema::table('refresh_tokens', function (Blueprint $table) { $table->dropIndex(['client_id']); $table->dropColumn('client_id'); }); } Schema::dropIfExists('refresh_tokens'); Schema::dropIfExists('tenant_tokens'); if (Schema::hasColumn('oauth_clients', 'tenant_id')) { Schema::table('oauth_clients', function (Blueprint $table) { $table->dropForeign(['tenant_id']); $table->dropColumn('tenant_id'); }); } if (Schema::hasColumn('oauth_clients', 'is_active')) { Schema::table('oauth_clients', function (Blueprint $table) { $table->dropColumn('is_active'); }); } Schema::dropIfExists('oauth_clients'); } } };