# Team invitation flow is clearly presented

- **Pattern:** `ab-002366` (`saas-onboarding.onboarding-ux.invitation-flow-clear`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002366
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002366)

## Why it matters

A buried or broken invitation flow directly limits expansion revenue in any multi-user SaaS. If an invited user clicks an email link and sees a 'User not found' error instead of a signup prompt, that seat is lost — and the inviter loses confidence in the product. CWE-284 (improper access control) applies when the invite acceptance route fails to redirect non-users to registration, effectively creating a dead end that prevents legitimate account creation through a valid access path.

## Severity rationale

Medium because a broken invite acceptance flow causes direct seat loss and inviter trust damage, but doesn't create unauthorized access — the failure is exclusion, not intrusion.

## Remediation

In the invite acceptance handler at `src/app/invite/[token]/page.tsx`, redirect non-users to signup with the invite context preserved:

```tsx
const invite = await getInviteByToken(token);
if (!invite || invite.expiresAt < new Date()) notFound();

const user = await getUserByEmail(invite.email);
if (!user) {
  redirect(`/signup?email=${encodeURIComponent(invite.email)}&invite=${token}`);
}

await acceptInvite(invite.id, user.id);
redirect('/dashboard');
```

Also add an 'Invite teammates' entry to `src/components/Sidebar.tsx` reachable within 2 clicks of the dashboard — users shouldn't have to dig through settings to find it.

## Detection

- **ID:** `invitation-flow-clear`
- **Severity:** `medium`
- **What to look for:** Count the clicks required to reach the invitation UI from the main dashboard. Enumerate the invitation flow: invite by email, invite link generation, role assignment. Check whether the invitation acceptance flow handles non-users.
- **Pass criteria:** An invitation UI exists and is accessible within no more than 2 clicks from the main dashboard. Invitation emails include the inviter's name and the application name. Invitees without accounts are directed to create one before accepting.
- **Fail criteria:** Invitation UI requires more than 2 clicks to reach from the main dashboard; invitation links expire without warning; invitees who click an invitation link and don't have an account see an error rather than a signup prompt.
- **Skip (N/A) when:** The application is explicitly single-user only (no team, workspace, or multi-user concepts exist in the codebase — no invitation routes, no team/workspace models, no role assignment).
- **Detail on fail:** `"Invitation feature exists but is nested 3+ clicks deep in settings. Invitation link acceptance for non-users shows a 'User not found' error instead of redirecting to signup."`
- **Remediation:** In the invitation acceptance handler at `src/app/invite/[token]/page.tsx`:

  ```tsx
  const invite = await getInviteByToken(token);
  const user = await getUserByEmail(invite.email);
  if (!user) {
    redirect(`/signup?email=${invite.email}&invite=${token}`);
  }
  ```

  Add an "Invite teammates" option to `src/components/Sidebar.tsx` within 2 clicks.

---

## External references

- cwe CWE-284

Taxons: user-experience, access-control

HTML version: https://auditbuffet.com/patterns/ab-002366
