A newly-invited byrå admin/member who signed up with email+password landed
on /onboarding instead of the cockpit. Root cause: team-invite acceptance
lived only in POST /api/team/accept, which the email-confirmation signup flow
never reaches before the dashboard (no session for the register page's
client-side accept), while the auth callback and the onboarding/select-company
recovery only understood company_invitations. So the invitee's byrå membership
did not exist when landing resolved, and they were funneled into creating a
company.
- New shared helper acceptPendingTeamInviteByToken (lib/company/pending-invites)
is the single server-side implementation of team-invite acceptance.
- POST /api/team/accept delegates to it; HTTP contract unchanged.
- /auth/callback accepts a team invite BEFORE the silent-team check and before
resolveLandingDestination runs, so an owner/admin resolves to /clients; the
invite cookie is cleared on success, kept otherwise for the retry.
- acceptPendingInviteByToken (onboarding/select-company recovery) tries the
company path, then falls back to the team helper.
- hasPendingInviteForEmail checks both invite tables, so a tokenless byrå
invitee is not misread as a first-timer.
No migration (team invite tables already exist). Company-invite and
non-invite flows are untouched.
Claude-Session: https://claude.ai/code/session_01ByL5dQXG8gGLtNBPj8g2C4
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
An invited user who registered via BankID was funneled into creating a
company instead of joining the one they were invited to: the register
page's BankID path never processed the gnubok-invite-token cookie
(unlike the login, MFA-verify, and auth-callback paths). Observed in
production 2026-07-24.
- register: BankID signup now accepts the pending invite before routing
to /select-company, mirroring the login page's BankID path.
- lib/company/pending-invites: acceptPendingInviteByToken retries a
missed acceptance from the cookie (pending + unexpired + email match,
same rules as POST /api/team/accept); hasPendingInviteForEmail detects
a stranded invitee whose cookie is gone.
- /onboarding and /select-company retry acceptance from the cookie and
redirect to the dashboard on success, making the auth callback's
long-promised fallback real; with no cookie but a pending invitation,
both surfaces show a 'join via the link in the invitation email' hint
instead of silently asking the invitee to create a company.
- No new accept path without the token: the hint deliberately points
back to the mailed link, so mailbox possession stays required and no
company name is leaked to unverified emails.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>