mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-12 15:03:52 +00:00
org context, idp failure page, cleanup signup
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { Session } from "@zitadel/server";
|
||||
import { listSessions, server } from "#/lib/zitadel";
|
||||
import { getBrandingSettings, listSessions, server } from "#/lib/zitadel";
|
||||
import { getAllSessionCookieIds } from "#/utils/cookies";
|
||||
import { UserPlusIcon } from "@heroicons/react/24/outline";
|
||||
import Link from "next/link";
|
||||
import SessionsList from "#/ui/SessionsList";
|
||||
import DynamicTheme from "#/ui/DynamicTheme";
|
||||
|
||||
async function loadSessions(): Promise<Session[]> {
|
||||
const ids = await getAllSessionCookieIds();
|
||||
@@ -26,34 +27,39 @@ export default async function Page({
|
||||
searchParams: Record<string | number | symbol, string | undefined>;
|
||||
}) {
|
||||
const authRequestId = searchParams?.authRequestId;
|
||||
const organization = searchParams?.organization;
|
||||
|
||||
let sessions = await loadSessions();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Accounts</h1>
|
||||
<p className="ztdl-p mb-6 block">Use your ZITADEL Account</p>
|
||||
const branding = await getBrandingSettings(server, organization);
|
||||
|
||||
<div className="flex flex-col w-full space-y-2">
|
||||
<SessionsList sessions={sessions} authRequestId={authRequestId} />
|
||||
<Link
|
||||
href={
|
||||
authRequestId
|
||||
? `/loginname?` +
|
||||
new URLSearchParams({
|
||||
authRequestId,
|
||||
})
|
||||
: "/loginname"
|
||||
}
|
||||
>
|
||||
<div className="flex flex-row items-center py-3 px-4 hover:bg-black/10 dark:hover:bg-white/10 rounded-md transition-all">
|
||||
<div className="w-8 h-8 mr-4 flex flex-row justify-center items-center rounded-full bg-black/5 dark:bg-white/5">
|
||||
<UserPlusIcon className="h-5 w-5" />
|
||||
return (
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Accounts</h1>
|
||||
<p className="ztdl-p mb-6 block">Use your ZITADEL Account</p>
|
||||
|
||||
<div className="flex flex-col w-full space-y-2">
|
||||
<SessionsList sessions={sessions} authRequestId={authRequestId} />
|
||||
<Link
|
||||
href={
|
||||
authRequestId
|
||||
? `/loginname?` +
|
||||
new URLSearchParams({
|
||||
authRequestId,
|
||||
})
|
||||
: "/loginname"
|
||||
}
|
||||
>
|
||||
<div className="flex flex-row items-center py-3 px-4 hover:bg-black/10 dark:hover:bg-white/10 rounded-md transition-all">
|
||||
<div className="w-8 h-8 mr-4 flex flex-row justify-center items-center rounded-full bg-black/5 dark:bg-white/5">
|
||||
<UserPlusIcon className="h-5 w-5" />
|
||||
</div>
|
||||
<span className="text-sm">Add another account</span>
|
||||
</div>
|
||||
<span className="text-sm">Add another account</span>
|
||||
</div>
|
||||
</Link>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DynamicTheme>
|
||||
);
|
||||
}
|
||||
|
||||
61
apps/login/app/(login)/idp/[provider]/failure/page.tsx
Normal file
61
apps/login/app/(login)/idp/[provider]/failure/page.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { ProviderSlug } from "#/lib/demos";
|
||||
import { getBrandingSettings, server } from "#/lib/zitadel";
|
||||
import Alert, { AlertType } from "#/ui/Alert";
|
||||
import DynamicTheme from "#/ui/DynamicTheme";
|
||||
import IdpSignin from "#/ui/IdpSignin";
|
||||
import {
|
||||
AddHumanUserRequest,
|
||||
IDPInformation,
|
||||
RetrieveIdentityProviderIntentResponse,
|
||||
user,
|
||||
IDPLink,
|
||||
} from "@zitadel/server";
|
||||
import { ClientError } from "nice-grpc";
|
||||
|
||||
const PROVIDER_NAME_MAPPING: {
|
||||
[provider: string]: string;
|
||||
} = {
|
||||
[ProviderSlug.GOOGLE]: "Google",
|
||||
[ProviderSlug.GITHUB]: "GitHub",
|
||||
};
|
||||
|
||||
export default async function Page({
|
||||
searchParams,
|
||||
params,
|
||||
}: {
|
||||
searchParams: Record<string | number | symbol, string | undefined>;
|
||||
params: { provider: ProviderSlug };
|
||||
}) {
|
||||
const { id, token, authRequestId, organization } = searchParams;
|
||||
const { provider } = params;
|
||||
|
||||
const branding = await getBrandingSettings(server, organization);
|
||||
|
||||
if (provider) {
|
||||
return (
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Login failure</h1>
|
||||
<div>
|
||||
An error signing in with{" "}
|
||||
{PROVIDER_NAME_MAPPING[provider]
|
||||
? PROVIDER_NAME_MAPPING[provider]
|
||||
: provider}{" "}
|
||||
happened!
|
||||
</div>
|
||||
|
||||
{/* <Alert type={AlertType.ALERT}>
|
||||
{}
|
||||
</Alert> */}
|
||||
</div>
|
||||
</DynamicTheme>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Register</h1>
|
||||
<p className="ztdl-p">No id and token received!</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
import { ProviderSlug } from "#/lib/demos";
|
||||
import { server } from "#/lib/zitadel";
|
||||
import { getBrandingSettings, server } from "#/lib/zitadel";
|
||||
import Alert, { AlertType } from "#/ui/Alert";
|
||||
import DynamicTheme from "#/ui/DynamicTheme";
|
||||
import IdpSignin from "#/ui/IdpSignin";
|
||||
import { createSessionForIdpAndUpdateCookie } from "#/utils/session";
|
||||
import {
|
||||
AddHumanUserRequest,
|
||||
IDPInformation,
|
||||
RetrieveIdentityProviderIntentResponse,
|
||||
user,
|
||||
IDPLink,
|
||||
Session,
|
||||
} from "@zitadel/server";
|
||||
import { ClientError } from "nice-grpc";
|
||||
|
||||
@@ -89,9 +88,11 @@ export default async function Page({
|
||||
searchParams: Record<string | number | symbol, string | undefined>;
|
||||
params: { provider: ProviderSlug };
|
||||
}) {
|
||||
const { id, token, authRequestId } = searchParams;
|
||||
const { id, token, authRequestId, organization } = searchParams;
|
||||
const { provider } = params;
|
||||
|
||||
const branding = await getBrandingSettings(server, organization);
|
||||
|
||||
if (provider && id && token) {
|
||||
return retrieveIDPIntent(id, token)
|
||||
.then((resp) => {
|
||||
@@ -100,40 +101,46 @@ export default async function Page({
|
||||
// handle login
|
||||
if (userId) {
|
||||
return (
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Login successful</h1>
|
||||
<div>You have successfully been loggedIn!</div>
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Login successful</h1>
|
||||
<div>You have successfully been loggedIn!</div>
|
||||
|
||||
<IdpSignin
|
||||
userId={userId}
|
||||
idpIntent={{ idpIntentId: id, idpIntentToken: token }}
|
||||
authRequestId={authRequestId}
|
||||
/>
|
||||
</div>
|
||||
<IdpSignin
|
||||
userId={userId}
|
||||
idpIntent={{ idpIntentId: id, idpIntentToken: token }}
|
||||
authRequestId={authRequestId}
|
||||
/>
|
||||
</div>
|
||||
</DynamicTheme>
|
||||
);
|
||||
} else {
|
||||
// handle register
|
||||
return createUser(provider, idpInformation)
|
||||
.then((userId) => {
|
||||
return (
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Register successful</h1>
|
||||
<div>You have successfully been registered!</div>
|
||||
</div>
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Register successful</h1>
|
||||
<div>You have successfully been registered!</div>
|
||||
</div>
|
||||
</DynamicTheme>
|
||||
);
|
||||
})
|
||||
.catch((error: ClientError) => {
|
||||
return (
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Register failed</h1>
|
||||
<div className="w-full">
|
||||
{
|
||||
<Alert type={AlertType.ALERT}>
|
||||
{JSON.stringify(error.message)}
|
||||
</Alert>
|
||||
}
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Register failed</h1>
|
||||
<div className="w-full">
|
||||
{
|
||||
<Alert type={AlertType.ALERT}>
|
||||
{JSON.stringify(error.message)}
|
||||
</Alert>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DynamicTheme>
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -143,16 +150,18 @@ export default async function Page({
|
||||
})
|
||||
.catch((error) => {
|
||||
return (
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>An error occurred</h1>
|
||||
<div className="w-full">
|
||||
{
|
||||
<Alert type={AlertType.ALERT}>
|
||||
{JSON.stringify(error.message)}
|
||||
</Alert>
|
||||
}
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>An error occurred</h1>
|
||||
<div className="w-full">
|
||||
{
|
||||
<Alert type={AlertType.ALERT}>
|
||||
{JSON.stringify(error.message)}
|
||||
</Alert>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DynamicTheme>
|
||||
);
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { getLegalAndSupportSettings, server } from "#/lib/zitadel";
|
||||
import {
|
||||
getBrandingSettings,
|
||||
getLegalAndSupportSettings,
|
||||
server,
|
||||
} from "#/lib/zitadel";
|
||||
import DynamicTheme from "#/ui/DynamicTheme";
|
||||
import { SignInWithIDP } from "#/ui/SignInWithIDP";
|
||||
import {
|
||||
GetActiveIdentityProvidersResponse,
|
||||
@@ -28,30 +33,35 @@ export default async function Page({
|
||||
searchParams: Record<string | number | symbol, string | undefined>;
|
||||
}) {
|
||||
const authRequestId = searchParams?.authRequestId;
|
||||
const organization = searchParams?.organization;
|
||||
|
||||
const legal = await getLegalAndSupportSettings(server);
|
||||
const legal = await getLegalAndSupportSettings(server, organization);
|
||||
|
||||
// TODO if org idps should be shown replace emptystring with the orgId.
|
||||
const identityProviders = await getIdentityProviders(server, "");
|
||||
const identityProviders = await getIdentityProviders(server, organization);
|
||||
|
||||
const host = process.env.VERCEL_URL
|
||||
? `https://${process.env.VERCEL_URL}`
|
||||
: "http://localhost:3000";
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Register</h1>
|
||||
<p className="ztdl-p">
|
||||
Select one of the following providers to register
|
||||
</p>
|
||||
const branding = await getBrandingSettings(server, organization);
|
||||
|
||||
{legal && identityProviders && process.env.ZITADEL_API_URL && (
|
||||
<SignInWithIDP
|
||||
host={host}
|
||||
identityProviders={identityProviders}
|
||||
authRequestId={authRequestId}
|
||||
></SignInWithIDP>
|
||||
)}
|
||||
</div>
|
||||
return (
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Register</h1>
|
||||
<p className="ztdl-p">
|
||||
Select one of the following providers to register
|
||||
</p>
|
||||
|
||||
{legal && identityProviders && process.env.ZITADEL_API_URL && (
|
||||
<SignInWithIDP
|
||||
host={host}
|
||||
identityProviders={identityProviders}
|
||||
authRequestId={authRequestId}
|
||||
organization={organization}
|
||||
></SignInWithIDP>
|
||||
)}
|
||||
</div>
|
||||
</DynamicTheme>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ import {
|
||||
} from "#/lib/zitadel";
|
||||
import DynamicTheme from "#/ui/DynamicTheme";
|
||||
import { SignInWithIDP } from "#/ui/SignInWithIDP";
|
||||
import ThemeWrapper from "#/ui/ThemeWrapper";
|
||||
import UsernameForm from "#/ui/UsernameForm";
|
||||
import { BrandingSettings } from "@zitadel/server";
|
||||
import {
|
||||
GetActiveIdentityProvidersResponse,
|
||||
IdentityProvider,
|
||||
@@ -71,6 +69,7 @@ export default async function Page({
|
||||
host={host}
|
||||
identityProviders={identityProviders}
|
||||
authRequestId={authRequestId}
|
||||
organization={organization}
|
||||
></SignInWithIDP>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getSession, server } from "#/lib/zitadel";
|
||||
import { getBrandingSettings, getSession, server } from "#/lib/zitadel";
|
||||
import Alert, { AlertType } from "#/ui/Alert";
|
||||
import DynamicTheme from "#/ui/DynamicTheme";
|
||||
import RegisterPasskey from "#/ui/RegisterPasskey";
|
||||
import UserAvatar from "#/ui/UserAvatar";
|
||||
import { getMostRecentCookieWithLoginname } from "#/utils/cookies";
|
||||
@@ -31,48 +32,52 @@ export default async function Page({
|
||||
? "When set up, you will be able to authenticate without a password."
|
||||
: "Your device will ask for your fingerprint, face, or screen lock";
|
||||
|
||||
const branding = await getBrandingSettings(server, organization);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>{title}</h1>
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>{title}</h1>
|
||||
|
||||
{sessionFactors && (
|
||||
<UserAvatar
|
||||
loginName={loginName ?? sessionFactors.factors?.user?.loginName}
|
||||
displayName={sessionFactors.factors?.user?.displayName}
|
||||
showDropdown
|
||||
></UserAvatar>
|
||||
)}
|
||||
<p className="ztdl-p mb-6 block">{description}</p>
|
||||
{sessionFactors && (
|
||||
<UserAvatar
|
||||
loginName={loginName ?? sessionFactors.factors?.user?.loginName}
|
||||
displayName={sessionFactors.factors?.user?.displayName}
|
||||
showDropdown
|
||||
></UserAvatar>
|
||||
)}
|
||||
<p className="ztdl-p mb-6 block">{description}</p>
|
||||
|
||||
<Alert type={AlertType.INFO}>
|
||||
<span>
|
||||
A passkey is an authentication method on a device like your
|
||||
fingerprint, Apple FaceID or similar.
|
||||
<a
|
||||
className="text-primary-light-500 dark:text-primary-dark-500 hover:text-primary-light-300 hover:dark:text-primary-dark-300"
|
||||
target="_blank"
|
||||
href="https://zitadel.com/docs/guides/manage/user/reg-create-user#with-passwordless"
|
||||
>
|
||||
Passwordless Authentication
|
||||
</a>
|
||||
</span>
|
||||
</Alert>
|
||||
<Alert type={AlertType.INFO}>
|
||||
<span>
|
||||
A passkey is an authentication method on a device like your
|
||||
fingerprint, Apple FaceID or similar.
|
||||
<a
|
||||
className="text-primary-light-500 dark:text-primary-dark-500 hover:text-primary-light-300 hover:dark:text-primary-dark-300"
|
||||
target="_blank"
|
||||
href="https://zitadel.com/docs/guides/manage/user/reg-create-user#with-passwordless"
|
||||
>
|
||||
Passwordless Authentication
|
||||
</a>
|
||||
</span>
|
||||
</Alert>
|
||||
|
||||
{!sessionFactors && (
|
||||
<div className="py-4">
|
||||
<Alert>
|
||||
Could not get the context of the user. Make sure to enter the
|
||||
username first or provide a loginName as searchParam.
|
||||
</Alert>
|
||||
</div>
|
||||
)}
|
||||
{!sessionFactors && (
|
||||
<div className="py-4">
|
||||
<Alert>
|
||||
Could not get the context of the user. Make sure to enter the
|
||||
username first or provide a loginName as searchParam.
|
||||
</Alert>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{sessionFactors?.id && (
|
||||
<RegisterPasskey
|
||||
sessionId={sessionFactors.id}
|
||||
isPrompt={!!promptPasswordless}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{sessionFactors?.id && (
|
||||
<RegisterPasskey
|
||||
sessionId={sessionFactors.id}
|
||||
isPrompt={!!promptPasswordless}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</DynamicTheme>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {
|
||||
getBrandingSettings,
|
||||
getLegalAndSupportSettings,
|
||||
getPasswordComplexitySettings,
|
||||
server,
|
||||
} from "#/lib/zitadel";
|
||||
import DynamicTheme from "#/ui/DynamicTheme";
|
||||
import RegisterFormWithoutPassword from "#/ui/RegisterFormWithoutPassword";
|
||||
import SetPasswordForm from "#/ui/SetPasswordForm";
|
||||
|
||||
@@ -16,40 +18,46 @@ export default async function Page({
|
||||
|
||||
const setPassword = !!(firstname && lastname && email);
|
||||
|
||||
const legal = await getLegalAndSupportSettings(server);
|
||||
const legal = await getLegalAndSupportSettings(server, organization);
|
||||
const passwordComplexitySettings = await getPasswordComplexitySettings(
|
||||
server,
|
||||
organization
|
||||
);
|
||||
|
||||
const branding = await getBrandingSettings(server, organization);
|
||||
|
||||
return setPassword ? (
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Set Password</h1>
|
||||
<p className="ztdl-p">Set the password for your account</p>
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Set Password</h1>
|
||||
<p className="ztdl-p">Set the password for your account</p>
|
||||
|
||||
{legal && passwordComplexitySettings && (
|
||||
<SetPasswordForm
|
||||
passwordComplexitySettings={passwordComplexitySettings}
|
||||
email={email}
|
||||
firstname={firstname}
|
||||
lastname={lastname}
|
||||
organization={organization}
|
||||
authRequestId={authRequestId}
|
||||
></SetPasswordForm>
|
||||
)}
|
||||
</div>
|
||||
{legal && passwordComplexitySettings && (
|
||||
<SetPasswordForm
|
||||
passwordComplexitySettings={passwordComplexitySettings}
|
||||
email={email}
|
||||
firstname={firstname}
|
||||
lastname={lastname}
|
||||
organization={organization}
|
||||
authRequestId={authRequestId}
|
||||
></SetPasswordForm>
|
||||
)}
|
||||
</div>
|
||||
</DynamicTheme>
|
||||
) : (
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Register</h1>
|
||||
<p className="ztdl-p">Create your ZITADEL account.</p>
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Register</h1>
|
||||
<p className="ztdl-p">Create your ZITADEL account.</p>
|
||||
|
||||
{legal && passwordComplexitySettings && (
|
||||
<RegisterFormWithoutPassword
|
||||
legal={legal}
|
||||
organization={organization}
|
||||
authRequestId={authRequestId}
|
||||
></RegisterFormWithoutPassword>
|
||||
)}
|
||||
</div>
|
||||
{legal && passwordComplexitySettings && (
|
||||
<RegisterFormWithoutPassword
|
||||
legal={legal}
|
||||
organization={organization}
|
||||
authRequestId={authRequestId}
|
||||
></RegisterFormWithoutPassword>
|
||||
)}
|
||||
</div>
|
||||
</DynamicTheme>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { createCallback, getSession, server } from "#/lib/zitadel";
|
||||
import {
|
||||
createCallback,
|
||||
getBrandingSettings,
|
||||
getSession,
|
||||
server,
|
||||
} from "#/lib/zitadel";
|
||||
import DynamicTheme from "#/ui/DynamicTheme";
|
||||
import UserAvatar from "#/ui/UserAvatar";
|
||||
import { getMostRecentCookieWithLoginname } from "#/utils/cookies";
|
||||
import { redirect } from "next/navigation";
|
||||
@@ -22,19 +28,23 @@ async function loadSession(loginName: string, authRequestId?: string) {
|
||||
}
|
||||
|
||||
export default async function Page({ searchParams }: { searchParams: any }) {
|
||||
const { loginName, authRequestId } = searchParams;
|
||||
const { loginName, authRequestId, organization } = searchParams;
|
||||
const sessionFactors = await loadSession(loginName, authRequestId);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>{`Welcome ${sessionFactors?.factors?.user?.displayName}`}</h1>
|
||||
<p className="ztdl-p mb-6 block">You are signed in.</p>
|
||||
const branding = await getBrandingSettings(server, organization);
|
||||
|
||||
<UserAvatar
|
||||
loginName={loginName ?? sessionFactors?.factors?.user?.loginName}
|
||||
displayName={sessionFactors?.factors?.user?.displayName}
|
||||
showDropdown
|
||||
></UserAvatar>
|
||||
</div>
|
||||
return (
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>{`Welcome ${sessionFactors?.factors?.user?.displayName}`}</h1>
|
||||
<p className="ztdl-p mb-6 block">You are signed in.</p>
|
||||
|
||||
<UserAvatar
|
||||
loginName={loginName ?? sessionFactors?.factors?.user?.loginName}
|
||||
displayName={sessionFactors?.factors?.user?.displayName}
|
||||
showDropdown
|
||||
></UserAvatar>
|
||||
</div>
|
||||
</DynamicTheme>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import {
|
||||
getLegalAndSupportSettings,
|
||||
getPasswordComplexitySettings,
|
||||
server,
|
||||
} from "#/lib/zitadel";
|
||||
import RegisterForm from "#/ui/RegisterForm";
|
||||
|
||||
export default async function Page() {
|
||||
const legal = await getLegalAndSupportSettings(server);
|
||||
const passwordComplexitySettings = await getPasswordComplexitySettings(
|
||||
server
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Register</h1>
|
||||
<p className="ztdl-p">Create your ZITADEL account.</p>
|
||||
|
||||
{legal && passwordComplexitySettings && (
|
||||
<RegisterForm
|
||||
legal={legal}
|
||||
passwordComplexitySettings={passwordComplexitySettings}
|
||||
></RegisterForm>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,28 +1,36 @@
|
||||
import { getBrandingSettings, server } from "#/lib/zitadel";
|
||||
import DynamicTheme from "#/ui/DynamicTheme";
|
||||
import VerifyEmailForm from "#/ui/VerifyEmailForm";
|
||||
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
||||
|
||||
export default async function Page({ searchParams }: { searchParams: any }) {
|
||||
const { userID, code, submit, orgID, loginname, passwordset } = searchParams;
|
||||
const { userID, code, submit, organization, loginname, passwordset } =
|
||||
searchParams;
|
||||
|
||||
const branding = await getBrandingSettings(server, organization);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Verify user</h1>
|
||||
<p className="ztdl-p mb-6 block">
|
||||
Enter the Code provided in the verification email.
|
||||
</p>
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<h1>Verify user</h1>
|
||||
<p className="ztdl-p mb-6 block">
|
||||
Enter the Code provided in the verification email.
|
||||
</p>
|
||||
|
||||
{userID ? (
|
||||
<VerifyEmailForm
|
||||
userId={userID}
|
||||
code={code}
|
||||
submit={submit === "true"}
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full flex flex-row items-center justify-center border border-yellow-600/40 dark:border-yellow-500/20 bg-yellow-200/30 text-yellow-600 dark:bg-yellow-700/20 dark:text-yellow-200 rounded-md py-2 scroll-px-40">
|
||||
<ExclamationTriangleIcon className="h-5 w-5 mr-2" />
|
||||
<span className="text-center text-sm">No userId provided!</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{userID ? (
|
||||
<VerifyEmailForm
|
||||
userId={userID}
|
||||
code={code}
|
||||
submit={submit === "true"}
|
||||
organization={organization}
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full flex flex-row items-center justify-center border border-yellow-600/40 dark:border-yellow-500/20 bg-yellow-200/30 text-yellow-600 dark:bg-yellow-700/20 dark:text-yellow-200 rounded-md py-2 scroll-px-40">
|
||||
<ExclamationTriangleIcon className="h-5 w-5 mr-2" />
|
||||
<span className="text-center text-sm">No userId provided!</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</DynamicTheme>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,13 +4,21 @@ import { NextRequest, NextResponse } from "next/server";
|
||||
export async function POST(request: NextRequest) {
|
||||
const body = await request.json();
|
||||
if (body) {
|
||||
const { email, password, firstName, lastName } = body;
|
||||
const {
|
||||
email,
|
||||
password,
|
||||
firstName,
|
||||
lastName,
|
||||
organization,
|
||||
authRequestId,
|
||||
} = body;
|
||||
|
||||
return addHumanUser(server, {
|
||||
email: email,
|
||||
firstName,
|
||||
lastName,
|
||||
password: password ? password : undefined,
|
||||
organization,
|
||||
})
|
||||
.then((userId) => {
|
||||
return NextResponse.json({ userId });
|
||||
|
||||
@@ -89,9 +89,11 @@ export async function GET(request: NextRequest) {
|
||||
|
||||
if (
|
||||
authRequest?.scope &&
|
||||
authRequest.scope.find((s) => ORG_SCOPE_REGEX.test(s))
|
||||
authRequest.scope.find((s: string) => ORG_SCOPE_REGEX.test(s))
|
||||
) {
|
||||
const orgId = authRequest.scope.find((s) => ORG_SCOPE_REGEX.test(s));
|
||||
const orgId = authRequest.scope.find((s: string) =>
|
||||
ORG_SCOPE_REGEX.test(s)
|
||||
);
|
||||
|
||||
if (orgId) {
|
||||
const matched = ORG_SCOPE_REGEX.exec(orgId);
|
||||
|
||||
@@ -87,11 +87,15 @@ export async function getGeneralSettings(
|
||||
}
|
||||
|
||||
export async function getLegalAndSupportSettings(
|
||||
server: ZitadelServer
|
||||
server: ZitadelServer,
|
||||
organization?: string
|
||||
): Promise<LegalAndSupportSettings | undefined> {
|
||||
const settingsService = settings.getSettings(server);
|
||||
return settingsService
|
||||
.getLegalAndSupportSettings({}, {})
|
||||
.getLegalAndSupportSettings(
|
||||
{ ctx: organization ? { orgId: organization } : { instance: true } },
|
||||
{}
|
||||
)
|
||||
.then((resp: GetLegalAndSupportSettingsResponse) => {
|
||||
return resp.settings;
|
||||
});
|
||||
@@ -261,11 +265,12 @@ export type AddHumanUserData = {
|
||||
lastName: string;
|
||||
email: string;
|
||||
password: string | undefined;
|
||||
organization: string | undefined;
|
||||
};
|
||||
|
||||
export async function addHumanUser(
|
||||
server: ZitadelServer,
|
||||
{ email, firstName, lastName, password }: AddHumanUserData
|
||||
{ email, firstName, lastName, password, organization }: AddHumanUserData
|
||||
): Promise<string> {
|
||||
const userService = user.getUser(server);
|
||||
|
||||
@@ -274,6 +279,11 @@ export async function addHumanUser(
|
||||
username: email,
|
||||
profile: { givenName: firstName, familyName: lastName },
|
||||
};
|
||||
|
||||
if (organization) {
|
||||
payload.organization = { orgId: organization };
|
||||
}
|
||||
|
||||
return userService
|
||||
.addHumanUser(
|
||||
password
|
||||
|
||||
@@ -32,11 +32,13 @@ type Inputs =
|
||||
type Props = {
|
||||
legal: LegalAndSupportSettings;
|
||||
passwordComplexitySettings: PasswordComplexitySettings;
|
||||
organization?: string;
|
||||
};
|
||||
|
||||
export default function RegisterForm({
|
||||
legal,
|
||||
passwordComplexitySettings,
|
||||
organization,
|
||||
}: Props) {
|
||||
const { register, handleSubmit, watch, formState } = useForm<Inputs>({
|
||||
mode: "onBlur",
|
||||
@@ -58,6 +60,7 @@ export default function RegisterForm({
|
||||
password: values.password,
|
||||
firstName: values.firstname,
|
||||
lastName: values.lastname,
|
||||
organization: organization,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -72,7 +75,13 @@ export default function RegisterForm({
|
||||
|
||||
function submitAndLink(value: Inputs): Promise<boolean | void> {
|
||||
return submitRegister(value).then((resp: any) => {
|
||||
return router.push(`/verify?userID=${resp.userId}`);
|
||||
const params: any = { userID: resp.userId };
|
||||
|
||||
if (organization) {
|
||||
params.organization = organization;
|
||||
}
|
||||
|
||||
return router.push(`/verify?` + new URLSearchParams(params));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ export default function RegisterFormWithoutPassword({
|
||||
email: values.email,
|
||||
firstName: values.firstname,
|
||||
lastName: values.lastname,
|
||||
organization: organization,
|
||||
}),
|
||||
});
|
||||
setLoading(false);
|
||||
@@ -88,16 +89,30 @@ export default function RegisterFormWithoutPassword({
|
||||
value: Inputs,
|
||||
withPassword: boolean = false
|
||||
) {
|
||||
const registerParams: any = value;
|
||||
|
||||
if (organization) {
|
||||
registerParams.organization = organization;
|
||||
}
|
||||
|
||||
return withPassword
|
||||
? router.push(`/register?` + new URLSearchParams(value))
|
||||
? router.push(`/register?` + new URLSearchParams(registerParams))
|
||||
: submitAndRegister(value)
|
||||
.then((resp: any) => {
|
||||
createSessionWithLoginName(value.email).then(({ factors }) => {
|
||||
setError("");
|
||||
return router.push(
|
||||
`/passkey/add?` +
|
||||
new URLSearchParams({ loginName: factors.user.loginName })
|
||||
);
|
||||
|
||||
const params: any = { loginName: factors.user.loginName };
|
||||
|
||||
if (organization) {
|
||||
params.organization = organization;
|
||||
}
|
||||
|
||||
if (authRequestId) {
|
||||
params.authRequestId = authRequestId;
|
||||
}
|
||||
|
||||
return router.push(`/passkey/add?` + new URLSearchParams(params));
|
||||
});
|
||||
})
|
||||
.catch((errorDetails: Error) => {
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface SignInWithIDPProps {
|
||||
host: string;
|
||||
identityProviders: any[];
|
||||
authRequestId?: string;
|
||||
organization?: string;
|
||||
startIDPFlowPath?: (idpId: string) => string;
|
||||
}
|
||||
|
||||
@@ -26,6 +27,7 @@ export function SignInWithIDP({
|
||||
host,
|
||||
identityProviders,
|
||||
authRequestId,
|
||||
organization,
|
||||
startIDPFlowPath = START_IDP_FLOW_PATH,
|
||||
}: SignInWithIDPProps) {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
@@ -35,6 +37,16 @@ export function SignInWithIDP({
|
||||
async function startFlow(idpId: string, provider: ProviderSlug) {
|
||||
setLoading(true);
|
||||
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (authRequestId) {
|
||||
params.set("authRequestId", authRequestId);
|
||||
}
|
||||
|
||||
if (organization) {
|
||||
params.set("organization", organization);
|
||||
}
|
||||
|
||||
const res = await fetch("/api/idp/start", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -42,11 +54,10 @@ export function SignInWithIDP({
|
||||
},
|
||||
body: JSON.stringify({
|
||||
idpId,
|
||||
successUrl: authRequestId
|
||||
? `${host}/idp/${provider}/success?` +
|
||||
new URLSearchParams({ authRequestId })
|
||||
: `${host}/idp/${provider}/success`,
|
||||
failureUrl: `${host}/idp/${provider}/failure`,
|
||||
successUrl:
|
||||
`${host}/idp/${provider}/success?` + new URLSearchParams(params),
|
||||
failureUrl:
|
||||
`${host}/idp/${provider}/failure?` + new URLSearchParams(params),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -16,9 +16,15 @@ type Props = {
|
||||
userId: string;
|
||||
code: string;
|
||||
submit: boolean;
|
||||
organization?: string;
|
||||
};
|
||||
|
||||
export default function VerifyEmailForm({ userId, code, submit }: Props) {
|
||||
export default function VerifyEmailForm({
|
||||
userId,
|
||||
code,
|
||||
submit,
|
||||
organization,
|
||||
}: Props) {
|
||||
const { register, handleSubmit, formState } = useForm<Inputs>({
|
||||
mode: "onBlur",
|
||||
defaultValues: {
|
||||
@@ -50,6 +56,7 @@ export default function VerifyEmailForm({ userId, code, submit }: Props) {
|
||||
body: JSON.stringify({
|
||||
code: values.code,
|
||||
userId,
|
||||
organization,
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user