antiwork/gumroad · error · VerificationError

deleted_user

deleted_user

Error message

deleted_user

What it means

Raised in `Logins::PasskeysController#create` (logins/passkeys_controller.rb:24): the WebAuthn assertion fully verified (signature, challenge, sign count all valid) and resolved to a stored `WebauthnCredential`, but that credential's user is soft-deleted (`user.deleted?`). Gumroad keeps passkey rows after account deletion, so authentication is refused at the last step with the generic 422 error message.

Source

Thrown at app/controllers/logins/passkeys_controller.rb:24

  AUTHENTICATION_ERROR_MESSAGE = "We couldn't sign you in with that passkey. Please try again or use your password."

  skip_before_action :check_suspended
  skip_before_action :invalidate_session_if_necessary

  def options
    render json: { success: true, options: build_webauthn_authentication_options }
  end

  def create
    Rails.logger.info("passkey.authentication.started")

    challenge = session.delete(AUTHENTICATION_CHALLENGE_SESSION_KEY)
    raise VerificationError, "missing_challenge" if challenge.blank?

    stored_credential = verified_credential(challenge)

    user = stored_credential.user
    raise VerificationError, "deleted_user" if user.deleted?

    stored_credential.save!

    user.remember_me = true
    sign_in(user)
    reset_two_factor_auth_login_session
    merge_guest_cart_with_user_cart
    refresh_passkey_setup_prompt(user)

    Rails.logger.info("passkey.authentication.succeeded user_id=#{user.id} webauthn_credential_id=#{stored_credential.id}")

    render json: { success: true, redirect_location: login_path_for(user) }
  rescue VerificationError => e
    log_authentication_failure(e.reason)
    render json: { success: false, error_message: AUTHENTICATION_ERROR_MESSAGE }, status: :unprocessable_entity
  end

  private

View on GitHub (pinned to afeacbd394)

Solutions

  1. If the account should exist, restore/undelete the user (support action) — the same passkey will then work again.
  2. If the account is intentionally gone, sign in flow should guide to account recovery/creation rather than passkey retry; retrying cannot succeed.
  3. On account deletion flows, consider destroying webauthn_credentials so deleted users' passkeys never surface in browser autofill.
  4. Frontend: treat the 422 as terminal for this credential and fall back to password/other methods, not a retry.

Example fix

# before — deletion leaves the passkey behind, login later fails opaquely
user.destroy # soft delete; webauthn_credentials remain

# after — remove credentials when the account is deleted
user.destroy
user.webauthn_credentials.destroy_all
Defensive patterns

Strategy: try-catch

Try / catch

begin
  user = stored_credential.user
  raise VerificationError, "deleted_user" if user.deleted?
rescue VerificationError => e
  log_authentication_failure(e.reason)
  render json: { success: false, error_message: AUTHENTICATION_ERROR_MESSAGE }, status: :unprocessable_entity
end

Prevention

When it happens

Trigger: A user with a registered passkey deletes their Gumroad account and later tries passkey login; an admin deletes/deactivates the account while its passkey remains stored; the credential was registered to an account that was subsequently removed in moderation.

Common situations: Returning users after account deletion attempts; shared devices where an old account's passkey still surfaces in the browser's autofill; automated tests exercising deleted-user login paths.

Related errors


AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21). Data as JSON: /api/errors/48d74e942080f60b. Report an issue: GitHub.