feat: add team invitation acceptance flow with email link routing

Implement end-to-end invitation acceptance: neutral entry route validates
token and routes to register (new users), login (existing users), or
auto-accepts (authenticated users). Handles 2FA token survival via
session, email case-insensitive matching, and dedicated error pages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-27 15:16:45 +01:00
parent 8f39bd9b73
commit 88e5803061
13 changed files with 422 additions and 19 deletions

View File

@@ -12,11 +12,21 @@ import { register } from '@/routes';
import { store } from '@/routes/login';
import { request } from '@/routes/password';
defineProps<{
type Props = {
status?: string;
canResetPassword: boolean;
canRegister: boolean;
}>();
invitation?: string;
};
const props = withDefaults(defineProps<Props>(), {
status: undefined,
invitation: undefined,
});
const registerUrl = props.invitation
? `${register()}?invitation=${props.invitation}`
: register();
</script>
<template>
@@ -39,6 +49,8 @@ defineProps<{
v-slot="{ errors, processing }"
class="flex flex-col gap-6"
>
<input v-if="invitation" type="hidden" name="invitation" :value="invitation" />
<div class="grid gap-6">
<div class="grid gap-2">
<Label for="email">Email address</Label>
@@ -103,7 +115,7 @@ defineProps<{
v-if="canRegister"
>
Don't have an account?
<TextLink :href="register()" :tabindex="5">Sign up</TextLink>
<TextLink :href="registerUrl" :tabindex="5">Sign up</TextLink>
</div>
</Form>
</AuthBase>