"""add lockout columns and mfa_backup_codes to users Revision ID: d4e5f6a7b8c9 Revises: c3d4e5f6a7b8 Create Date: 2026-05-02 00:00:00.000000 Adds three nullable/defaulted columns to users: - failed_login_count INTEGER NOT NULL DEFAULT 0 Incremented on every failed login, reset on success or lockout expiry. - locked_until DATETIME NULL When set (and in the future), login is rejected with HTTP 429. - mfa_backup_codes TEXT NULL JSON array of Argon2id-hashed one-time backup codes generated at MFA enrollment. NULL = no codes generated / MFA not enabled. Cleared when MFA is disabled. """ from alembic import op import sqlalchemy as sa revision = 'd4e5f6a7b8c9' down_revision = 'c3d4e5f6a7b8' branch_labels = None depends_on = None def upgrade(): with op.batch_alter_table('users', schema=None) as batch_op: batch_op.add_column( sa.Column( 'failed_login_count', sa.Integer(), nullable=False, server_default='0', ) ) batch_op.add_column( sa.Column('locked_until', sa.DateTime(), nullable=True) ) batch_op.add_column( sa.Column('mfa_backup_codes', sa.Text(), nullable=True) ) def downgrade(): with op.batch_alter_table('users', schema=None) as batch_op: batch_op.drop_column('mfa_backup_codes') batch_op.drop_column('locked_until') batch_op.drop_column('failed_login_count')