Skip to content

Fix authenticated relayer password verification - #631

Draft
ryux1 wants to merge 5 commits into
aio-libs:masterfrom
ryux1:fix/authenticated-relayer-salt
Draft

Fix authenticated relayer password verification#631
ryux1 wants to merge 5 commits into
aio-libs:masterfrom
ryux1:fix/authenticated-relayer-salt

Conversation

@ryux1

@ryux1 ryux1 commented Sep 6, 2026

Copy link
Copy Markdown

What do these changes do?

Store each user's PBKDF2 salt in the authenticated relayer example database and use that salt when verifying a login. The authenticator decodes SMTP login bytes before querying SQLite, compares password digests with hmac.compare_digest(), and rejects incompatible legacy databases with regeneration guidance.

The example now propagates startup errors, remains active until shutdown, and cleans up its controller after cancellation or partial startup failure. Regression tests cover successful and failed authentication, the legacy schema, controller lifetime, and startup cleanup.

Are there changes in behavior for the user?

Yes. Credentials generated by make_user_db.py can now authenticate successfully. Existing databases using the old schema must be regenerated because their salts were never stored and cannot be recovered.

Related issue number

Fixes #475

Checklist

  • I think the code is well written
  • Unit tests for the changes exist
  • Relevant test environments have been executed:
    • Linux, Python 3.13: full suite, 570 passed
    • Linux, Python 3.13: QA, 9 passed
    • Linux, Python 3.13: MyPy and flake8 passed
  • Documentation reflects the changes
  • Added a news entry to NEWS.rst

Drafted with OpenAI Codex; personal review by the contributor remains required before the PR is marked ready.

@greptile-apps

greptile-apps Bot commented Sep 6, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

Safe to merge: no blocking failure remains in the reviewed relayer authentication and startup paths.

No blocking failure remains.

T-Rex T-Rex Logs

What T-Rex did

  • Ran a focused Python reproduction that built a legacy userauth(username, hashpass) SQLite database and exercised the current Authenticator and amain paths; the legacy database was rejected during construction and startup and the controller was never started, then a salted-compatible database was used to accept correct credentials, reject incorrect credentials, and complete the controller lifecycle.
  • Ran a focused Python harness that exercised current amain with a controller whose start() opened a real local listener and then raised RuntimeError; the current path called stop() and closed the listener before propagating the exception, while the prior control-flow shape propagated the same exception without stopping.
  • Compared legacy and salted schemas and results: the legacy capture showed SCHEMA_COLUMNS=['username', 'hashpass'] and deterministic incompatibility errors with empty controller events, while the salted capture showed the required salt column, accepted correct credentials, rejected wrong credentials, and completed the controller lifecycle.
  • Captured pre- and post-capture behavior: the legacy shape propagated the RuntimeError with stop_called=False and an open listener, while the current amain path printed STOP: listener_fd=-1 and stop_called=True before propagating the same exception.
  • All related scripts and command outputs for legacy and current paths are provided as artifacts to enable independent verification of the described outcomes.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (4): Last reviewed commit: "Clean up relayer after startup failure" | Re-trigger Greptile

Comment thread examples/authenticated_relayer/server.py Outdated
@ryux1

ryux1 commented Sep 6, 2026

Copy link
Copy Markdown
Author

Addressed in b491d32. Since the legacy hashes were created with salts that were never persisted, they cannot be migrated or verified. Authenticator initialization now validates the required columns and fails with an instruction to recreate the database using make_user_db.py. A regression test covers the legacy two-column schema, and the full suite passes with 568 tests.

@ryux1

ryux1 commented Sep 6, 2026

Copy link
Copy Markdown
Author

Addressed in 7643a49. The example now owns the main coroutine with asyncio.run(), so authenticator startup errors propagate and terminate the process. On successful startup, amain() waits until cancellation and stops the controller in its finally block instead of stopping it immediately. The new lifecycle test verifies that the controller remains running and is stopped on cancellation; the full suite passes with 569 tests.

Comment thread examples/authenticated_relayer/server.py Outdated
@ryux1

ryux1 commented Sep 6, 2026

Copy link
Copy Markdown
Author

Addressed in d783988. Controller.start() is now inside the try/finally, so a partial startup failure still invokes stop() before the exception propagates. A regression test uses a controller whose start() raises and verifies cleanup runs. Focused tests, flake8, and MyPy pass; the full suite now passes with 570 tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

examples/authenticated_relayer/server.py uses a random salt value

1 participant