import { SignJWT } from "jose";
import { cookies } from "next/headers";
import { type NextRequest, NextResponse } from "next/server";
export async function GET(req: NextRequest): Promise<NextResponse> {
const domain = getDomain(req); // your logic to determine the docs domain
// use the state param to determine redirect location
const returnTo = req.nextUrl.searchParams.get("state");
const redirectLocation = returnTo ?? `https://${domain}`;
// fetch the user's API key, roles, and secret (from your config or database)
const apiKey = await getApiKeyForUser();
const roles = await getRolesForUser();
const secret = await getSecretForDomain(domain);
if (!secret) {
// redirect with an error if credentials are missing
const url = new URL(redirectLocation);
url.searchParams.set("error", "missing_credentials");
return NextResponse.redirect(url);
}
// mint the JWT using the secret key
const fernToken = await mintFernToken({ secret, apiKey, roles });
if (!fernToken) {
const url = new URL(redirectLocation);
url.searchParams.set("error", "token_creation_failed");
return NextResponse.redirect(url);
}
// set the fern_token as a cookie on the docs domain
const cookieJar = await cookies();
cookieJar.set("fern_token", fernToken, {
httpOnly: true,
secure: true,
sameSite: "lax",
domain,
});
// redirect the user back to the docs
return NextResponse.redirect(redirectLocation);
}
const encoder = new TextEncoder();
async function mintFernToken({
secret,
apiKey,
roles,
}: {
secret: string;
apiKey?: string;
roles?: string[];
}): Promise<string> {
const fern: Record<string, unknown> = {};
if (roles) {
fern.roles = roles;
}
if (apiKey) {
fern.playground = {
initial_state: {
auth: {
bearer_token: apiKey,
},
},
};
}
return await new SignJWT({ fern })
.setProtectedHeader({ alg: "HS256", typ: "JWT" })
.setIssuedAt()
.setExpirationTime("1d") // set to any value
.setIssuer("https://buildwithfern.com")
.sign(encoder.encode(secret)); // sign using the secret provided by Fern
}