Compare commits

...

1 Commits

Author SHA1 Message Date
Piyush Gupta
5fd1cd65d1 chore: adds debug logs 2025-06-05 09:57:02 +05:30
17 changed files with 87 additions and 10 deletions

View File

@@ -0,0 +1,3 @@
import { GET } from "@/modules/ee/auth/saml/api/well-known/cert/route";
export { GET };

View File

@@ -0,0 +1,3 @@
import { GET } from "@/modules/ee/auth/saml/api/well-known/sp-metadata/route";
export { GET };

View File

@@ -597,7 +597,6 @@
"contact_deleted_successfully": "Kontakt erfolgreich gelöscht", "contact_deleted_successfully": "Kontakt erfolgreich gelöscht",
"contact_not_found": "Kein solcher Kontakt gefunden", "contact_not_found": "Kein solcher Kontakt gefunden",
"contacts_table_refresh": "Kontakte aktualisieren", "contacts_table_refresh": "Kontakte aktualisieren",
"contacts_table_refresh_error": "Beim Aktualisieren der Kontakte ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.",
"contacts_table_refresh_success": "Kontakte erfolgreich aktualisiert", "contacts_table_refresh_success": "Kontakte erfolgreich aktualisiert",
"first_name": "Vorname", "first_name": "Vorname",
"last_name": "Nachname", "last_name": "Nachname",

View File

@@ -597,7 +597,6 @@
"contact_deleted_successfully": "Contact deleted successfully", "contact_deleted_successfully": "Contact deleted successfully",
"contact_not_found": "No such contact found", "contact_not_found": "No such contact found",
"contacts_table_refresh": "Refresh contacts", "contacts_table_refresh": "Refresh contacts",
"contacts_table_refresh_error": "Something went wrong while refreshing contacts, please try again",
"contacts_table_refresh_success": "Contacts refreshed successfully", "contacts_table_refresh_success": "Contacts refreshed successfully",
"first_name": "First Name", "first_name": "First Name",
"last_name": "Last Name", "last_name": "Last Name",

View File

@@ -597,7 +597,6 @@
"contact_deleted_successfully": "Contact supprimé avec succès", "contact_deleted_successfully": "Contact supprimé avec succès",
"contact_not_found": "Aucun contact trouvé", "contact_not_found": "Aucun contact trouvé",
"contacts_table_refresh": "Rafraîchir les contacts", "contacts_table_refresh": "Rafraîchir les contacts",
"contacts_table_refresh_error": "Une erreur s'est produite lors de la mise à jour des contacts. Veuillez réessayer.",
"contacts_table_refresh_success": "Contacts rafraîchis avec succès", "contacts_table_refresh_success": "Contacts rafraîchis avec succès",
"first_name": "Prénom", "first_name": "Prénom",
"last_name": "Nom de famille", "last_name": "Nom de famille",

View File

@@ -597,7 +597,6 @@
"contact_deleted_successfully": "Contato excluído com sucesso", "contact_deleted_successfully": "Contato excluído com sucesso",
"contact_not_found": "Nenhum contato encontrado", "contact_not_found": "Nenhum contato encontrado",
"contacts_table_refresh": "Atualizar contatos", "contacts_table_refresh": "Atualizar contatos",
"contacts_table_refresh_error": "Ocorreu um erro ao atualizar os contatos. Por favor, tente novamente.",
"contacts_table_refresh_success": "Contatos atualizados com sucesso", "contacts_table_refresh_success": "Contatos atualizados com sucesso",
"first_name": "Primeiro Nome", "first_name": "Primeiro Nome",
"last_name": "Sobrenome", "last_name": "Sobrenome",

View File

@@ -597,7 +597,6 @@
"contact_deleted_successfully": "Contacto eliminado com sucesso", "contact_deleted_successfully": "Contacto eliminado com sucesso",
"contact_not_found": "Nenhum contacto encontrado", "contact_not_found": "Nenhum contacto encontrado",
"contacts_table_refresh": "Atualizar contactos", "contacts_table_refresh": "Atualizar contactos",
"contacts_table_refresh_error": "Algo correu mal ao atualizar os contactos, por favor, tente novamente",
"contacts_table_refresh_success": "Contactos atualizados com sucesso", "contacts_table_refresh_success": "Contactos atualizados com sucesso",
"first_name": "Primeiro Nome", "first_name": "Primeiro Nome",
"last_name": "Apelido", "last_name": "Apelido",

View File

