From 31d8b1e65bc9c9a4d21eb7538bf2e8d742384807 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Thu, 8 Oct 2026 15:13:39 -0400 Subject: [PATCH] DRY pass #3: unlinking a device by id or by its own token is one tail (#5373) revoke_self and revoke_device each loaded the caller's DeviceToken, deleted it, committed and audited DEVICE_UNLINKED; _unlink_device(which) is that, owner-scoped as before, with each route keeping only how it names the row. Co-Authored-By: Claude Opus 5.5 --- src/inkwell/auth.py | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/inkwell/auth.py b/src/inkwell/auth.py index e7bfc9b..789ec27 100644 --- a/src/inkwell/auth.py +++ b/src/inkwell/auth.py @@ -659,21 +659,9 @@ async def revoke_own_device(): # using" is meaningless rather than merely unauthorized. The web app # revokes by id. return json_error("no device token was presented", 400) - async with session_scope() as db: - row = await db.scalar( - select(DeviceToken).where( - DeviceToken.token_hash == hash_token(token), - # Owner-scoped like every other device route. The hash already pins - # a single row; the guarantee shouldn't rest on one column. - DeviceToken.user_id == g.user_id, - ) - ) - if row is None: - return not_found() - await db.delete(row) - await db.commit() - await audit.record(audit.DEVICE_UNLINKED, user_id=g.user_id, email=await _email_of(db), detail=row.name) - return jsonify({"ok": True}) + # Owner-scoped like every other device route. The hash already pins a single + # row; the guarantee shouldn't rest on one column. + return await _unlink_device(DeviceToken.token_hash == hash_token(token)) @bp.delete("/devices/") @@ -682,10 +670,13 @@ async def revoke_device(device_id: str): did = parse_uuid(device_id) if did is None: return not_found() + return await _unlink_device(DeviceToken.id == did) + + +async def _unlink_device(which): + """Delete the caller's device token matching `which`, and say so in the audit log.""" async with session_scope() as db: - row = await db.scalar( - select(DeviceToken).where(DeviceToken.id == did, DeviceToken.user_id == g.user_id) - ) + row = await db.scalar(select(DeviceToken).where(which, DeviceToken.user_id == g.user_id)) if row is None: return not_found() await db.delete(row)