import { Alert, AlertType } from "@/components/alert"; import { DynamicTheme } from "@/components/dynamic-theme"; import { IdpSignin } from "@/components/idp-signin"; import { idpTypeToIdentityProviderType, PROVIDER_MAPPING } from "@/lib/idp"; import { addIDPLink, createUser, getBrandingSettings, getIDPByID, listUsers, retrieveIDPIntent, } from "@/lib/zitadel"; import { AutoLinkingOption } from "@zitadel/proto/zitadel/idp/v2/idp_pb"; import { BrandingSettings } from "@zitadel/proto/zitadel/settings/v2/branding_settings_pb"; import { getLocale, getTranslations } from "next-intl/server"; async function loginFailed(branding?: BrandingSettings) { const locale = getLocale(); const t = await getTranslations({ locale, namespace: "idp" }); return ( {t("loginError.title")} {{t("loginError.title")}} ); } async function loginSuccess( userId: string, idpIntent: { idpIntentId: string; idpIntentToken: string }, authRequestId?: string, branding?: BrandingSettings, ) { const locale = getLocale(); const t = await getTranslations({ locale, namespace: "idp" }); return ( {t("loginSuccess.title")} {t("loginSuccess.description")} ); } async function linkingSuccess( userId: string, idpIntent: { idpIntentId: string; idpIntentToken: string }, authRequestId?: string, branding?: BrandingSettings, ) { const locale = getLocale(); const t = await getTranslations({ locale, namespace: "idp" }); return ( {t("linkingSuccess.title")} {t("linkingSuccess.description")} ); } export default async function Page(props: { searchParams: Promise>; params: Promise<{ provider: string }>; }) { const params = await props.params; const searchParams = await props.searchParams; const locale = getLocale(); const t = await getTranslations({ locale, namespace: "idp" }); const { id, token, authRequestId, organization, link } = searchParams; const { provider } = params; const branding = await getBrandingSettings(organization); if (!provider || !id || !token) { return loginFailed(branding); } const intent = await retrieveIDPIntent(id, token); const { idpInformation, userId } = intent; // sign in user. If user should be linked continue if (userId && !link) { // TODO: update user if idp.options.isAutoUpdate is true return loginSuccess( userId, { idpIntentId: id, idpIntentToken: token }, authRequestId, branding, ); } if (!idpInformation) { return loginFailed(branding); } const idp = await getIDPByID(idpInformation.idpId); const options = idp?.config?.options; if (!idp) { throw new Error("IDP not found"); } const providerType = idpTypeToIdentityProviderType(idp.type); // search for potential user via username, then link if (options?.isLinkingAllowed) { let foundUser; const email = PROVIDER_MAPPING[providerType](idpInformation).email?.email; if (options.autoLinking === AutoLinkingOption.EMAIL && email) { foundUser = await listUsers({ email }).then((response) => { return response.result ? response.result[0] : null; }); } else if (options.autoLinking === AutoLinkingOption.USERNAME) { foundUser = await listUsers( options.autoLinking === AutoLinkingOption.USERNAME ? { userName: idpInformation.userName } : { email }, ).then((response) => { return response.result ? response.result[0] : null; }); } else { foundUser = await listUsers({ userName: idpInformation.userName, email, }).then((response) => { return response.result ? response.result[0] : null; }); } if (foundUser) { const idpLink = await addIDPLink( { id: idpInformation.idpId, userId: idpInformation.userId, userName: idpInformation.userName, }, foundUser.userId, ).catch((error) => { return ( {t("linkingError.title")} { {t("linkingError.description")} } ); }); if (idpLink) { return linkingSuccess( foundUser.userId, { idpIntentId: id, idpIntentToken: token }, authRequestId, branding, ); } } } if (options?.isCreationAllowed && options.isAutoCreation) { const newUser = await createUser(providerType, idpInformation); if (newUser) { return ( {t("registerSuccess.title")} {t("registerSuccess.description")} ); } } // return login failed if no linking or creation is allowed and no user was found return loginFailed(branding); }