id(); $table->string('name', 100)->unique(); $table->string('display_name', 120); $table->string('description', 255)->nullable(); $table->json('claims')->nullable(); $table->boolean('is_active')->default(true); $table->timestamps(); $table->index(['is_active', 'name']); }); Schema::create('oauth_clients', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('logo_url')->nullable(); $table->string('client_id', 80)->unique(); $table->string('client_secret_hash')->nullable(); $table->json('redirect_uris')->nullable(); $table->json('allowed_userinfo_fields')->nullable(); $table->json('userinfo_claim_remap')->nullable(); $table->boolean('is_confidential')->default(true); $table->boolean('is_active')->default(true); $table->timestamps(); $table->index(['is_active', 'client_id']); }); Schema::create('oauth_client_scope', function (Blueprint $table) { $table->id(); $table->foreignId('client_id')->constrained('oauth_clients')->cascadeOnDelete(); $table->foreignId('scope_id')->constrained('oauth_scopes')->cascadeOnDelete(); $table->timestamps(); $table->unique(['client_id', 'scope_id'], 'uq_oauth_client_scope'); }); Schema::create('oauth_consents', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained('users')->cascadeOnDelete(); $table->foreignId('client_id')->constrained('oauth_clients')->cascadeOnDelete(); $table->string('scope_fingerprint', 64); $table->json('scopes'); $table->timestamp('granted_at'); $table->timestamps(); $table->unique(['user_id', 'client_id', 'scope_fingerprint'], 'uq_oauth_consents_fingerprint'); $table->index(['user_id', 'client_id']); }); Schema::create('oauth_authorizations', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained('users')->cascadeOnDelete(); $table->foreignId('client_id')->constrained('oauth_clients')->cascadeOnDelete(); $table->string('scope', 1000); $table->string('scope_fingerprint', 64); $table->timestamp('revoked_at')->nullable(); $table->timestamps(); $table->index(['user_id', 'client_id']); }); Schema::create('oauth_authorization_codes', function (Blueprint $table) { $table->id(); $table->string('code_hash', 64)->unique(); $table->foreignId('authorization_id')->constrained('oauth_authorizations')->cascadeOnDelete(); $table->foreignId('user_id')->constrained('users')->cascadeOnDelete(); $table->foreignId('client_id')->constrained('oauth_clients')->cascadeOnDelete(); $table->text('redirect_uri'); $table->string('scope', 1000); $table->string('nonce')->nullable(); $table->timestamp('expires_at'); $table->timestamp('consumed_at')->nullable(); $table->timestamp('revoked_at')->nullable(); $table->timestamps(); $table->index(['client_id', 'expires_at']); }); Schema::create('oauth_access_tokens', function (Blueprint $table) { $table->id(); $table->string('jti', 64)->unique(); $table->foreignId('authorization_id')->constrained('oauth_authorizations')->cascadeOnDelete(); $table->foreignId('user_id')->constrained('users')->cascadeOnDelete(); $table->foreignId('client_id')->constrained('oauth_clients')->cascadeOnDelete(); $table->string('scope', 1000); $table->timestamp('expires_at'); $table->timestamp('revoked_at')->nullable(); $table->timestamps(); $table->index(['client_id', 'user_id']); $table->index(['expires_at', 'revoked_at']); }); Schema::create('oauth_refresh_tokens', function (Blueprint $table) { $table->id(); $table->string('jti', 64)->unique(); $table->string('token_hash', 64)->unique(); $table->foreignId('authorization_id')->constrained('oauth_authorizations')->cascadeOnDelete(); $table->foreignId('user_id')->constrained('users')->cascadeOnDelete(); $table->foreignId('client_id')->constrained('oauth_clients')->cascadeOnDelete(); $table->foreignId('previous_refresh_token_id')->nullable()->constrained('oauth_refresh_tokens')->nullOnDelete(); $table->foreignId('replaced_by_refresh_token_id')->nullable()->constrained('oauth_refresh_tokens')->nullOnDelete(); $table->timestamp('expires_at'); $table->timestamp('used_at')->nullable(); $table->timestamp('replayed_at')->nullable(); $table->timestamp('revoked_at')->nullable(); $table->timestamps(); $table->index(['authorization_id', 'expires_at']); }); DB::table('oauth_scopes')->insert([ [ 'name' => 'openid', 'display_name' => 'OpenID', 'description' => '启用 OIDC 身份声明', 'claims' => json_encode([], JSON_UNESCAPED_UNICODE), 'is_active' => true, 'created_at' => now(), 'updated_at' => now(), ], [ 'name' => 'profile', 'display_name' => 'Profile', 'description' => '基础用户资料', 'claims' => json_encode(['nickname'], JSON_UNESCAPED_UNICODE), 'is_active' => true, 'created_at' => now(), 'updated_at' => now(), ], [ 'name' => 'email', 'display_name' => 'Email', 'description' => '邮箱信息', 'claims' => json_encode(['email'], JSON_UNESCAPED_UNICODE), 'is_active' => true, 'created_at' => now(), 'updated_at' => now(), ], [ 'name' => 'phone', 'display_name' => 'Phone', 'description' => '手机号信息', 'claims' => json_encode(['phone'], JSON_UNESCAPED_UNICODE), 'is_active' => true, 'created_at' => now(), 'updated_at' => now(), ], ]); } public function down(): void { Schema::dropIfExists('oauth_refresh_tokens'); Schema::dropIfExists('oauth_access_tokens'); Schema::dropIfExists('oauth_authorization_codes'); Schema::dropIfExists('oauth_authorizations'); Schema::dropIfExists('oauth_consents'); Schema::dropIfExists('oauth_client_scope'); Schema::dropIfExists('oauth_clients'); Schema::dropIfExists('oauth_scopes'); } };