@@ -597,7 +597,6 @@
"contact_deleted_successfully": "聯絡人已成功刪除", "contact_deleted_successfully": "聯絡人已成功刪除",
"contact_not_found": "找不到此聯絡人", "contact_not_found": "找不到此聯絡人",
"contacts_table_refresh": "重新整理聯絡人", "contacts_table_refresh": "重新整理聯絡人",
"contacts_table_refresh_error": "重新整理聯絡人時發生錯誤,請再試一次",
"contacts_table_refresh_success": "聯絡人已成功重新整理", "contacts_table_refresh_success": "聯絡人已成功重新整理",
"first_name": "名字", "first_name": "名字",
"last_name": "姓氏", "last_name": "姓氏",

View File

@@ -187,6 +187,7 @@ export const authOptions: NextAuthOptions = {
}, },
callbacks: { callbacks: {
async jwt({ token }) { async jwt({ token }) {
console.log("jwt: token", token);
const existingUser = await getUserByEmail(token?.email!); const existingUser = await getUserByEmail(token?.email!);
if (!existingUser) { if (!existingUser) {
@@ -200,6 +201,8 @@ export const authOptions: NextAuthOptions = {
}; };
}, },
async session({ session, token }) { async session({ session, token }) {
console.log("session: session", session);
console.log("session: token", token);
// @ts-expect-error // @ts-expect-error
session.user.id = token?.id; session.user.id = token?.id;
// @ts-expect-error // @ts-expect-error
@@ -223,6 +226,9 @@ export const authOptions: NextAuthOptions = {
return true; return true;
} }
if (ENTERPRISE_LICENSE_KEY) { if (ENTERPRISE_LICENSE_KEY) {
console.log("signIn: user", user);
console.log("signIn: account", account);
console.log("signIn: callbackUrl", callbackUrl);
const result = await handleSsoCallback({ user, account, callbackUrl }); const result = await handleSsoCallback({ user, account, callbackUrl });
if (result) { if (result) {

View File

@@ -20,10 +20,13 @@ export const GET = async (req: NextRequest) => {
try { try {
const { redirect_url } = await oauthController.authorize(searchParams as OAuthReq); const { redirect_url } = await oauthController.authorize(searchParams as OAuthReq);
console.log("saml/authorize", redirect_url);
if (!redirect_url) { if (!redirect_url) {
return responses.internalServerErrorResponse("Failed to get redirect URL"); return responses.internalServerErrorResponse("Failed to get redirect URL");
} }
console.log("saml/authorize: reached to return redirect url", redirect_url);
return NextResponse.redirect(redirect_url); return NextResponse.redirect(redirect_url);
} catch (err: unknown) { } catch (err: unknown) {
const errorMessage = err instanceof Error ? err.message : "An unknown error occurred"; const errorMessage = err instanceof Error ? err.message : "An unknown error occurred";

View File

@@ -15,18 +15,24 @@ export const POST = async (req: Request) => {
const { oauthController } = jacksonInstance; const { oauthController } = jacksonInstance;
const formData = await req.formData(); const formData = await req.formData();
console.log("saml/callback: formData", formData);
const body = Object.fromEntries(formData.entries()); const body = Object.fromEntries(formData.entries());
const { RelayState, SAMLResponse } = body as unknown as SAMLCallbackBody; const { RelayState, SAMLResponse } = body as unknown as SAMLCallbackBody;
console.log("saml/callback: RelayState", RelayState);
console.log("saml/callback: SAMLResponse", SAMLResponse);
const { redirect_url } = await oauthController.samlResponse({ const { redirect_url } = await oauthController.samlResponse({
RelayState, RelayState,
SAMLResponse, SAMLResponse,
}); });
console.log("saml/callback: redirect_url", redirect_url);
if (!redirect_url) { if (!redirect_url) {
return responses.internalServerErrorResponse("Failed to get redirect URL"); return responses.internalServerErrorResponse("Failed to get redirect URL");
} }
console.log("saml/callback: redirecting to", redirect_url);
return redirect(redirect_url); return redirect(redirect_url);
}; };

View File

@@ -10,9 +10,13 @@ export const POST = async (req: Request) => {
const { oauthController } = jacksonInstance; const { oauthController } = jacksonInstance;
const body = await req.formData(); const body = await req.formData();
console.log("saml/token: body", body);
const formData = Object.fromEntries(body.entries()); const formData = Object.fromEntries(body.entries());
console.log("saml/token: formData", formData);
const response = await oauthController.token(formData as unknown as OAuthTokenReq); const response = await oauthController.token(formData as unknown as OAuthTokenReq);
console.log("saml/token: response", response);
return Response.json(response); return Response.json(response);
}; };

View File

@@ -9,8 +9,8 @@ export const GET = async (req: Request) => {
} }
const { oauthController } = jacksonInstance; const { oauthController } = jacksonInstance;
const token = extractAuthToken(req); const token = extractAuthToken(req);
console.log("saml/userinfo: token", token);
const user = await oauthController.userInfo(token); const user = await oauthController.userInfo(token);
console.log("saml/userinfo: user", user);
return Response.json(user); return Response.json(user);
}; };

View File

@@ -0,0 +1,20 @@
import { responses } from "@/app/lib/api/response";
import jackson from "@/modules/ee/auth/saml/lib/jackson";
export async function GET() {
const jacksonInstance = await jackson();
if (!jacksonInstance) {
return responses.forbiddenResponse("SAML SSO is not enabled in your Formbricks license");
}
const { spConfig } = jacksonInstance;
const config = await spConfig.get();
return new Response(config.publicKey, {
status: 200,
headers: {
"Content-Type": "application/x-x509-ca-cert",
},
});
}

View File

@@ -0,0 +1,22 @@
import { responses } from "@/app/lib/api/response";
import jackson from "@/modules/ee/auth/saml/lib/jackson";
import { NextRequest } from "next/server";
export const GET = async (req: NextRequest) => {
const { searchParams } = new URL(req.url);
const encryption = searchParams.get("encryption") === "true";
const jacksonInstance = await jackson();
if (!jacksonInstance) {
return responses.forbiddenResponse("SAML SSO is not enabled in your Formbricks license");
}
const { spConfig } = jacksonInstance;
const xml = await spConfig.toXMLMetadata(encryption);
return new Response(xml, {
status: 200,
headers: {
"Content-Type": "text/xml",
},
});
};

View File

@@ -3,7 +3,12 @@
import { SAML_AUDIENCE, SAML_DATABASE_URL, SAML_PATH, WEBAPP_URL } from "@/lib/constants"; import { SAML_AUDIENCE, SAML_DATABASE_URL, SAML_PATH, WEBAPP_URL } from "@/lib/constants";
import { preloadConnection } from "@/modules/ee/auth/saml/lib/preload-connection"; import { preloadConnection } from "@/modules/ee/auth/saml/lib/preload-connection";
import { getIsSamlSsoEnabled } from "@/modules/ee/license-check/lib/utils"; import { getIsSamlSsoEnabled } from "@/modules/ee/license-check/lib/utils";
import type { IConnectionAPIController, IOAuthController, JacksonOption } from "@boxyhq/saml-jackson"; import type {
IConnectionAPIController,
IOAuthController,
ISPSSOConfig,
JacksonOption,
} from "@boxyhq/saml-jackson";
const opts: JacksonOption = { const opts: JacksonOption = {
externalUrl: WEBAPP_URL, externalUrl: WEBAPP_URL,
@@ -19,12 +24,13 @@ const opts: JacksonOption = {
declare global { declare global {
var oauthController: IOAuthController | undefined; var oauthController: IOAuthController | undefined;
var connectionController: IConnectionAPIController | undefined; var connectionController: IConnectionAPIController | undefined;
var spConfig: ISPSSOConfig | undefined;
} }
const g = global; const g = global;
export default async function init() { export default async function init() {
if (!g.oauthController || !g.connectionController) { if (!g.oauthController || !g.connectionController || !g.spConfig) {
const isSamlSsoEnabled = await getIsSamlSsoEnabled(); const isSamlSsoEnabled = await getIsSamlSsoEnabled();
if (!isSamlSsoEnabled) return; if (!isSamlSsoEnabled) return;
@@ -34,10 +40,12 @@ export default async function init() {
g.oauthController = ret.oauthController; g.oauthController = ret.oauthController;
g.connectionController = ret.connectionAPIController; g.connectionController = ret.connectionAPIController;
g.spConfig = ret.spConfig;
} }
return { return {
oauthController: g.oauthController, oauthController: g.oauthController,
connectionController: g.connectionController, connectionController: g.connectionController,
spConfig: g.spConfig,
}; };
} }

View File

@@ -403,6 +403,14 @@ const nextConfig = {
source: "/api/v1/management/attribute-classes/:id*", source: "/api/v1/management/attribute-classes/:id*",
destination: "/api/v1/management/contact-attribute-keys/:id*", destination: "/api/v1/management/contact-attribute-keys/:id*",
}, },
{
source: "/.well-known/saml.cer",
destination: "/api/auth/saml/well-known/cert",
},
{
source: "/.well-known/sp-metadata",
destination: "/api/auth/saml/well-known/sp-metadata",
},
]; ];
}, },
env: { env: {