mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-12 14:42:17 +00:00
register user api, verify email, resend, set password
This commit is contained in:
@@ -31,7 +31,7 @@ export default async function Page({
|
||||
<h1>{sessionFactors?.factors?.user?.displayName ?? "Password"}</h1>
|
||||
<p className="ztdl-p mb-6 block">Enter your password.</p>
|
||||
|
||||
{!sessionFactors && (
|
||||
{(!sessionFactors || !loginName) && (
|
||||
<div className="py-4">
|
||||
<Alert>
|
||||
Could not get the context of the user. Make sure to enter the
|
||||
@@ -49,14 +49,16 @@ export default async function Page({
|
||||
></UserAvatar>
|
||||
)}
|
||||
|
||||
<PasswordForm
|
||||
loginName={loginName}
|
||||
authRequestId={authRequestId}
|
||||
organization={organization}
|
||||
loginSettings={loginSettings}
|
||||
promptPasswordless={promptPasswordless === "true"}
|
||||
isAlternative={alt === "true"}
|
||||
/>
|
||||
{loginName && (
|
||||
<PasswordForm
|
||||
loginName={loginName}
|
||||
authRequestId={authRequestId}
|
||||
organization={organization}
|
||||
loginSettings={loginSettings}
|
||||
promptPasswordless={promptPasswordless === "true"}
|
||||
isAlternative={alt === "true"}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</DynamicTheme>
|
||||
);
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import { addHumanUser } from "@/lib/zitadel";
|
||||
import {
|
||||
createSessionAndUpdateCookie,
|
||||
createSessionForUserIdAndUpdateCookie,
|
||||
} from "@/utils/session";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const body = await request.json();
|
||||
if (body) {
|
||||
const {
|
||||
email,
|
||||
password,
|
||||
firstName,
|
||||
lastName,
|
||||
organization,
|
||||
authRequestId,
|
||||
} = body;
|
||||
|
||||
return addHumanUser({
|
||||
email: email,
|
||||
firstName,
|
||||
lastName,
|
||||
password: password ? password : undefined,
|
||||
organization,
|
||||
})
|
||||
.then((user) => {
|
||||
return createSessionForUserIdAndUpdateCookie(
|
||||
user.userId,
|
||||
password,
|
||||
undefined,
|
||||
authRequestId,
|
||||
).then((session) => {
|
||||
return NextResponse.json({
|
||||
userId: user.userId,
|
||||
sessionId: session.id,
|
||||
factors: session.factors,
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
return NextResponse.json(error, { status: 500 });
|
||||
});
|
||||
} else {
|
||||
return NextResponse.error();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { resendEmailCode } from "@/lib/zitadel";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const body = await request.json();
|
||||
if (body) {
|
||||
const { userId } = body;
|
||||
|
||||
// replace with resend Mail method once its implemented
|
||||
return resendEmailCode(userId)
|
||||
.then((resp) => {
|
||||
return NextResponse.json(resp);
|
||||
})
|
||||
.catch((error) => {
|
||||
return NextResponse.json(error, { status: 500 });
|
||||
});
|
||||
} else {
|
||||
return NextResponse.error();
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import { listUsers, passwordReset } from "@/lib/zitadel";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const body = await request.json();
|
||||
if (body) {
|
||||
const { loginName, organization } = body;
|
||||
return listUsers({
|
||||
userName: loginName,
|
||||
organizationId: organization,
|
||||
}).then((users) => {
|
||||
if (
|
||||
users.details &&
|
||||
Number(users.details.totalResult) == 1 &&
|
||||
users.result[0].userId
|
||||
) {
|
||||
const userId = users.result[0].userId;
|
||||
|
||||
return passwordReset(userId)
|
||||
.then((resp) => {
|
||||
return NextResponse.json(resp);
|
||||
})
|
||||
.catch((error) => {
|
||||
return NextResponse.json(error, { status: 500 });
|
||||
});
|
||||
} else {
|
||||
return NextResponse.json({ error: "User not found" }, { status: 404 });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { verifyEmail } from "@/lib/zitadel";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const body = await request.json();
|
||||
if (body) {
|
||||
const { userId, code } = body;
|
||||
|
||||
return verifyEmail(userId, code)
|
||||
.then((resp) => {
|
||||
return NextResponse.json(resp);
|
||||
})
|
||||
.catch((error) => {
|
||||
return NextResponse.json(error, { status: 500 });
|
||||
});
|
||||
} else {
|
||||
return NextResponse.error();
|
||||
}
|
||||
}
|
||||
24
apps/login/src/lib/server/email.ts
Normal file
24
apps/login/src/lib/server/email.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
"use server";
|
||||
|
||||
import { resendEmailCode, verifyEmail } from "@/lib/zitadel";
|
||||
|
||||
type VerifyUserByEmailCommand = {
|
||||
userId: string;
|
||||
code: string;
|
||||
};
|
||||
|
||||
export async function verifyUserByEmail(command: VerifyUserByEmailCommand) {
|
||||
const { userId, code } = command;
|
||||
return verifyEmail(userId, code);
|
||||
}
|
||||
|
||||
type resendVerifyEmailCommand = {
|
||||
userId: string;
|
||||
};
|
||||
|
||||
export async function resendVerifyEmail(command: resendVerifyEmailCommand) {
|
||||
const { userId } = command;
|
||||
|
||||
// replace with resend Mail method once its implemented
|
||||
return resendEmailCode(userId);
|
||||
}
|
||||
27
apps/login/src/lib/server/password.ts
Normal file
27
apps/login/src/lib/server/password.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
"use server";
|
||||
|
||||
import { listUsers, passwordReset } from "@/lib/zitadel";
|
||||
|
||||
type ResetPasswordCommand = {
|
||||
loginName: string;
|
||||
organization?: string;
|
||||
};
|
||||
|
||||
export async function resetPassword(command: ResetPasswordCommand) {
|
||||
const { loginName, organization } = command;
|
||||
const users = await listUsers({
|
||||
userName: loginName,
|
||||
organizationId: organization,
|
||||
});
|
||||
|
||||
if (
|
||||
!users.details ||
|
||||
Number(users.details.totalResult) !== 1 ||
|
||||
users.result[0].userId
|
||||
) {
|
||||
throw Error("Could not find user");
|
||||
}
|
||||
const userId = users.result[0].userId;
|
||||
|
||||
return passwordReset(userId);
|
||||
}
|
||||
44
apps/login/src/lib/server/register.ts
Normal file
44
apps/login/src/lib/server/register.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
"use server";
|
||||
|
||||
import { addHumanUser } from "@/lib/zitadel";
|
||||
import {
|
||||
createSessionAndUpdateCookie,
|
||||
createSessionForUserIdAndUpdateCookie,
|
||||
} from "@/utils/session";
|
||||
|
||||
type RegisterUserCommand = {
|
||||
email: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
password?: string;
|
||||
organization?: string;
|
||||
authRequestId?: string;
|
||||
};
|
||||
export async function registerUser(command: RegisterUserCommand) {
|
||||
const { email, password, firstName, lastName, organization, authRequestId } =
|
||||
command;
|
||||
|
||||
const human = await addHumanUser({
|
||||
email: email,
|
||||
firstName,
|
||||
lastName,
|
||||
password: password ? password : undefined,
|
||||
organization,
|
||||
});
|
||||
if (!human) {
|
||||
throw Error("Could not create user");
|
||||
}
|
||||
|
||||
return createSessionForUserIdAndUpdateCookie(
|
||||
human.userId,
|
||||
password,
|
||||
undefined,
|
||||
authRequestId,
|
||||
).then((session) => {
|
||||
return {
|
||||
userId: human.userId,
|
||||
sessionId: session.id,
|
||||
factors: session.factors,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import { create } from "@zitadel/client";
|
||||
import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
||||
import { updateSession } from "@/lib/server/session";
|
||||
import { resetPassword } from "@/lib/server/password";
|
||||
|
||||
type Inputs = {
|
||||
password: string;
|
||||
@@ -24,7 +25,7 @@ type Inputs = {
|
||||
|
||||
type Props = {
|
||||
loginSettings: LoginSettings | undefined;
|
||||
loginName?: string;
|
||||
loginName: string;
|
||||
organization?: string;
|
||||
authRequestId?: string;
|
||||
isAlternative?: boolean; // whether password was requested as alternative auth method
|
||||
@@ -69,30 +70,20 @@ export default function PasswordForm({
|
||||
return response;
|
||||
}
|
||||
|
||||
async function resetPassword() {
|
||||
async function resetPasswordAndContinue() {
|
||||
setError("");
|
||||
setLoading(true);
|
||||
|
||||
const res = await fetch("/api/resetpassword", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
loginName,
|
||||
organization,
|
||||
authRequestId,
|
||||
}),
|
||||
const response = await resetPassword({
|
||||
loginName,
|
||||
organization,
|
||||
}).catch((error: Error) => {
|
||||
setLoading(false);
|
||||
setError(error.message ?? "Could not reset password");
|
||||
});
|
||||
|
||||
const response = await res.json();
|
||||
|
||||
setLoading(false);
|
||||
if (!res.ok) {
|
||||
console.log(response.details.details);
|
||||
setError(response.details?.details ?? "Could not verify password");
|
||||
return Promise.reject(response.details);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -237,7 +228,7 @@ export default function PasswordForm({
|
||||
/>
|
||||
<button
|
||||
className="transition-all text-sm hover:text-primary-light-500 dark:hover:text-primary-dark-500"
|
||||
onClick={() => resetPassword()}
|
||||
onClick={() => resetPasswordAndContinue()}
|
||||
type="button"
|
||||
disabled={loading}
|
||||
>
|
||||
|
||||
@@ -13,7 +13,7 @@ import AuthenticationMethodRadio, {
|
||||
import Alert from "./Alert";
|
||||
import BackButton from "./BackButton";
|
||||
import { LegalAndSupportSettings } from "@zitadel/proto/zitadel/settings/v2/legal_settings_pb";
|
||||
import { first } from "node_modules/cypress/types/lodash";
|
||||
import { registerUser } from "@/lib/server/register";
|
||||
|
||||
type Inputs =
|
||||
| {
|
||||
@@ -57,24 +57,19 @@ export default function RegisterFormWithoutPassword({
|
||||
|
||||
async function submitAndRegister(values: Inputs) {
|
||||
setLoading(true);
|
||||
const res = await fetch("/api/registeruser", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: values.email,
|
||||
firstName: values.firstname,
|
||||
lastName: values.lastname,
|
||||
organization: organization,
|
||||
}),
|
||||
const response = await registerUser({
|
||||
email: values.email,
|
||||
firstName: values.firstname,
|
||||
lastName: values.lastname,
|
||||
organization: organization,
|
||||
}).catch((error) => {
|
||||
setError(error.message ?? "Could not register user");
|
||||
setLoading(false);
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
if (!res.ok) {
|
||||
const error = await res.json();
|
||||
throw new Error(error.details);
|
||||
}
|
||||
return res.json();
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async function submitAndContinue(
|
||||
@@ -91,28 +86,28 @@ export default function RegisterFormWithoutPassword({
|
||||
registerParams.authRequestId = authRequestId;
|
||||
}
|
||||
|
||||
return withPassword
|
||||
? router.push(`/register?` + new URLSearchParams(registerParams))
|
||||
: submitAndRegister(value)
|
||||
.then((session) => {
|
||||
setError("");
|
||||
if (withPassword) {
|
||||
return router.push(`/register?` + new URLSearchParams(registerParams));
|
||||
} else {
|
||||
const session = await submitAndRegister(value).catch((error) => {
|
||||
setError(error.message ?? "Could not register user");
|
||||
});
|
||||
|
||||
const params: any = { loginName: session.factors.user.loginName };
|
||||
const params = new URLSearchParams({});
|
||||
if (session?.factors?.user?.loginName) {
|
||||
params.set("loginName", session.factors?.user?.loginName);
|
||||
}
|
||||
|
||||
if (organization) {
|
||||
params.organization = organization;
|
||||
}
|
||||
if (organization) {
|
||||
params.set("organization", organization);
|
||||
}
|
||||
|
||||
if (authRequestId) {
|
||||
params.authRequestId = authRequestId;
|
||||
}
|
||||
if (authRequestId) {
|
||||
params.set("authRequestId", authRequestId);
|
||||
}
|
||||
|
||||
return router.push(`/passkey/add?` + new URLSearchParams(params));
|
||||
})
|
||||
.catch((errorDetails: Error) => {
|
||||
setLoading(false);
|
||||
setError(errorDetails.message);
|
||||
});
|
||||
return router.push(`/passkey/add?` + new URLSearchParams(params));
|
||||
}
|
||||
}
|
||||
|
||||
const { errors } = formState;
|
||||
|
||||
@@ -15,6 +15,7 @@ import { useRouter } from "next/navigation";
|
||||
import { Spinner } from "./Spinner";
|
||||
import Alert from "./Alert";
|
||||
import { PasswordComplexitySettings } from "@zitadel/proto/zitadel/settings/v2/password_settings_pb";
|
||||
import { registerUser } from "@/lib/server/register";
|
||||
|
||||
type Inputs =
|
||||
| {
|
||||
@@ -56,52 +57,36 @@ export default function SetPasswordForm({
|
||||
|
||||
async function submitRegister(values: Inputs) {
|
||||
setLoading(true);
|
||||
const res = await fetch("/api/registeruser", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: email,
|
||||
firstName: firstname,
|
||||
lastName: lastname,
|
||||
organization: organization,
|
||||
authRequestId: authRequestId,
|
||||
password: values.password,
|
||||
}),
|
||||
const response = await registerUser({
|
||||
email: email,
|
||||
firstName: firstname,
|
||||
lastName: lastname,
|
||||
organization: organization,
|
||||
authRequestId: authRequestId,
|
||||
password: values.password,
|
||||
}).catch((error: Error) => {
|
||||
setError(error.message ?? "Could not register user");
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
if (!res.ok) {
|
||||
const error = await res.json();
|
||||
throw new Error(error.details);
|
||||
|
||||
if (!response) {
|
||||
setError("Could not register user");
|
||||
return;
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
const params: any = { userId: response.userId };
|
||||
|
||||
function submitAndLink(value: Inputs): Promise<boolean | void> {
|
||||
return submitRegister(value)
|
||||
.then((registerResponse) => {
|
||||
setError("");
|
||||
if (authRequestId) {
|
||||
params.authRequestId = authRequestId;
|
||||
}
|
||||
if (organization) {
|
||||
params.organization = organization;
|
||||
}
|
||||
if (response && response.sessionId) {
|
||||
params.sessionId = response.sessionId;
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
const params: any = { userId: registerResponse.userId };
|
||||
|
||||
if (authRequestId) {
|
||||
params.authRequestId = authRequestId;
|
||||
}
|
||||
if (organization) {
|
||||
params.organization = organization;
|
||||
}
|
||||
if (registerResponse && registerResponse.sessionId) {
|
||||
params.sessionId = registerResponse.sessionId;
|
||||
}
|
||||
|
||||
return router.push(`/verify?` + new URLSearchParams(params));
|
||||
})
|
||||
.catch((errorDetails: Error) => {
|
||||
setLoading(false);
|
||||
setError(errorDetails.message);
|
||||
});
|
||||
return router.push(`/verify?` + new URLSearchParams(params));
|
||||
}
|
||||
|
||||
const { errors } = formState;
|
||||
@@ -177,7 +162,7 @@ export default function SetPasswordForm({
|
||||
!formState.isValid ||
|
||||
watchPassword !== watchConfirmPassword
|
||||
}
|
||||
onClick={handleSubmit(submitAndLink)}
|
||||
onClick={handleSubmit(submitRegister)}
|
||||
>
|
||||
{loading && <Spinner className="h-5 w-5 mr-2" />}
|
||||
continue
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useForm } from "react-hook-form";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Spinner } from "./Spinner";
|
||||
import Alert from "@/ui/Alert";
|
||||
import { resendVerifyEmail, verifyUserByEmail } from "@/lib/server/email";
|
||||
|
||||
type Inputs = {
|
||||
code: string;
|
||||
@@ -50,71 +51,48 @@ export default function VerifyEmailForm({
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
async function submitCode(values: Inputs) {
|
||||
setLoading(true);
|
||||
const res = await fetch("/api/verifyemail", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
code: values.code,
|
||||
userId,
|
||||
organization,
|
||||
}),
|
||||
});
|
||||
|
||||
const response = await res.json();
|
||||
|
||||
setLoading(false);
|
||||
if (!res.ok) {
|
||||
setError(response.rawMessage);
|
||||
return Promise.reject(response);
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
async function resendCode() {
|
||||
setLoading(true);
|
||||
const res = await fetch("/api/resendverifyemail", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
userId,
|
||||
}),
|
||||
const response = await resendVerifyEmail({
|
||||
userId,
|
||||
}).catch((error: Error) => {
|
||||
setLoading(false);
|
||||
setError(error.message);
|
||||
});
|
||||
|
||||
const response = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
setLoading(false);
|
||||
setError(response.details);
|
||||
return Promise.reject(response);
|
||||
} else {
|
||||
setLoading(false);
|
||||
return response;
|
||||
}
|
||||
setLoading(false);
|
||||
return response;
|
||||
}
|
||||
|
||||
function submitCodeAndContinue(value: Inputs): Promise<boolean | void> {
|
||||
return submitCode(value).then((resp: any) => {
|
||||
const params = new URLSearchParams({});
|
||||
|
||||
if (organization) {
|
||||
params.set("organization", organization);
|
||||
}
|
||||
|
||||
if (authRequestId && sessionId) {
|
||||
params.set("authRequest", authRequestId);
|
||||
params.set("sessionId", sessionId);
|
||||
return router.push(`/login?` + params);
|
||||
} else {
|
||||
return router.push(`/loginname?` + params);
|
||||
}
|
||||
async function submitCodeAndContinue(value: Inputs): Promise<boolean | void> {
|
||||
setLoading(true);
|
||||
const verifyResponse = await verifyUserByEmail({
|
||||
code: value.code,
|
||||
userId,
|
||||
}).catch((error: Error) => {
|
||||
setLoading(false);
|
||||
setError(error.message);
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
|
||||
if (!verifyResponse) {
|
||||
setError("Could not verify email");
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({});
|
||||
|
||||
if (organization) {
|
||||
params.set("organization", organization);
|
||||
}
|
||||
|
||||
if (authRequestId && sessionId) {
|
||||
params.set("authRequest", authRequestId);
|
||||
params.set("sessionId", sessionId);
|
||||
return router.push(`/login?` + params);
|
||||
} else {
|
||||
return router.push(`/loginname?` + params);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user