sf-auth-middleware-nextjs/src/callback.ts

89 lines
2.4 KiB
TypeScript

import { NextResponse, type NextRequest } from "next/server";
import { AUTH_VALIDATE_URL, DEFAULT_COOKIE_NAMES } from "./constants";
import type { SfAuthCookieNames } from "./middleware";
export type SfAuthCallbackOptions = {
redirectTo: string;
cookieNames?: SfAuthCookieNames;
validateEndpoint?: string;
};
type ValidateResponse = {
valid: boolean;
user_id: string;
};
const resolveCookieNames = (cookieNames?: SfAuthCookieNames) => ({
userId: cookieNames?.userId ?? DEFAULT_COOKIE_NAMES.userId,
username: cookieNames?.username ?? DEFAULT_COOKIE_NAMES.username
});
const errorResponse = (message: string, status = 400) =>
new NextResponse(message, {
status,
headers: {
"content-type": "text/plain; charset=utf-8"
}
});
export const createSfAuthCallbackRoute = (
options: SfAuthCallbackOptions
) => {
const cookieNames = resolveCookieNames(options.cookieNames);
const validateUrl = options.validateEndpoint ?? AUTH_VALIDATE_URL;
return async (request: NextRequest) => {
const url = new URL(request.url);
const userId = url.searchParams.get("user_id");
const username = url.searchParams.get("username");
const key = url.searchParams.get("key");
if (!userId || !username || !key) {
return errorResponse("Missing required query parameters.");
}
let validateResponse: ValidateResponse;
try {
const response = await fetch(validateUrl, {
method: "POST",
headers: {
"content-type": "application/json"
},
body: JSON.stringify({ user_id: userId, key })
});
if (!response.ok) {
return errorResponse("Failed to validate credentials.");
}
validateResponse = (await response.json()) as ValidateResponse;
} catch (error) {
return errorResponse("Unable to validate credentials.", 500);
}
if (!validateResponse.valid || validateResponse.user_id !== userId) {
return errorResponse("Invalid credentials.");
}
const redirectTarget = new URL(options.redirectTo, request.url);
const response = NextResponse.redirect(redirectTarget);
response.cookies.set(cookieNames.userId, userId, {
httpOnly: true,
sameSite: "strict",
secure: true,
path: "/"
});
response.cookies.set(cookieNames.username, username, {
httpOnly: true,
sameSite: "strict",
secure: true,
path: "/"
});
return response;
};
};