[PR #106] [MERGED] fix: Restore soft-deleted users on re-authentication #259

Closed
opened 2026-02-15 17:16:32 -05:00 by yindo · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/openclaw/clawhub/pull/106
Author: @mkrokosz
Created: 2/2/2026
Status: Merged
Merged: 2/6/2026
Merged by: @steipete

Base: mainHead: fix/restore-soft-deleted-users-on-reauth


📝 Commits (3)

  • 13b3017 fix: Restore soft-deleted users on re-authentication (with ban check)
  • 4e3bd2b fix: Convert userId to string for targetId comparison
  • 87c05bb fix: restore soft-deleted users on reauth (#106) (thanks @mkrokosz)

📊 Changes

3 files changed (+114 additions, -0 deletions)

View changed files

📝 CHANGELOG.md (+1 -0)
convex/auth.test.ts (+69 -0)
📝 convex/auth.ts (+44 -0)

📄 Description

Summary

Users who deleted their ClawHub account cannot sign in again with the same GitHub account. This PR fixes that by restoring self-deleted accounts on re-authentication.

The Problem

When a user deletes their account:

  1. deletedAt timestamp is set (soft-delete)
  2. User record remains in database with GitHub ID

When they try to sign in again:

  1. Convex Auth finds existing user by GitHub ID
  2. Session is created
  3. But me query filters deleted users: if (!user || user.deletedAt) return null
  4. Frontend sees "not logged in", shows sign-in button
  5. User is stuck - can never log in, no error message

The Fix

Adds a createOrUpdateUser callback in convex/auth.ts that:

  1. Detects soft-deleted users during OAuth
  2. Checks audit logs to determine if user was banned vs self-deleted
  3. If banned → throws error "This account has been suspended"
  4. If self-deleted → clears deletedAt to restore account

Security Consideration

Both deleteAccount and banUser set the same deletedAt field. This fix ensures banned users cannot restore their accounts by re-authenticating - only self-deleted users can restore.

Performance

The callback runs on every OAuth sign-in, but:

User State Extra Queries Notes
New user 0 Returns null immediately
Active user 0 Single if check on already-loaded field
Soft-deleted user 1 Indexed query on auditLogs.by_target

For 99% of logins (active users), there is zero performance impact. The audit log query only runs for soft-deleted users attempting to sign in, and uses the existing by_target index.

Testing

  1. Create account → Delete account → Try to sign in again → Should restore and log in
  2. Create account → Get banned by moderator → Try to sign in → Should see "suspended" error
  3. Normal active user signs in → No change in behavior

🤖 Generated with Claude Code

Greptile Overview

Greptile Summary

This PR adds a callbacks.createOrUpdateUser hook in convex/auth.ts to restore soft-deleted user records when the same GitHub account re-authenticates. The callback:

  • Lets Convex Auth create brand-new users (returns null when there is no existingUserId).
  • For existing, active users, returns early without extra queries.
  • For soft-deleted users, checks the auditLogs.by_target index for a user.ban record and blocks login with a suspension error if present; otherwise it clears deletedAt and updates updatedAt to restore the account.

This aligns the auth flow with the existing me/requireUser behavior that filters out deletedAt users, while preserving bans by treating ban audit logs as the source of truth.

Confidence Score: 4/5

  • This PR looks safe to merge and addresses the soft-delete re-login bug with minimal behavioral surface area.
  • The change is localized to the Convex Auth callback, follows existing ban semantics (via user.ban audit logs), and avoids extra queries for normal logins. The main concern is a minor schema/type mismatch (auditLogs.targetId is string while code compares against an Id<'users'>), which could make the ban check brittle depending on how IDs are serialized in practice.
  • convex/auth.ts

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

Context used:

  • Context from dashboard - AGENTS.md (source)

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/openclaw/clawhub/pull/106 **Author:** [@mkrokosz](https://github.com/mkrokosz) **Created:** 2/2/2026 **Status:** ✅ Merged **Merged:** 2/6/2026 **Merged by:** [@steipete](https://github.com/steipete) **Base:** `main` ← **Head:** `fix/restore-soft-deleted-users-on-reauth` --- ### 📝 Commits (3) - [`13b3017`](https://github.com/openclaw/clawhub/commit/13b301786f737f309f8a261095154bdef1f6f845) fix: Restore soft-deleted users on re-authentication (with ban check) - [`4e3bd2b`](https://github.com/openclaw/clawhub/commit/4e3bd2b006f2b99a7177ed4d20f3af61cc8a44fa) fix: Convert userId to string for targetId comparison - [`87c05bb`](https://github.com/openclaw/clawhub/commit/87c05bba81b9e45b770d2ca1b7cede1096830b7a) fix: restore soft-deleted users on reauth (#106) (thanks @mkrokosz) ### 📊 Changes **3 files changed** (+114 additions, -0 deletions) <details> <summary>View changed files</summary> 📝 `CHANGELOG.md` (+1 -0) ➕ `convex/auth.test.ts` (+69 -0) 📝 `convex/auth.ts` (+44 -0) </details> ### 📄 Description ## Summary Users who deleted their ClawHub account cannot sign in again with the same GitHub account. This PR fixes that by restoring self-deleted accounts on re-authentication. ## The Problem When a user deletes their account: 1. `deletedAt` timestamp is set (soft-delete) 2. User record remains in database with GitHub ID When they try to sign in again: 1. Convex Auth finds existing user by GitHub ID 2. Session is created 3. But `me` query filters deleted users: `if (!user || user.deletedAt) return null` 4. Frontend sees "not logged in", shows sign-in button 5. User is stuck - can never log in, no error message ## The Fix Adds a `createOrUpdateUser` callback in `convex/auth.ts` that: 1. Detects soft-deleted users during OAuth 2. Checks audit logs to determine if user was **banned** vs **self-deleted** 3. If banned → throws error "This account has been suspended" 4. If self-deleted → clears `deletedAt` to restore account ## Security Consideration Both `deleteAccount` and `banUser` set the same `deletedAt` field. This fix ensures **banned users cannot restore their accounts** by re-authenticating - only self-deleted users can restore. ## Performance The callback runs on every OAuth sign-in, but: | User State | Extra Queries | Notes | |------------|---------------|-------| | New user | 0 | Returns `null` immediately | | Active user | 0 | Single `if` check on already-loaded field | | Soft-deleted user | 1 | Indexed query on `auditLogs.by_target` | **For 99% of logins (active users), there is zero performance impact.** The audit log query only runs for soft-deleted users attempting to sign in, and uses the existing `by_target` index. ## Testing 1. Create account → Delete account → Try to sign in again → Should restore and log in 2. Create account → Get banned by moderator → Try to sign in → Should see "suspended" error 3. Normal active user signs in → No change in behavior --- 🤖 Generated with [Claude Code](https://claude.ai/code) <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR adds a `callbacks.createOrUpdateUser` hook in `convex/auth.ts` to restore soft-deleted user records when the same GitHub account re-authenticates. The callback: - Lets Convex Auth create brand-new users (returns `null` when there is no `existingUserId`). - For existing, active users, returns early without extra queries. - For soft-deleted users, checks the `auditLogs.by_target` index for a `user.ban` record and blocks login with a suspension error if present; otherwise it clears `deletedAt` and updates `updatedAt` to restore the account. This aligns the auth flow with the existing `me`/`requireUser` behavior that filters out `deletedAt` users, while preserving bans by treating ban audit logs as the source of truth. <h3>Confidence Score: 4/5</h3> - This PR looks safe to merge and addresses the soft-delete re-login bug with minimal behavioral surface area. - The change is localized to the Convex Auth callback, follows existing ban semantics (via `user.ban` audit logs), and avoids extra queries for normal logins. The main concern is a minor schema/type mismatch (`auditLogs.targetId` is `string` while code compares against an `Id<'users'>`), which could make the ban check brittle depending on how IDs are serialized in practice. - convex/auth.ts <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> **Context used:** - Context from `dashboard` - AGENTS.md ([source](https://app.greptile.com/review/custom-context?memory=a1d58d20-b4dd-4cbb-973a-9fd7824e1921)) <!-- /greptile_comment --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
yindo added the pull-request label 2026-02-15 17:16:32 -05:00
yindo closed this issue 2026-02-15 17:16:32 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: openclaw/clawhub#259