mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-21 13:40:31 -06:00
Compare commits
1 Commits
4.0.0-rc.1
...
saml-sso-e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fd1cd65d1 |
3
apps/web/app/api/auth/saml/well-known/cert/route.ts
Normal file
3
apps/web/app/api/auth/saml/well-known/cert/route.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { GET } from "@/modules/ee/auth/saml/api/well-known/cert/route";
|
||||
|
||||
export { GET };
|
||||
@@ -0,0 +1,3 @@
|
||||
import { GET } from "@/modules/ee/auth/saml/api/well-known/sp-metadata/route";
|
||||
|
||||
export { GET };
|
||||
@@ -597,7 +597,6 @@
|
||||
"contact_deleted_successfully": "Kontakt erfolgreich gelöscht",
|
||||
"contact_not_found": "Kein solcher Kontakt gefunden",
|
||||
"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",
|
||||
"first_name": "Vorname",
|
||||
"last_name": "Nachname",
|
||||
|
||||
@@ -597,7 +597,6 @@
|
||||
"contact_deleted_successfully": "Contact deleted successfully",
|
||||
"contact_not_found": "No such contact found",
|
||||
"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",
|
||||
"first_name": "First Name",
|
||||
"last_name": "Last Name",
|
||||
|
||||
@@ -597,7 +597,6 @@
|
||||
"contact_deleted_successfully": "Contact supprimé avec succès",
|
||||
"contact_not_found": "Aucun contact trouvé",
|
||||
"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",
|
||||
"first_name": "Prénom",
|
||||
"last_name": "Nom de famille",
|
||||
|
||||
@@ -597,7 +597,6 @@
|
||||
"contact_deleted_successfully": "Contato excluído com sucesso",
|
||||
"contact_not_found": "Nenhum contato encontrado",
|
||||
"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",
|
||||
"first_name": "Primeiro Nome",
|
||||
"last_name": "Sobrenome",
|
||||
|
||||
@@ -597,7 +597,6 @@
|
||||
"contact_deleted_successfully": "Contacto eliminado com sucesso",
|
||||
"contact_not_found": "Nenhum contacto encontrado",
|
||||
"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",
|
||||
"first_name": "Primeiro Nome",
|
||||
"last_name": "Apelido",
|
||||
|
||||
@@ -597,7 +597,6 @@
|
||||
"contact_deleted_successfully": "聯絡人已成功刪除",
|
||||
"contact_not_found": "找不到此聯絡人",
|
||||
"contacts_table_refresh": "重新整理聯絡人",
|
||||
"contacts_table_refresh_error": "重新整理聯絡人時發生錯誤,請再試一次",
|
||||
"contacts_table_refresh_success": "聯絡人已成功重新整理",
|
||||
"first_name": "名字",
|
||||
"last_name": "姓氏",
|
||||
|
||||
@@ -187,6 +187,7 @@ export const authOptions: NextAuthOptions = {
|
||||
},
|
||||
callbacks: {
|
||||
async jwt({ token }) {
|
||||
console.log("jwt: token", token);
|
||||
const existingUser = await getUserByEmail(token?.email!);
|
||||
|
||||
if (!existingUser) {
|
||||
@@ -200,6 +201,8 @@ export const authOptions: NextAuthOptions = {
|
||||
};
|
||||
},
|
||||
async session({ session, token }) {
|
||||
console.log("session: session", session);
|
||||
console.log("session: token", token);
|
||||
// @ts-expect-error
|
||||
session.user.id = token?.id;
|
||||
// @ts-expect-error
|
||||
@@ -223,6 +226,9 @@ export const authOptions: NextAuthOptions = {
|
||||
return true;
|
||||
}
|
||||
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 });
|
||||
|
||||
if (result) {
|
||||
|
||||
@@ -20,10 +20,13 @@ export const GET = async (req: NextRequest) => {
|
||||
try {
|
||||
const { redirect_url } = await oauthController.authorize(searchParams as OAuthReq);
|
||||
|
||||
console.log("saml/authorize", redirect_url);
|
||||
|
||||
if (!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);
|
||||
} catch (err: unknown) {
|
||||
const errorMessage = err instanceof Error ? err.message : "An unknown error occurred";
|
||||
|
||||
@@ -15,18 +15,24 @@ export const POST = async (req: Request) => {
|
||||
const { oauthController } = jacksonInstance;
|
||||
|
||||
const formData = await req.formData();
|
||||
console.log("saml/callback: formData", formData);
|
||||
const body = Object.fromEntries(formData.entries());
|
||||
|
||||
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({
|
||||
RelayState,
|
||||
SAMLResponse,
|
||||
});
|
||||
|
||||
console.log("saml/callback: redirect_url", redirect_url);
|
||||
|
||||
if (!redirect_url) {
|
||||
return responses.internalServerErrorResponse("Failed to get redirect URL");
|
||||
}
|
||||
|
||||
console.log("saml/callback: redirecting to", redirect_url);
|
||||
return redirect(redirect_url);
|
||||
};
|
||||
|
||||
@@ -10,9 +10,13 @@ export const POST = async (req: Request) => {
|
||||
const { oauthController } = jacksonInstance;
|
||||
|
||||
const body = await req.formData();
|
||||
console.log("saml/token: body", body);
|
||||
const formData = Object.fromEntries(body.entries());
|
||||
console.log("saml/token: formData", formData);
|
||||
|
||||
const response = await oauthController.token(formData as unknown as OAuthTokenReq);
|
||||
|
||||
console.log("saml/token: response", response);
|
||||
|
||||
return Response.json(response);
|
||||
};
|
||||
|
||||
@@ -9,8 +9,8 @@ export const GET = async (req: Request) => {
|
||||
}
|
||||
const { oauthController } = jacksonInstance;
|
||||
const token = extractAuthToken(req);
|
||||
|
||||
console.log("saml/userinfo: token", token);
|
||||
const user = await oauthController.userInfo(token);
|
||||
|
||||
console.log("saml/userinfo: user", user);
|
||||
return Response.json(user);
|
||||
};
|
||||
|
||||
20
apps/web/modules/ee/auth/saml/api/well-known/cert/route.ts
Normal file
20
apps/web/modules/ee/auth/saml/api/well-known/cert/route.ts
Normal 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",
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -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",
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -3,7 +3,12 @@
|
||||
import { SAML_AUDIENCE, SAML_DATABASE_URL, SAML_PATH, WEBAPP_URL } from "@/lib/constants";
|
||||
import { preloadConnection } from "@/modules/ee/auth/saml/lib/preload-connection";
|
||||
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 = {
|
||||
externalUrl: WEBAPP_URL,
|
||||
@@ -19,12 +24,13 @@ const opts: JacksonOption = {
|
||||
declare global {
|
||||
var oauthController: IOAuthController | undefined;
|
||||
var connectionController: IConnectionAPIController | undefined;
|
||||
var spConfig: ISPSSOConfig | undefined;
|
||||
}
|
||||
|
||||
const g = global;
|
||||
|
||||
export default async function init() {
|
||||
if (!g.oauthController || !g.connectionController) {
|
||||
if (!g.oauthController || !g.connectionController || !g.spConfig) {
|
||||
const isSamlSsoEnabled = await getIsSamlSsoEnabled();
|
||||
if (!isSamlSsoEnabled) return;
|
||||
|
||||
@@ -34,10 +40,12 @@ export default async function init() {
|
||||
|
||||
g.oauthController = ret.oauthController;
|
||||
g.connectionController = ret.connectionAPIController;
|
||||
g.spConfig = ret.spConfig;
|
||||
}
|
||||
|
||||
return {
|
||||
oauthController: g.oauthController,
|
||||
connectionController: g.connectionController,
|
||||
spConfig: g.spConfig,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -403,6 +403,14 @@ const nextConfig = {
|
||||
source: "/api/v1/management/attribute-classes/: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: {
|
||||
|
||||
Reference in New Issue
Block a user