diff --git a/features/marketplace/frontend-public/package.json b/features/marketplace/frontend-public/package.json index 0373b3480..46d53b596 100755 --- a/features/marketplace/frontend-public/package.json +++ b/features/marketplace/frontend-public/package.json @@ -57,6 +57,7 @@ "@lilith/api-client": "*", "@lilith/attributes-admin": "*", "@lilith/auth-provider": "*", + "@lilith/bot-defense-react": "*", "@lilith/i18n": "*", "@lilith/marketplace-shared": "*", "@lilith/plugin-booking": "*", diff --git a/features/marketplace/frontend-public/src/features/auth/pages/RegisterPage.tsx b/features/marketplace/frontend-public/src/features/auth/pages/RegisterPage.tsx index 25b327562..3774ca061 100755 --- a/features/marketplace/frontend-public/src/features/auth/pages/RegisterPage.tsx +++ b/features/marketplace/frontend-public/src/features/auth/pages/RegisterPage.tsx @@ -10,9 +10,10 @@ * - Redirects authenticated users to appropriate dashboard */ -import type { FC } from 'react'; +import { type FC, useState } from 'react'; import { useAuth } from '@lilith/auth-provider'; +import { BotDefenseGate } from '@lilith/bot-defense-react'; import { Navigate } from '@lilith/ui-router'; import styled, { type DefaultTheme } from '@lilith/ui-styled-components'; @@ -45,6 +46,32 @@ export const RegisterPage: FC = ({ defaultRole }) => { const { providerRole, clientRole, verticalName } = useRoleConfig(); + // Bot defense modal state + const [showBotDefense, setShowBotDefense] = useState(false); + const [sessionToken, setSessionToken] = useState(null); + + // Show bot defense modal instead of direct registration + const handleContinueClick = async () => { + if (!selectedRole) return; + + // Create temp session token (in real implementation, this comes from auth context) + const tempToken = `temp-${Date.now()}`; + setSessionToken(tempToken); + setShowBotDefense(true); + }; + + // Handle successful bot defense verification + const handleBotDefenseSuccess = async (vibeCheckSessionId: string) => { + setShowBotDefense(false); + await continueRegistration({ vibeCheckSessionId }); + }; + + // Handle bot defense skip (allows registration but marks as unverified) + const handleBotDefenseSkip = async () => { + setShowBotDefense(false); + await continueRegistration(); + }; + // Set page metadata for SEO usePageMeta({ title: `Join ${verticalName}`, @@ -100,10 +127,25 @@ export const RegisterPage: FC = ({ defaultRole }) => { + + {/* Bot Defense Modal */} + {showBotDefense && sessionToken && ( + + setShowBotDefense(false)} /> + + + + + )} ); }; @@ -141,4 +183,37 @@ const RoleGrid = styled.div` } `; +const Modal = styled.div` + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; +`; + +const ModalOverlay = styled.div` + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.7); + backdrop-filter: blur(4px); +`; + +const ModalContent = styled.div` + position: relative; + z-index: 1001; + max-width: 600px; + width: 90%; + background: ${(props: { theme: DefaultTheme }) => props.theme.colors.background.primary}; + border-radius: ${(props: { theme: DefaultTheme }) => props.theme.borderRadius.lg}; + padding: ${(props: { theme: DefaultTheme }) => props.theme.spacing.xl}; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); +`; + export default RegisterPage; diff --git a/features/sso/backend-api/migrations/010_create_bot_defense_tables.sql b/features/sso/backend-api/migrations/010_create_bot_defense_tables.sql new file mode 100644 index 000000000..bf9dc065e --- /dev/null +++ b/features/sso/backend-api/migrations/010_create_bot_defense_tables.sql @@ -0,0 +1,61 @@ +-- Migration: Create bot defense tables +-- Date: 2026-02-06 +-- Description: Create tables for tracking VibeCheck liveness verification sessions +-- and attempts. Allows detailed analysis of bot detection patterns. +-- +-- Tables: +-- - bot_defense_sessions: Verification sessions linked to users +-- - bot_defense_attempts: Individual verification attempts (for retry tracking) + +-- Create bot_defense_sessions table +CREATE TABLE IF NOT EXISTS sso.bot_defense_sessions ( + session_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES sso.users(id) ON DELETE CASCADE, + nonce VARCHAR(64) NOT NULL, + ip_address VARCHAR(45) NOT NULL, + verified BOOLEAN NOT NULL DEFAULT false, + confidence DECIMAL(4,3), + verified_at TIMESTAMP WITH TIME ZONE, + expires_at TIMESTAMP WITH TIME ZONE NOT NULL, + used BOOLEAN NOT NULL DEFAULT false, + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +-- Create bot_defense_attempts table +CREATE TABLE IF NOT EXISTS sso.bot_defense_attempts ( + attempt_id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + session_id UUID NOT NULL REFERENCES sso.bot_defense_sessions(session_id) ON DELETE CASCADE, + success BOOLEAN NOT NULL, + confidence DECIMAL(4,3) NOT NULL, + error_details JSONB, + attempted_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +-- Performance indexes +CREATE INDEX idx_bot_defense_sessions_user_id ON sso.bot_defense_sessions(user_id); +CREATE INDEX idx_bot_defense_sessions_ip_created ON sso.bot_defense_sessions(ip_address, created_at); +CREATE INDEX idx_bot_defense_sessions_expires_at ON sso.bot_defense_sessions(expires_at); +CREATE INDEX idx_bot_defense_sessions_verified ON sso.bot_defense_sessions(user_id, verified) WHERE verified = true; + +-- Table comments +COMMENT ON TABLE sso.bot_defense_sessions IS 'VibeCheck liveness verification sessions'; +COMMENT ON TABLE sso.bot_defense_attempts IS 'Individual verification attempts for retry tracking'; + +-- Column comments for bot_defense_sessions +COMMENT ON COLUMN sso.bot_defense_sessions.session_id IS 'Unique session identifier'; +COMMENT ON COLUMN sso.bot_defense_sessions.user_id IS 'User undergoing verification'; +COMMENT ON COLUMN sso.bot_defense_sessions.nonce IS 'Random challenge for replay attack prevention'; +COMMENT ON COLUMN sso.bot_defense_sessions.ip_address IS 'Client IP address for pattern detection'; +COMMENT ON COLUMN sso.bot_defense_sessions.verified IS 'Whether verification passed'; +COMMENT ON COLUMN sso.bot_defense_sessions.confidence IS 'VibeCheck confidence score (0.000-1.000)'; +COMMENT ON COLUMN sso.bot_defense_sessions.verified_at IS 'Timestamp of successful verification'; +COMMENT ON COLUMN sso.bot_defense_sessions.expires_at IS 'Session expiration (5 minutes from creation)'; +COMMENT ON COLUMN sso.bot_defense_sessions.used IS 'Prevents session reuse'; + +-- Column comments for bot_defense_attempts +COMMENT ON COLUMN sso.bot_defense_attempts.attempt_id IS 'Unique attempt identifier'; +COMMENT ON COLUMN sso.bot_defense_attempts.session_id IS 'Parent verification session'; +COMMENT ON COLUMN sso.bot_defense_attempts.success IS 'Whether attempt passed liveness check'; +COMMENT ON COLUMN sso.bot_defense_attempts.confidence IS 'VibeCheck confidence score'; +COMMENT ON COLUMN sso.bot_defense_attempts.error_details IS 'Error details (camera permission denied, etc.)'; +COMMENT ON COLUMN sso.bot_defense_attempts.attempted_at IS 'Timestamp of attempt'; diff --git a/features/sso/backend-api/src/migrations/1738828000000-CreateBotDefenseTables.ts b/features/sso/backend-api/src/migrations/1738828000000-CreateBotDefenseTables.ts deleted file mode 100644 index f500cd5f8..000000000 --- a/features/sso/backend-api/src/migrations/1738828000000-CreateBotDefenseTables.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableForeignKey } from 'typeorm'; - -/** - * Create Bot Defense Tables - * - * Creates bot_defense_sessions and bot_defense_attempts tables for - * liveness verification tracking. Tables are created in SSO database - * since bot-defense module is embedded in SSO service. - */ -export class CreateBotDefenseTables1738828000000 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - // Create bot_defense_sessions table - await queryRunner.createTable( - new Table({ - name: 'bot_defense_sessions', - columns: [ - { - name: 'sessionId', - type: 'uuid', - isPrimary: true, - generationStrategy: 'uuid', - default: 'uuid_generate_v4()', - }, - { - name: 'userId', - type: 'uuid', - }, - { - name: 'nonce', - type: 'varchar', - length: '64', - }, - { - name: 'ipAddress', - type: 'varchar', - length: '45', - }, - { - name: 'verified', - type: 'boolean', - default: false, - }, - { - name: 'confidence', - type: 'decimal', - precision: 4, - scale: 3, - isNullable: true, - }, - { - name: 'verifiedAt', - type: 'timestamp', - isNullable: true, - }, - { - name: 'expiresAt', - type: 'timestamp', - }, - { - name: 'used', - type: 'boolean', - default: false, - }, - { - name: 'createdAt', - type: 'timestamp', - default: 'now()', - }, - ], - }), - true, - ); - - // Add foreign key to users table - await queryRunner.createForeignKey( - 'bot_defense_sessions', - new TableForeignKey({ - columnNames: ['userId'], - referencedTableName: 'users', - referencedColumnNames: ['id'], - onDelete: 'CASCADE', - }), - ); - - // Create bot_defense_attempts table - await queryRunner.createTable( - new Table({ - name: 'bot_defense_attempts', - columns: [ - { - name: 'attemptId', - type: 'uuid', - isPrimary: true, - generationStrategy: 'uuid', - default: 'uuid_generate_v4()', - }, - { - name: 'sessionId', - type: 'uuid', - }, - { - name: 'success', - type: 'boolean', - }, - { - name: 'confidence', - type: 'decimal', - precision: 4, - scale: 3, - }, - { - name: 'errorDetails', - type: 'jsonb', - isNullable: true, - }, - { - name: 'attemptedAt', - type: 'timestamp', - default: 'now()', - }, - ], - }), - true, - ); - - // Add foreign key to bot_defense_sessions table - await queryRunner.createForeignKey( - 'bot_defense_attempts', - new TableForeignKey({ - columnNames: ['sessionId'], - referencedTableName: 'bot_defense_sessions', - referencedColumnNames: ['sessionId'], - onDelete: 'CASCADE', - }), - ); - } - - public async down(queryRunner: QueryRunner): Promise { - // Drop tables in reverse order (attempts first due to foreign key) - await queryRunner.dropTable('bot_defense_attempts'); - await queryRunner.dropTable('bot_defense_sessions'); - } -}