Your login
Better Auth
Where verifyInvite, redeemInvite and isInvited go when your login runs on Better 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
better-auth.ts
// NearlyIn with Better Auth: a database hook is the gate, the invitation code is the fast lane.
import { betterAuth } from "better-auth";
import { APIError } from "better-auth/api";
import { magicLink } from "better-auth/plugins";
import { isInvited, redeemInvite, verifyInvite } from "nearlyin/server";
export const auth = betterAuth({
// database: …your adapter…
plugins: [
magicLink({
sendMagicLink: async ({ email, url }) => {
console.log(`Send ${url} to ${email} with your mailer.`);
},
}),
],
databaseHooks: {
user: {
create: {
// 1. isInvited: the gate. No user row is ever written for an address that is not invited.
before: async (user) => {
if (!(await isInvited(user.email))) {
throw new APIError("FORBIDDEN", { message: "You are not invited yet." });
}
return { data: user };
},
},
},
},
});
/** 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");
// 2. 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 };
// 3. redeemInvite: works exactly once. Only go on if it returns the address.
const email = await redeemInvite(code);
if (!email) return new Response("This invitation is no longer valid.", { status: 410 });
// Mails the sign-in link. The hook above lets the new user through: they are invited.
await auth.api.signInMagicLink({ body: { email, callbackURL: "/" }, headers: request.headers });
return new Response("Check your inbox for the sign-in link.");
}What the three calls do is explained under Invitations and codes.