diff --git a/src/langbot/pkg/api/http/service/user.py b/src/langbot/pkg/api/http/service/user.py index 773737694..d0edf2e9b 100644 --- a/src/langbot/pkg/api/http/service/user.py +++ b/src/langbot/pkg/api/http/service/user.py @@ -963,18 +963,14 @@ class UserService: return list(result.all()) async def get_passkey_by_credential_id(self, credential_id: str) -> passkey.PasskeyCredential | None: - statement = ( - sqlalchemy.select(passkey.PasskeyCredential) - .where(passkey.PasskeyCredential.credential_id == credential_id) + statement = sqlalchemy.select(passkey.PasskeyCredential).where( + passkey.PasskeyCredential.credential_id == credential_id ) async with self._session_factory()() as session: return await session.scalar(statement) async def get_passkey_by_uuid(self, passkey_uuid: str) -> passkey.PasskeyCredential | None: - statement = ( - sqlalchemy.select(passkey.PasskeyCredential) - .where(passkey.PasskeyCredential.uuid == passkey_uuid) - ) + statement = sqlalchemy.select(passkey.PasskeyCredential).where(passkey.PasskeyCredential.uuid == passkey_uuid) async with self._session_factory()() as session: return await session.scalar(statement) @@ -1000,8 +996,7 @@ class UserService: existing_passkeys = await self.get_user_passkeys(account_uuid) exclude_credentials = [ - PublicKeyCredentialDescriptor(id=base64url_to_bytes(pk.credential_id)) - for pk in existing_passkeys + PublicKeyCredentialDescriptor(id=base64url_to_bytes(pk.credential_id)) for pk in existing_passkeys ] options = webauthn.generate_registration_options( @@ -1051,7 +1046,7 @@ class UserService: credential_name = (name or '').strip() if not credential_name: - credential_name = f"Passkey ({datetime.datetime.now().strftime('%Y-%m-%d %H:%M')})" + credential_name = f'Passkey ({datetime.datetime.now().strftime("%Y-%m-%d %H:%M")})' record = passkey.PasskeyCredential( uuid=str(uuid.uuid4()), @@ -1092,8 +1087,7 @@ class UserService: user_passkeys = await self.get_user_passkeys(user_obj.uuid) if user_passkeys: allow_credentials = [ - PublicKeyCredentialDescriptor(id=base64url_to_bytes(pk.credential_id)) - for pk in user_passkeys + PublicKeyCredentialDescriptor(id=base64url_to_bytes(pk.credential_id)) for pk in user_passkeys ] options = webauthn.generate_authentication_options( diff --git a/src/langbot/pkg/entity/persistence/passkey.py b/src/langbot/pkg/entity/persistence/passkey.py index b0102c6c0..210e228cb 100644 --- a/src/langbot/pkg/entity/persistence/passkey.py +++ b/src/langbot/pkg/entity/persistence/passkey.py @@ -28,9 +28,7 @@ class PasskeyCredential(Base): aaguid = sqlalchemy.Column(sqlalchemy.String(64), nullable=True) transports = sqlalchemy.Column(sqlalchemy.String(255), nullable=True) backed_up = sqlalchemy.Column(sqlalchemy.Boolean, nullable=False, default=False) - created_at = sqlalchemy.Column( - sqlalchemy.DateTime, nullable=False, server_default=sqlalchemy.func.now() - ) + created_at = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, server_default=sqlalchemy.func.now()) last_used_at = sqlalchemy.Column(sqlalchemy.DateTime, nullable=True) __table_args__ = ( diff --git a/src/langbot/pkg/persistence/mgr.py b/src/langbot/pkg/persistence/mgr.py index 5d226675b..e80624afb 100644 --- a/src/langbot/pkg/persistence/mgr.py +++ b/src/langbot/pkg/persistence/mgr.py @@ -63,6 +63,7 @@ _ALEMBIC_TENANT_TABLES = { 'mcp_servers', 'model_providers', 'codex_credentials', + 'passkey_credentials', 'llm_models', 'embedding_models', 'rerank_models', diff --git a/tests/integration/api/test_user_passkey_api.py b/tests/integration/api/test_user_passkey_api.py index 59be08cfd..bc58f9f28 100644 --- a/tests/integration/api/test_user_passkey_api.py +++ b/tests/integration/api/test_user_passkey_api.py @@ -8,14 +8,10 @@ from unittest.mock import AsyncMock, Mock import pytest -from tests.integration.api.test_smoke import ( - fake_api_app, - mock_circular_import_chain, - quart_test_client, -) +pytest_plugins = ['tests.integration.api.test_smoke'] -pytestmark = [pytest.mark.integration, pytest.mark.usefixtures('mock_circular_import_chain')] +pytestmark = pytest.mark.integration class TestPasskeyPublicEndpoints: diff --git a/tests/integration/persistence/test_migrations_postgres.py b/tests/integration/persistence/test_migrations_postgres.py index 11af89c59..a8283b2ff 100644 --- a/tests/integration/persistence/test_migrations_postgres.py +++ b/tests/integration/persistence/test_migrations_postgres.py @@ -550,11 +550,13 @@ class TestPostgreSQLWorkspaceMigration: ) assert 'workspaces' not in tables_before_migration assert 'codex_credentials' not in tables_before_migration + assert 'passkey_credentials' not in tables_before_migration await manager._initialize_managed_schema() async with postgres_engine.connect() as conn: assert 'codex_credentials' in await conn.run_sync(lambda sync: sa.inspect(sync).get_table_names()) + assert 'passkey_credentials' in await conn.run_sync(lambda sync: sa.inspect(sync).get_table_names()) account = (await conn.execute(text('SELECT uuid, status, source FROM users'))).mappings().one() workspace = ( (await conn.execute(text('SELECT * FROM workspaces WHERE source = :source'), {'source': 'local'}))