mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-12 09:54:00 +00:00
idp for invites
This commit is contained in:
@@ -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}
|
||||
displayName={sessionWithData.factors?.user?.displayName}
|
||||
showDropdown
|
||||
searchParams={searchParams}
|
||||
></UserAvatar>
|
||||
)}
|
||||
<UserAvatar
|
||||
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>
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,121 +76,134 @@ export function SignInWithIdp({
|
||||
return (
|
||||
<div className="flex flex-col w-full space-y-2 text-sm">
|
||||
{identityProviders &&
|
||||
identityProviders.map((idp, i) => {
|
||||
switch (idp.type) {
|
||||
case IdentityProviderType.APPLE:
|
||||
return (
|
||||
<SignInWithApple
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(idp.id, idpTypeToSlug(IdentityProviderType.APPLE))
|
||||
}
|
||||
></SignInWithApple>
|
||||
);
|
||||
case IdentityProviderType.OAUTH:
|
||||
return (
|
||||
<SignInWithGeneric
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(idp.id, idpTypeToSlug(IdentityProviderType.OAUTH))
|
||||
}
|
||||
></SignInWithGeneric>
|
||||
);
|
||||
case IdentityProviderType.OIDC:
|
||||
return (
|
||||
<SignInWithGeneric
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(idp.id, idpTypeToSlug(IdentityProviderType.OIDC))
|
||||
}
|
||||
></SignInWithGeneric>
|
||||
);
|
||||
case IdentityProviderType.GITHUB:
|
||||
return (
|
||||
<SignInWithGithub
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.GITHUB),
|
||||
)
|
||||
}
|
||||
></SignInWithGithub>
|
||||
);
|
||||
case IdentityProviderType.GITHUB_ES:
|
||||
return (
|
||||
<SignInWithGithub
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.GITHUB_ES),
|
||||
)
|
||||
}
|
||||
></SignInWithGithub>
|
||||
);
|
||||
case IdentityProviderType.AZURE_AD:
|
||||
return (
|
||||
<SignInWithAzureAd
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.AZURE_AD),
|
||||
)
|
||||
}
|
||||
></SignInWithAzureAd>
|
||||
);
|
||||
case IdentityProviderType.GOOGLE:
|
||||
return (
|
||||
<SignInWithGoogle
|
||||
key={`idp-${i}`}
|
||||
e2e="google"
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.GOOGLE),
|
||||
)
|
||||
}
|
||||
></SignInWithGoogle>
|
||||
);
|
||||
case IdentityProviderType.GITLAB:
|
||||
return (
|
||||
<SignInWithGitlab
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.GITLAB),
|
||||
)
|
||||
}
|
||||
></SignInWithGitlab>
|
||||
);
|
||||
case IdentityProviderType.GITLAB_SELF_HOSTED:
|
||||
return (
|
||||
<SignInWithGitlab
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.GITLAB_SELF_HOSTED),
|
||||
)
|
||||
}
|
||||
></SignInWithGitlab>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
identityProviders
|
||||
// .filter((idp) =>
|
||||
// linkOnly ? idp.config?.options.isLinkingAllowed : true,
|
||||
// )
|
||||
.map((idp, i) => {
|
||||
switch (idp.type) {
|
||||
case IdentityProviderType.APPLE:
|
||||
return (
|
||||
<SignInWithApple
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.APPLE),
|
||||
)
|
||||
}
|
||||
></SignInWithApple>
|
||||
);
|
||||
case IdentityProviderType.OAUTH:
|
||||
return (
|
||||
<SignInWithGeneric
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.OAUTH),
|
||||
)
|
||||
}
|
||||
></SignInWithGeneric>
|
||||
);
|
||||
case IdentityProviderType.OIDC:
|
||||
return (
|
||||
<SignInWithGeneric
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.OIDC),
|
||||
)
|
||||
}
|
||||
></SignInWithGeneric>
|
||||
);
|
||||
case IdentityProviderType.GITHUB:
|
||||
return (
|
||||
<SignInWithGithub
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.GITHUB),
|
||||
)
|
||||
}
|
||||
></SignInWithGithub>
|
||||
);
|
||||
case IdentityProviderType.GITHUB_ES:
|
||||
return (
|
||||
<SignInWithGithub
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.GITHUB_ES),
|
||||
)
|
||||
}
|
||||
></SignInWithGithub>
|
||||
);
|
||||
case IdentityProviderType.AZURE_AD:
|
||||
return (
|
||||
<SignInWithAzureAd
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.AZURE_AD),
|
||||
)
|
||||
}
|
||||
></SignInWithAzureAd>
|
||||
);
|
||||
case IdentityProviderType.GOOGLE:
|
||||
return (
|
||||
<SignInWithGoogle
|
||||
key={`idp-${i}`}
|
||||
e2e="google"
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.GOOGLE),
|
||||
)
|
||||
}
|
||||
></SignInWithGoogle>
|
||||
);
|
||||
case IdentityProviderType.GITLAB:
|
||||
return (
|
||||
<SignInWithGitlab
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.GITLAB),
|
||||
)
|
||||
}
|
||||
></SignInWithGitlab>
|
||||
);
|
||||
case IdentityProviderType.GITLAB_SELF_HOSTED:
|
||||
return (
|
||||
<SignInWithGitlab
|
||||
key={`idp-${i}`}
|
||||
name={idp.name}
|
||||
onClick={() =>
|
||||
startFlow(
|
||||
idp.id,
|
||||
idpTypeToSlug(IdentityProviderType.GITLAB_SELF_HOSTED),
|
||||
)
|
||||
}
|
||||
></SignInWithGitlab>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
{error && (
|
||||
<div className="py-4">
|
||||
<Alert>{error}</Alert>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user