Invitation-Gated Registration
Admin-minted invitations gate sign-up: the RegisterPage only succeeds for an invited email.
Live Preview
Create account
Already have an account? Sign in
Key Features
- Registration requires an admin-created invitation for the exact email
- InvitationManager admin panel to create list and revoke invitations
- Invitation claimed atomically (markUsedIfUnused) so one invite yields one account
- Same 403 for any uninvited email so registration status never leaks
- Email verification link sent on successful registration
- Auto-login on success via the established session
Code
Invitation-Gated Registration — full flow
// 1. src/routes/api/auth/register/+server.ts — the bundled register handler
import { createRegisterHandler } from '@urbicon-ui/auth/server';
import { authDeps } from '$lib/server/auth-setup';
export const { POST } = createRegisterHandler(authDeps);
// 2. src/lib/server/invitations.ts — the bundled invitation handlers.
// You only supply the authorization gate (who may manage invitations)
// and the roles an invite may carry; validation, the invitedBy scoping
// (session user, never the body) and {error,code} responses ship with it.
import { createInvitationHandlers } from '@urbicon-ui/auth/server';
import { authDeps } from './auth-setup';
export const invitations = createInvitationHandlers(authDeps, {
authorize: (user) => user.role === 'ADMIN',
roles: ['ADMIN', 'USER']
});
// src/routes/api/invitations/+server.ts — create + list
import { invitations } from '$lib/server/invitations';
export const POST = invitations.POST;
export const GET = invitations.GET;
// src/routes/api/invitations/[id]/+server.ts — revoke (InvitationManager's delete)
import { invitations } from '$lib/server/invitations';
export const DELETE = invitations.DELETE;
// 3. src/routes/auth/register/+page.svelte
<script lang="ts">
import { RegisterPage } from '@urbicon-ui/auth';
import { en } from '@urbicon-ui/auth/i18n/en';
import { goto } from '$app/navigation';
import { page } from '$app/state';
</script>
<!-- The invite link is /auth/register?token=<secret>&email=<invitee>. The token
IS the proof of invitation — without it the request is refused. -->
<RegisterPage
t={en}
token={page.url.searchParams.get('token') ?? ''}
defaultEmail={page.url.searchParams.get('email') ?? ''}
onSuccess={() => goto('/')}
/>
// 4. src/routes/admin/invitations/+page.svelte — admin panel
<script lang="ts">
import { InvitationManager } from '@urbicon-ui/auth';
import { en } from '@urbicon-ui/auth/i18n/en';
const roles = [
{ value: 'ADMIN', label: 'Admin' },
{ value: 'USER', label: 'User' }
];
</script>
<InvitationManager t={en} {roles} apiPath="/api/invitations" />