web-production-saas-starter/next_b2b_starter/lib/auth/stytch-server.ts
2025-12-16 18:54:41 +04:00

102 lines
2.6 KiB
TypeScript

/**
* Server-only utilities for building auth URLs and config
* These read from process.env and are NOT bundled into client code
*/
import "server-only";
import type { StytchClientConfig } from "./config-types";
export interface LoginUrlOptions {
returnTo?: string;
}
export interface LogoutUrlOptions {
returnTo?: string;
}
function sanitizeReturnTo(value: string | undefined): string | undefined {
if (!value) return undefined;
const trimmed = value.trim();
if (!trimmed.startsWith("/") || trimmed.startsWith("//")) {
return undefined;
}
return trimmed;
}
export function getBaseUrl(): string {
let baseUrl =
process.env.APP_BASE_URL ||
process.env.NEXT_PUBLIC_BASE_URL ||
process.env.NEXT_PUBLIC_APP_BASE_URL ||
process.env.APP_URL ||
null;
const resolved = baseUrl || "http://localhost:3000";
return resolved.replace(/\/$/, "");
}
function resolveRoute(value: string | undefined, fallback: string): URL {
const base = getBaseUrl();
const trimmed = value?.trim();
if (!trimmed) {
return new URL(fallback, `${base}/`);
}
if (/^https?:\/\//i.test(trimmed)) {
return new URL(trimmed);
}
const path = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
return new URL(path, `${base}/`);
}
export function buildLoginUrl(options?: LoginUrlOptions): string {
const url = resolveRoute(process.env.NEXT_PUBLIC_STYTCH_LOGIN_PATH, "/auth");
const returnTo = sanitizeReturnTo(options?.returnTo);
if (returnTo) {
url.searchParams.set("returnTo", returnTo);
}
return url.toString();
}
export function buildLogoutUrl(options?: LogoutUrlOptions): string {
const url = resolveRoute(
process.env.NEXT_PUBLIC_STYTCH_LOGOUT_PATH,
"/api/auth/logout"
);
const returnTo = sanitizeReturnTo(options?.returnTo);
if (returnTo) {
url.searchParams.set("returnTo", returnTo);
}
return url.toString();
}
/**
* Build complete client config from environment variables
* This should only be called on the server
*/
export function buildStytchClientConfig(): StytchClientConfig {
const publicToken = process.env.NEXT_PUBLIC_STYTCH_PUBLIC_TOKEN;
if (!publicToken) {
throw new Error("NEXT_PUBLIC_STYTCH_PUBLIC_TOKEN is required");
}
return {
publicToken,
sessionDurationMinutes:
Number(process.env.NEXT_PUBLIC_STYTCH_SESSION_DURATION_MINUTES ?? "480") ||
480,
loginPath: process.env.NEXT_PUBLIC_STYTCH_LOGIN_PATH || "/auth",
logoutPath:
process.env.NEXT_PUBLIC_STYTCH_LOGOUT_PATH || "/api/auth/logout",
redirectPath:
process.env.NEXT_PUBLIC_STYTCH_REDIRECT_PATH || "/authenticate",
baseUrl: getBaseUrl(),
};
}