Your login
Supabase Auth
Where verifyInvite, redeemInvite and isInvited go when your login runs on Supabase Auth.
NearlyIn does not replace your login; it only decides who may sign up. The file is checked against the library's real types; it has not been run against a real account with the provider.
The code
supabase.ts
// NearlyIn with Supabase Auth: the invitation link creates the user; a hook keeps everyone else out.
import { createClient } from "@supabase/supabase-js";
import { isInvited, redeemInvite, verifyInvite } from "nearlyin/server";
// Server only: the service role key may create users.
const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE_KEY!);
/** GET /join?invite=<code>: the page the invitation mail links to. */
export async function showJoinPage(request: Request): Promise<Response> {
const code = new URL(request.url).searchParams.get("invite");
// 1. verifyInvite: who is this? Does not use the code up, so opening the link twice is fine.
const email = await verifyInvite(code);
if (!email) return new Response("This invitation is no longer valid.", { status: 410 });
return Response.json({ email, code }); // render "Welcome, <email>" with a button that posts the code
}
/** POST /join: the person pressed the button. */
export async function acceptInvitation(request: Request): Promise<Response> {
const { code } = (await request.json()) as { code?: string };
// 2. redeemInvite: works exactly once. Create the user only if it returns the address.
const email = await redeemInvite(code);
if (!email) return new Response("This invitation is no longer valid.", { status: 410 });
// The code proved that the address is theirs: mark it as confirmed right away.
const created = await supabase.auth.admin.createUser({ email, email_confirm: true });
if (created.error) return new Response(created.error.message, { status: 500 });
const link = await supabase.auth.admin.generateLink({ type: "magiclink", email });
if (link.error) return new Response(link.error.message, { status: 500 });
return Response.redirect(link.data.properties.action_link, 303); // signs them in
}
/**
* POST /hooks/before-user-created: Supabase's "Before User Created" auth hook (HTTP).
* A second lock, for sign-ups that do not come through the invitation link.
*/
export async function beforeUserCreated(request: Request): Promise<Response> {
const { user } = (await request.json()) as { user: { email?: string } };
// 3. isInvited: for when there is no code, only an address.
if (user.email && (await isInvited(user.email))) return Response.json({});
return Response.json({ error: { http_code: 403, message: "You are not invited yet." } }, { status: 403 });
}What the three calls do is explained under Invitations and codes.