org context, idp failure page, cleanup signup

This commit is contained in:
peintnermax
2024-04-01 13:57:39 +02:00
parent bf316fe5a3
commit 8530015244
17 changed files with 371 additions and 220 deletions

View File

@@ -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>
);
}

View 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>
);
}
}

View File

@@ -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 {

View File

@@ -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>
);
}

View File

@@ -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>

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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>
);
}

View File

@@ -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 });

View File

@@ -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);

View File

@@ -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

View File

@@ -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));
});
}

View File

@@ -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) => {

View File

@@ -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),
}),
});

View File

@@ -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,
}),
});