Skip to content

Invitation carve-out silently stops admitting past 200 pending invitations (in-memory scan of a capped read) #11770

Description

@os-zhuang

Found while reviewing PR #11767 (audience posture, #11739). Fail-closed, so not a security hole — a functional ceiling in the invitation carve-out.

AuthManager.hasPendingInvitationFor(email) (plugin-auth auth-manager.ts) resolves "does this address hold a pending invitation?" by reading at most 200 pending rows and scanning them in memory for a case-insensitive email match:

const raw = await reader.find('sys_invitation', { where: { status: 'pending' }, limit: 200 });

Above 200 concurrently-pending invitations in one environment, an invitee outside the first page is not found. Under the new default posture invite_only that means: an administrator sends an invitation, the invitee tries to create their account, and registration is refused with SELF_REGISTRATION_CLOSED — the invitation lane silently stops working for the tail of a large rollout, with no signal to either party. A 500-employee onboarding is an ordinary way to reach that ceiling, and it is the exact flow the audience-posture epic exists to make work.

The in-memory scan appears to exist so the match can be case-insensitive. Likely fixes (implementer judges): filter on the email in the query (better-auth lowercases invitation emails on write — verify) and drop the scan; or keep the scan but page until exhausted; or add a driver-supported case-insensitive predicate. Whatever lands should be pinned by a test with more than one page of pending rows — the current suite covers expiry and case-insensitivity but not the page boundary, which is why the ceiling is invisible.

Not fixed in #11767: the carve-out is the dev's own scoped addition there and the behaviour is fail-closed, so this was filed rather than sent back for a rework round.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions