fix: feat(auth): token flow end-to-end, DB allowlist, rolling expiration (#58)#190
Open
IliyaBrook wants to merge 6 commits intoRichardAtCT:mainfrom
Open
fix: feat(auth): token flow end-to-end, DB allowlist, rolling expiration (#58)#190IliyaBrook wants to merge 6 commits intoRichardAtCT:mainfrom
IliyaBrook wants to merge 6 commits intoRichardAtCT:mainfrom
Conversation
… command responses
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #58. The token auth infrastructure was fully implemented in
mainbut never wired end-to-end — three gaps blocked real use. This PR closes those gaps and adds a second admin flow (DB-backed allowlist) that fits Telegram UX better than the token dance alone. It also fixes a UX issue with token expiration semantics.What was added
1. Token flow (closes the three gaps from #58)
/auth <token>— users can finally present a token/auth <token>and passes it ascredentials={"token": ...}toauthenticate_user()(was always{}before)InMemoryTokenStoragereplaced withSqliteTokenStoragebacked by the existinguser_tokenstable — tokens now survive restarts2. New admin commands for token flow
/auth generate <user_id>/auth revoke <user_id>Admin responses:
/auth generate <user_id>:/auth revoke <user_id>(user has active token):/auth revoke <user_id>(no active token in DB):Previously this always said "Token revoked" even when nothing was revoked.
3. New DB-backed allowlist (no token dance required)
The token dance (generate → copy → paste) is the classic pattern for cross-channel auth (e.g., CLI admin + web user), but it's overkill inside Telegram where
user_idis already platform-verified. Added a simpler admin-managed allowlist using the already-existingusers.is_allowedcolumn — a user added this way can just write to the bot, no token required./auth add <user_id>/auth remove <user_id>Admin responses:
/auth add <user_id>:/auth remove <user_id>:/auth removefor a user not in the allowlist:Backed by a new
DatabaseAllowlistAuthProviderthat sits between the env-basedWhitelistAuthProvider(permanent admins) andTokenAuthProviderin the auth chain.4. Utility commands
/author/auth status— show the caller's own auth status/auth <garbage>from an admin now shows a help hint with the real subcommands instead of a misleading "Authenticated successfully"Rolling token expiration (UX fix)
The original behavior was overkill for Telegram
The inherited design used a fixed 30-day token expiration combined with a 24-hour in-memory session. That's a reasonable "defense in depth" for web/mobile apps, but it produced a real pain point inside Telegram:
/auth <token>— authenticated, session lasts 24h./auth <token>again, roughly once per day of inactivity.In Telegram this extra check buys nothing: every message already carries a
user_idthat the Telegram platform verifies. The token is a one-time proof that the admin approved thisuser_id— re-verifying it every 24h doesn't add any real security, it only annoys legitimate users.What changed
TokenAuthProvider.authenticate()now accepts empty credentials when the caller'suser_idhas a non-expired active token in the DB. The first/auth <token>is still required to establish the link; after that, the presence of an active stored token is enough. A wrong raw token is still rejected — the fallback only kicks in when no raw token is supplied.authenticate()succeeds,expires_atis pushed tonow + 30 daysandlast_usedis updated (thelast_usedcolumn was in the schema from day one but nothing ever wrote to it).Practical behavior
/auth <token>, then just keeps using the bot. The token silently refreshes whenever the 24h session lapses, so no more repeated paste-the-token dance./auth generate)./auth revoke <user_id>still works exactly as before — session ends, next message is rejected.Two ways to grant access (pick what fits)
Simpler — persistent allowlist (recommended for trusted users):
Token-based (for time-boxed / out-of-band distribution):