Your login
Auth.js
Where verifyInvite, redeemInvite and isInvited go when your login runs on Auth.js.
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
authjs.ts
// NearlyIn with Auth.js: only invited addresses can sign in, whatever provider they use.
import type { AuthConfig } from "@auth/core";
import GitHub from "@auth/core/providers/github";
import { isInvited, redeemInvite, verifyInvite } from "nearlyin/server";
const INVITE_COOKIE = "invite";
/** 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 || !code) return new Response("This invitation is no longer valid.", { status: 410 });
// Keep the code for after the sign-in, out of reach of scripts and out of the address bar.
return new Response(`Welcome, ${email}. Sign in to get started.`, {
headers: { "Set-Cookie": `${INVITE_COOKIE}=${code}; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600` },
});
}
export const authConfig: AuthConfig = {
providers: [GitHub],
callbacks: {
// 2. isInvited: the gate. Runs on every sign-in, with every provider.
async signIn({ user }) {
return user.email ? isInvited(user.email) : false;
},
},
};
/** GET /welcome: where Auth.js sends people after signing in (`pages.newUser` or your redirect). */
export async function finishJoining(request: Request): Promise<Response> {
const code = /(?:^|;\s*)invite=([^;]+)/.exec(request.headers.get("cookie") ?? "")?.[1];
// 3. redeemInvite: the account exists now, so the code has done its job. Works exactly once.
if (code) await redeemInvite(code);
return new Response("You are in.", {
headers: { "Set-Cookie": `${INVITE_COOKIE}=; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=0` },
});
}What the three calls do is explained under Invitations and codes.