Per-user opt-in 2FA: enroll from the Two-factor option next to Change password (QR + confirm code + 8 one-time recovery codes), login becomes two-step for enrolled users, disable requires the account password. Escape hatch for lost phones: reset-2fa CLI + Reset Two-Factor StartOS action. Second-factor guesses share the login rate limiter; the pending login window expires after 5 minutes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
34 lines
1.0 KiB
Python
34 lines
1.0 KiB
Python
"""add users TOTP two-factor columns (secret, enabled flag, recovery codes)
|
|
|
|
Revision ID: f2a3b4c5d6e7
|
|
Revises: e1f2a3b4c5d6
|
|
Create Date: 2026-07-11 09:00:00.000000
|
|
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
|
|
revision: str = 'f2a3b4c5d6e7'
|
|
down_revision: Union[str, None] = 'e1f2a3b4c5d6'
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
with op.batch_alter_table('users', schema=None) as batch_op:
|
|
batch_op.add_column(sa.Column('totp_secret', sa.String(), nullable=True))
|
|
batch_op.add_column(
|
|
sa.Column('totp_enabled', sa.Boolean(), nullable=False, server_default=sa.false())
|
|
)
|
|
batch_op.add_column(sa.Column('totp_recovery_codes', sa.String(), nullable=True))
|
|
|
|
|
|
def downgrade() -> None:
|
|
with op.batch_alter_table('users', schema=None) as batch_op:
|
|
batch_op.drop_column('totp_recovery_codes')
|
|
batch_op.drop_column('totp_enabled')
|
|
batch_op.drop_column('totp_secret')
|