idp for invites

This commit is contained in:
Max Peintner
2024-11-29 13:38:56 +01:00
parent 837f45b36c
commit bac941890a
5 changed files with 185 additions and 145 deletions

View File

@@ -2,10 +2,12 @@ import { Alert } from "@/components/alert";
import { BackButton } from "@/components/back-button";
import { ChooseAuthenticatorToSetup } from "@/components/choose-authenticator-to-setup";
import { DynamicTheme } from "@/components/dynamic-theme";
import { SignInWithIdp } from "@/components/sign-in-with-idp";
import { UserAvatar } from "@/components/user-avatar";
import { getSessionCookieById } from "@/lib/cookies";
import { loadMostRecentSession } from "@/lib/session";
import {
getActiveIdentityProviders,
getBrandingSettings,
getLoginSettings,
getSession,
@@ -74,6 +76,10 @@ export default async function Page(props: {
});
}
if (!sessionWithData) {
return <Alert>{tError("unknownContext")}</Alert>;
}
const branding = await getBrandingSettings(
sessionWithData.factors?.user?.organizationId,
);
@@ -82,22 +88,32 @@ export default async function Page(props: {
sessionWithData.factors?.user?.organizationId,
);
const identityProviders = await getActiveIdentityProviders(
sessionWithData.factors?.user?.organizationId,
).then((resp) => {
return resp.identityProviders;
});
const params = new URLSearchParams({
initial: "true", // defines that a code is not required and is therefore not shown in the UI
});
if (loginName) {
params.set("loginName", loginName);
if (sessionWithData.factors?.user?.loginName) {
params.set("loginName", sessionWithData.factors?.user?.loginName);
}
if (organization) {
params.set("organization", organization);
if (sessionWithData.factors?.user?.organizationId) {
params.set("organization", sessionWithData.factors?.user?.organizationId);
}
if (authRequestId) {
params.set("authRequestId", authRequestId);
}
const host = process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: "http://localhost:3000";
return (
<DynamicTheme branding={branding}>
<div className="flex flex-col items-center space-y-4">
@@ -105,18 +121,14 @@ export default async function Page(props: {
<p className="ztdl-p">{t("description")}</p>
{sessionWithData && (
<UserAvatar
loginName={loginName ?? sessionWithData.factors?.user?.loginName}
loginName={sessionWithData.factors?.user?.loginName}
displayName={sessionWithData.factors?.user?.displayName}
showDropdown
searchParams={searchParams}
></UserAvatar>
)}
{!(loginName || sessionId) && <Alert>{tError("unknownContext")}</Alert>}
{loginSettings && sessionWithData && (
{loginSettings && (
<ChooseAuthenticatorToSetup
authMethods={sessionWithData.authMethods}
loginSettings={loginSettings}
@@ -124,6 +136,20 @@ export default async function Page(props: {
></ChooseAuthenticatorToSetup>
)}
<p className="ztdl-p text-center">
or sign in with an Identity Provider
</p>
{loginSettings?.allowExternalIdp && identityProviders && (
<SignInWithIdp
host={host}
identityProviders={identityProviders}
authRequestId={authRequestId}
organization={sessionWithData.factors?.user?.organizationId}
linkOnly={true} // tell the callback function to just link the IDP and not login, to get an error when user is already available
></SignInWithIdp>
)}
<div className="mt-8 flex w-full flex-row items-center">
<BackButton />
<span className="flex-grow"></span>

View File

@@ -37,7 +37,7 @@ export default async function Page(props: {
const searchParams = await props.searchParams;
const locale = getLocale();
const t = await getTranslations({ locale, namespace: "idp" });
const { id, token, authRequestId, organization } = searchParams;
const { id, token, authRequestId, organization, link } = searchParams;
const { provider } = params;
const branding = await getBrandingSettings(organization);
@@ -50,7 +50,8 @@ export default async function Page(props: {
const { idpInformation, userId } = intent;
if (userId) {
// sign in user. If user should be linked continue
if (userId && !link) {
// TODO: update user if idp.options.isAutoUpdate is true
return (

View File

@@ -2,23 +2,14 @@ import { DynamicTheme } from "@/components/dynamic-theme";
import { SignInWithIdp } from "@/components/sign-in-with-idp";
import { UsernameForm } from "@/components/username-form";
import {
getActiveIdentityProviders,
getBrandingSettings,
getDefaultOrg,
getLoginSettings,
settingsService,
} from "@/lib/zitadel";
import { makeReqCtx } from "@zitadel/client/v2";
import { Organization } from "@zitadel/proto/zitadel/org/v2/org_pb";
import { getLocale, getTranslations } from "next-intl/server";
function getIdentityProviders(orgId?: string) {
return settingsService
.getActiveIdentityProviders({ ctx: makeReqCtx(orgId) }, {})
.then((resp) => {
return resp.identityProviders;
});
}
export default async function Page(props: {
searchParams: Promise<Record<string | number | symbol, string | undefined>>;
}) {
@@ -47,9 +38,11 @@ export default async function Page(props: {
organization ?? defaultOrganization,
);
const identityProviders = await getIdentityProviders(
const identityProviders = await getActiveIdentityProviders(
organization ?? defaultOrganization,
);
).then((resp) => {
return resp.identityProviders;
});
const branding = await getBrandingSettings(
organization ?? defaultOrganization,
@@ -68,7 +61,7 @@ export default async function Page(props: {
submit={submit}
allowRegister={!!loginSettings?.allowRegister}
>
{identityProviders && process.env.ZITADEL_API_URL && (
{identityProviders && (
<SignInWithIdp
host={host}
identityProviders={identityProviders}

View File

@@ -22,6 +22,7 @@ export interface SignInWithIDPProps {
identityProviders: IdentityProvider[];
authRequestId?: string;
organization?: string;
linkOnly?: boolean;
}
export function SignInWithIdp({
@@ -29,6 +30,7 @@ export function SignInWithIdp({
identityProviders,
authRequestId,
organization,
linkOnly,
}: SignInWithIDPProps) {
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string>("");
@@ -39,6 +41,10 @@ export function SignInWithIdp({
const params = new URLSearchParams();
if (linkOnly) {
params.set("link", "true");
}
if (authRequestId) {
params.set("authRequestId", authRequestId);
}
@@ -70,7 +76,11 @@ export function SignInWithIdp({
return (
<div className="flex flex-col w-full space-y-2 text-sm">
{identityProviders &&
identityProviders.map((idp, i) => {
identityProviders
// .filter((idp) =>
// linkOnly ? idp.config?.options.isLinkingAllowed : true,
// )
.map((idp, i) => {
switch (idp.type) {
case IdentityProviderType.APPLE:
return (
@@ -78,7 +88,10 @@ export function SignInWithIdp({
key={`idp-${i}`}
name={idp.name}
onClick={() =>
startFlow(idp.id, idpTypeToSlug(IdentityProviderType.APPLE))
startFlow(
idp.id,
idpTypeToSlug(IdentityProviderType.APPLE),
)
}
></SignInWithApple>
);
@@ -88,7 +101,10 @@ export function SignInWithIdp({
key={`idp-${i}`}
name={idp.name}
onClick={() =>
startFlow(idp.id, idpTypeToSlug(IdentityProviderType.OAUTH))
startFlow(
idp.id,
idpTypeToSlug(IdentityProviderType.OAUTH),
)
}
></SignInWithGeneric>
);
@@ -98,7 +114,10 @@ export function SignInWithIdp({
key={`idp-${i}`}
name={idp.name}
onClick={() =>
startFlow(idp.id, idpTypeToSlug(IdentityProviderType.OIDC))
startFlow(
idp.id,
idpTypeToSlug(IdentityProviderType.OIDC),
)
}
></SignInWithGeneric>
);

View File

@@ -149,6 +149,7 @@ export async function sendPassword(command: UpdateSessionCommand) {
);
const humanUser = user.type.case === "human" ? user.type.value : undefined;
console.log("humanUser", humanUser);
if (
availableSecondFactors?.length == 0 &&
humanUser?.passwordChangeRequired