mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-09 12:27:43 +00:00
fix: rename forgetPassword APIs to requestPasswordReset (#2947)
* fix: rename to * Update demo/nextjs/app/(auth)/forget-password/page.tsx Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
@@ -29,7 +29,7 @@ export default function Component() {
|
|||||||
setError("");
|
setError("");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await client.forgetPassword({
|
await client.requestPasswordReset({
|
||||||
email,
|
email,
|
||||||
redirectTo: "/reset-password",
|
redirectTo: "/reset-password",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export default function CodeTabs() {
|
|||||||
? `emailAndPassword: {
|
? `emailAndPassword: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
${
|
${
|
||||||
options.forgetPassword
|
options.requestPasswordReset
|
||||||
? `async sendResetPassword(data, request) {
|
? `async sendResetPassword(data, request) {
|
||||||
// Send an email to the user with a link to reset their password
|
// Send an email to the user with a link to reset their password
|
||||||
},`
|
},`
|
||||||
|
|||||||
@@ -372,11 +372,11 @@ export function Builder() {
|
|||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
id="email-provider-forget-password"
|
id="email-provider-forget-password"
|
||||||
checked={options.forgetPassword}
|
checked={options.requestPasswordReset}
|
||||||
onCheckedChange={(checked) => {
|
onCheckedChange={(checked) => {
|
||||||
setOptions((prev) => ({
|
setOptions((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
forgetPassword: checked,
|
requestPasswordReset: checked,
|
||||||
}));
|
}));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export default function SignIn() {
|
|||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Label htmlFor="password">Password</Label>
|
<Label htmlFor="password">Password</Label>
|
||||||
{options.forgetPassword && (
|
{options.requestPasswordReset && (
|
||||||
<Link
|
<Link
|
||||||
href="#"
|
href="#"
|
||||||
className="ml-auto inline-block text-sm underline"
|
className="ml-auto inline-block text-sm underline"
|
||||||
@@ -208,7 +208,7 @@ export default function SignIn() {
|
|||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Label htmlFor="password">Password</Label>
|
<Label htmlFor="password">Password</Label>
|
||||||
${
|
${
|
||||||
options.forgetPassword
|
options.requestPasswordReset
|
||||||
? `<Link
|
? `<Link
|
||||||
href="#"
|
href="#"
|
||||||
className="ml-auto inline-block text-sm underline"
|
className="ml-auto inline-block text-sm underline"
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ export const optionsAtom = atom({
|
|||||||
signUp: true,
|
signUp: true,
|
||||||
label: true,
|
label: true,
|
||||||
rememberMe: true,
|
rememberMe: true,
|
||||||
forgetPassword: true,
|
requestPasswordReset: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ await authClient.sendVerificationEmail({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### Forget Password
|
### Request Password Reset
|
||||||
|
|
||||||
To allow users to reset a password first you need to provide `sendResetPassword` function to the email and password authenticator. The `sendResetPassword` function takes a data object with the following properties:
|
To allow users to reset a password first you need to provide `sendResetPassword` function to the email and password authenticator. The `sendResetPassword` function takes a data object with the following properties:
|
||||||
|
|
||||||
@@ -195,7 +195,7 @@ export const auth = betterAuth({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Once you configured your server you can call `forgetPassword` function to send reset password link to user. If the user exists, it will trigger the `sendResetPassword` function you provided in the auth config.
|
Once you configured your server you can call `requestPasswordReset` function to send reset password link to user. If the user exists, it will trigger the `sendResetPassword` function you provided in the auth config.
|
||||||
|
|
||||||
It takes an object with the following properties:
|
It takes an object with the following properties:
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ It takes an object with the following properties:
|
|||||||
- `redirectTo`: The URL to redirect to after the user clicks on the link in the email. If the token is valid, the user will be redirected to this URL with the token in the query string. If the token is invalid, the user will be redirected to this URL with an error message in the query string `?error=invalid_token`.
|
- `redirectTo`: The URL to redirect to after the user clicks on the link in the email. If the token is valid, the user will be redirected to this URL with the token in the query string. If the token is invalid, the user will be redirected to this URL with an error message in the query string `?error=invalid_token`.
|
||||||
|
|
||||||
```ts title="auth-client.ts"
|
```ts title="auth-client.ts"
|
||||||
const { data, error } = await authClient.forgetPassword({
|
const { data, error } = await authClient.requestPasswordReset({
|
||||||
email: "test@example.com",
|
email: "test@example.com",
|
||||||
redirectTo: "/reset-password",
|
redirectTo: "/reset-password",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -159,12 +159,12 @@ const isVerified = await authClient.phoneNumber.verify({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
### Forget Password
|
### Request Password Reset
|
||||||
|
|
||||||
To initiate a forget password flow using `phoneNumber`, you can start by calling `forgetPassword` on the client to send an OTP code to the user's phone number.
|
To initiate a request password reset flow using `phoneNumber`, you can start by calling `requestPasswordReset` on the client to send an OTP code to the user's phone number.
|
||||||
|
|
||||||
```ts title="auth-client.ts"
|
```ts title="auth-client.ts"
|
||||||
await authClient.phoneNumber.forgetPassword({
|
await authClient.phoneNumber.requestPasswordReset({
|
||||||
phoneNumber: "+1234567890"
|
phoneNumber: "+1234567890"
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
@@ -199,6 +199,7 @@ export const auth = betterAuth({
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
- `sendPasswordResetOTP`: A function that sends the OTP code to the user's phone number for password reset. It takes the phone number and the OTP code as arguments.
|
||||||
- `phoneNumberValidator`: A custom function to validate the phone number. It takes the phone number as an argument and returns a boolean indicating whether the phone number is valid.
|
- `phoneNumberValidator`: A custom function to validate the phone number. It takes the phone number as an argument and returns a boolean indicating whether the phone number is valid.
|
||||||
- `signUpOnVerification`: An object with the following properties:
|
- `signUpOnVerification`: An object with the following properties:
|
||||||
- `getTempEmail`: A function that generates a temporary email for the user. It takes the phone number as an argument and returns the temporary email.
|
- `getTempEmail`: A function that generates a temporary email for the user. It takes the phone number as an argument and returns the temporary email.
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { View } from "react-native";
|
|||||||
import Icons from "@expo/vector-icons/AntDesign";
|
import Icons from "@expo/vector-icons/AntDesign";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
|
|
||||||
export default function ForgetPassword() {
|
export default function RequestPasswordReset() {
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
return (
|
return (
|
||||||
<Card className="w-10/12 ">
|
<Card className="w-10/12 ">
|
||||||
@@ -36,7 +36,7 @@ export default function ForgetPassword() {
|
|||||||
<View className="w-full gap-2">
|
<View className="w-full gap-2">
|
||||||
<Button
|
<Button
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
authClient.forgetPassword({
|
authClient.requestPasswordReset({
|
||||||
email,
|
email,
|
||||||
redirectTo: "/reset-password",
|
redirectTo: "/reset-password",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ export const {
|
|||||||
signOut,
|
signOut,
|
||||||
signUp,
|
signUp,
|
||||||
useSession,
|
useSession,
|
||||||
forgetPassword,
|
requestPasswordReset,
|
||||||
resetPassword,
|
resetPassword,
|
||||||
} = authClient;
|
} = authClient;
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { forgetPassword } from "~/lib/auth-client.js";
|
import { requestPasswordReset } from "~/lib/auth-client.js";
|
||||||
|
|
||||||
const email = ref("");
|
const email = ref("");
|
||||||
|
|
||||||
const handleForgetPassword = async () => {
|
const handleRequestPasswordReset = async () => {
|
||||||
if (!email.value) {
|
if (!email.value) {
|
||||||
alert("Please enter your email address");
|
alert("Please enter your email address");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await forgetPassword(
|
await requestPasswordReset(
|
||||||
{
|
{
|
||||||
email: email.value,
|
email: email.value,
|
||||||
redirectTo: "/reset-password",
|
redirectTo: "/reset-password",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// onSuccess find the url with token in server console. For detail check forgetPassword section: https://www.better-auth.com/docs/authentication/email-password
|
// onSuccess find the url with token in server console. For detail check requestPasswordReset section: https://www.better-auth.com/docs/authentication/email-password
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
alert("Password reset link sent to your email");
|
alert("Password reset link sent to your email");
|
||||||
window.location.href = "/sign-in";
|
window.location.href = "/sign-in";
|
||||||
@@ -43,7 +43,7 @@ const handleForgetPassword = async () => {
|
|||||||
<Label for="email">Email</Label>
|
<Label for="email">Email</Label>
|
||||||
<Input id="email" type="email" placeholder="m@example.com" required v-model="email" />
|
<Input id="email" type="email" placeholder="m@example.com" required v-model="email" />
|
||||||
</div>
|
</div>
|
||||||
<Button type="button" class="w-full" @click="handleForgetPassword">
|
<Button type="button" class="w-full" @click="handleRequestPasswordReset">
|
||||||
Reset Password
|
Reset Password
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ import {
|
|||||||
refreshToken,
|
refreshToken,
|
||||||
getAccessToken,
|
getAccessToken,
|
||||||
accountInfo,
|
accountInfo,
|
||||||
|
requestPasswordReset,
|
||||||
|
requestPasswordResetCallback,
|
||||||
} from "./routes";
|
} from "./routes";
|
||||||
import { ok } from "./routes/ok";
|
import { ok } from "./routes/ok";
|
||||||
import { signUpEmail } from "./routes/sign-up";
|
import { signUpEmail } from "./routes/sign-up";
|
||||||
@@ -105,6 +107,8 @@ export function getEndpoints<
|
|||||||
updateUser: updateUser<Option>(),
|
updateUser: updateUser<Option>(),
|
||||||
deleteUser,
|
deleteUser,
|
||||||
forgetPasswordCallback,
|
forgetPasswordCallback,
|
||||||
|
requestPasswordReset,
|
||||||
|
requestPasswordResetCallback,
|
||||||
listSessions: listSessions<Option>(),
|
listSessions: listSessions<Option>(),
|
||||||
revokeSession,
|
revokeSession,
|
||||||
revokeSessions,
|
revokeSessions,
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ describe("Origin Check", async (it) => {
|
|||||||
customFetchImpl,
|
customFetchImpl,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const res = await client.forgetPassword({
|
const res = await client.requestPasswordReset({
|
||||||
email: testUser.email,
|
email: testUser.email,
|
||||||
redirectTo: "http://malicious.com",
|
redirectTo: "http://malicious.com",
|
||||||
});
|
});
|
||||||
@@ -235,7 +235,7 @@ describe("Origin Check", async (it) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const res = await client.forgetPassword({
|
const res = await client.requestPasswordReset({
|
||||||
email: testUser.email,
|
email: testUser.email,
|
||||||
redirectTo: "http://localhost:5000/reset-password",
|
redirectTo: "http://localhost:5000/reset-password",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ export * from "./sign-in";
|
|||||||
export * from "./callback";
|
export * from "./callback";
|
||||||
export * from "./session";
|
export * from "./session";
|
||||||
export * from "./sign-out";
|
export * from "./sign-out";
|
||||||
export * from "./forget-password";
|
export * from "./reset-password";
|
||||||
export * from "./email-verification";
|
export * from "./email-verification";
|
||||||
export * from "./update-user";
|
export * from "./update-user";
|
||||||
export * from "./error";
|
export * from "./error";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ describe("forget password", async (it) => {
|
|||||||
const mockSendEmail = vi.fn();
|
const mockSendEmail = vi.fn();
|
||||||
let token = "";
|
let token = "";
|
||||||
|
|
||||||
const { client, testUser } = await getTestInstance(
|
const { client, testUser, auth } = await getTestInstance(
|
||||||
{
|
{
|
||||||
emailAndPassword: {
|
emailAndPassword: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -20,7 +20,7 @@ describe("forget password", async (it) => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
it("should send a reset password email when enabled", async () => {
|
it("should send a reset password email when enabled", async () => {
|
||||||
await client.forgetPassword({
|
await client.requestPasswordReset({
|
||||||
email: testUser.email,
|
email: testUser.email,
|
||||||
redirectTo: "http://localhost:3000",
|
redirectTo: "http://localhost:3000",
|
||||||
});
|
});
|
||||||
@@ -99,7 +99,7 @@ describe("forget password", async (it) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { headers } = await signInWithTestUser();
|
const { headers } = await signInWithTestUser();
|
||||||
await client.forgetPassword({
|
await client.requestPasswordReset({
|
||||||
email: testUser.email,
|
email: testUser.email,
|
||||||
redirectTo: "/sign-in",
|
redirectTo: "/sign-in",
|
||||||
fetchOptions: {
|
fetchOptions: {
|
||||||
@@ -126,7 +126,7 @@ describe("forget password", async (it) => {
|
|||||||
token,
|
token,
|
||||||
});
|
});
|
||||||
expect(res.data?.status).toBe(true);
|
expect(res.data?.status).toBe(true);
|
||||||
await client.forgetPassword({
|
await client.requestPasswordReset({
|
||||||
email: testUser.email,
|
email: testUser.email,
|
||||||
redirectTo: "/sign-in",
|
redirectTo: "/sign-in",
|
||||||
fetchOptions: {
|
fetchOptions: {
|
||||||
@@ -158,7 +158,7 @@ describe("forget password", async (it) => {
|
|||||||
|
|
||||||
const queryParams = "foo=bar&baz=qux";
|
const queryParams = "foo=bar&baz=qux";
|
||||||
const redirectTo = `http://localhost:3000?${queryParams}`;
|
const redirectTo = `http://localhost:3000?${queryParams}`;
|
||||||
const res = await client.forgetPassword({
|
const res = await client.requestPasswordReset({
|
||||||
email: testUser.email,
|
email: testUser.email,
|
||||||
redirectTo,
|
redirectTo,
|
||||||
});
|
});
|
||||||
@@ -192,7 +192,7 @@ describe("revoke sessions on password reset", async (it) => {
|
|||||||
it("should revoke other sessions when revokeSessionsOnPasswordReset is enabled", async () => {
|
it("should revoke other sessions when revokeSessionsOnPasswordReset is enabled", async () => {
|
||||||
const { headers } = await signInWithTestUser();
|
const { headers } = await signInWithTestUser();
|
||||||
|
|
||||||
await client.forgetPassword({
|
await client.requestPasswordReset({
|
||||||
email: testUser.email,
|
email: testUser.email,
|
||||||
redirectTo: "http://localhost:3000",
|
redirectTo: "http://localhost:3000",
|
||||||
});
|
});
|
||||||
@@ -234,7 +234,7 @@ describe("revoke sessions on password reset", async (it) => {
|
|||||||
|
|
||||||
const { headers } = await signInWithTestUser();
|
const { headers } = await signInWithTestUser();
|
||||||
|
|
||||||
await client.forgetPassword({
|
await client.requestPasswordReset({
|
||||||
email: testUser.email,
|
email: testUser.email,
|
||||||
redirectTo: "http://localhost:3000",
|
redirectTo: "http://localhost:3000",
|
||||||
});
|
});
|
||||||
@@ -31,6 +31,116 @@ function redirectCallback(
|
|||||||
return url.href;
|
return url.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const requestPasswordReset = createAuthEndpoint(
|
||||||
|
"/request-password-reset",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
body: z.object({
|
||||||
|
/**
|
||||||
|
* The email address of the user to send a password reset email to.
|
||||||
|
*/
|
||||||
|
email: z
|
||||||
|
.string({
|
||||||
|
description:
|
||||||
|
"The email address of the user to send a password reset email to",
|
||||||
|
})
|
||||||
|
.email(),
|
||||||
|
/**
|
||||||
|
* The URL to redirect the user to reset their password.
|
||||||
|
* If the token isn't valid or expired, it'll be redirected with a query parameter `?
|
||||||
|
* error=INVALID_TOKEN`. If the token is valid, it'll be redirected with a query parameter `?
|
||||||
|
* token=VALID_TOKEN
|
||||||
|
*/
|
||||||
|
redirectTo: z
|
||||||
|
.string({
|
||||||
|
description:
|
||||||
|
"The URL to redirect the user to reset their password. If the token isn't valid or expired, it'll be redirected with a query parameter `?error=INVALID_TOKEN`. If the token is valid, it'll be redirected with a query parameter `?token=VALID_TOKEN",
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
}),
|
||||||
|
metadata: {
|
||||||
|
openapi: {
|
||||||
|
description: "Send a password reset email to the user",
|
||||||
|
responses: {
|
||||||
|
"200": {
|
||||||
|
description: "Success",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
status: {
|
||||||
|
type: "boolean",
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async (ctx) => {
|
||||||
|
if (!ctx.context.options.emailAndPassword?.sendResetPassword) {
|
||||||
|
ctx.context.logger.error(
|
||||||
|
"Reset password isn't enabled.Please pass an emailAndPassword.sendResetPassword function in your auth config!",
|
||||||
|
);
|
||||||
|
throw new APIError("BAD_REQUEST", {
|
||||||
|
message: "Reset password isn't enabled",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const { email, redirectTo } = ctx.body;
|
||||||
|
|
||||||
|
const user = await ctx.context.internalAdapter.findUserByEmail(email, {
|
||||||
|
includeAccounts: true,
|
||||||
|
});
|
||||||
|
if (!user) {
|
||||||
|
ctx.context.logger.error("Reset Password: User not found", { email });
|
||||||
|
return ctx.json({
|
||||||
|
status: true,
|
||||||
|
message:
|
||||||
|
"If this email exists in our system, check your email for the reset link",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const defaultExpiresIn = 60 * 60 * 1;
|
||||||
|
const expiresAt = getDate(
|
||||||
|
ctx.context.options.emailAndPassword.resetPasswordTokenExpiresIn ||
|
||||||
|
defaultExpiresIn,
|
||||||
|
"sec",
|
||||||
|
);
|
||||||
|
const verificationToken = generateId(24);
|
||||||
|
await ctx.context.internalAdapter.createVerificationValue(
|
||||||
|
{
|
||||||
|
value: user.user.id,
|
||||||
|
identifier: `reset-password:${verificationToken}`,
|
||||||
|
expiresAt,
|
||||||
|
},
|
||||||
|
ctx,
|
||||||
|
);
|
||||||
|
const callbackURL = redirectTo ? encodeURIComponent(redirectTo) : "";
|
||||||
|
const url = `${ctx.context.baseURL}/reset-password/${verificationToken}?callbackURL=${callbackURL}`;
|
||||||
|
await ctx.context.options.emailAndPassword.sendResetPassword(
|
||||||
|
{
|
||||||
|
user: user.user,
|
||||||
|
url,
|
||||||
|
token: verificationToken,
|
||||||
|
},
|
||||||
|
ctx.request,
|
||||||
|
);
|
||||||
|
return ctx.json({
|
||||||
|
status: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use requestPasswordReset instead. This endpoint will be removed in the next major
|
||||||
|
* version.
|
||||||
|
*/
|
||||||
export const forgetPassword = createAuthEndpoint(
|
export const forgetPassword = createAuthEndpoint(
|
||||||
"/forget-password",
|
"/forget-password",
|
||||||
{
|
{
|
||||||
@@ -137,7 +247,7 @@ export const forgetPassword = createAuthEndpoint(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export const forgetPasswordCallback = createAuthEndpoint(
|
export const requestPasswordResetCallback = createAuthEndpoint(
|
||||||
"/reset-password/:token",
|
"/reset-password/:token",
|
||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@@ -192,6 +302,11 @@ export const forgetPasswordCallback = createAuthEndpoint(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use requestPasswordResetCallback instead
|
||||||
|
*/
|
||||||
|
export const forgetPasswordCallback = requestPasswordResetCallback;
|
||||||
|
|
||||||
export const resetPassword = createAuthEndpoint(
|
export const resetPassword = createAuthEndpoint(
|
||||||
"/reset-password",
|
"/reset-password",
|
||||||
{
|
{
|
||||||
@@ -48,6 +48,18 @@ export interface PhoneNumberOptions {
|
|||||||
* @param request - the request object
|
* @param request - the request object
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
|
sendPasswordResetOTP?: (
|
||||||
|
data: { phoneNumber: string; code: string },
|
||||||
|
request?: Request,
|
||||||
|
) => Promise<void> | void;
|
||||||
|
/**
|
||||||
|
* a callback to send otp on user requesting to reset their password
|
||||||
|
*
|
||||||
|
* @param data - contains phone number and code
|
||||||
|
* @param request - the request object
|
||||||
|
* @returns
|
||||||
|
* @deprecated Use sendPasswordResetOTP instead. This function will be removed in the next major version.
|
||||||
|
*/
|
||||||
sendForgetPasswordOTP?: (
|
sendForgetPasswordOTP?: (
|
||||||
data: { phoneNumber: string; code: string },
|
data: { phoneNumber: string; code: string },
|
||||||
request?: Request,
|
request?: Request,
|
||||||
@@ -693,6 +705,9 @@ export const phoneNumber = (options?: PhoneNumberOptions) => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
/**
|
||||||
|
* @deprecated Use requestPasswordResetPhoneNumber instead. This endpoint will be removed in the next major version.
|
||||||
|
*/
|
||||||
forgetPasswordPhoneNumber: createAuthEndpoint(
|
forgetPasswordPhoneNumber: createAuthEndpoint(
|
||||||
"/phone-number/forget-password",
|
"/phone-number/forget-password",
|
||||||
{
|
{
|
||||||
@@ -746,7 +761,7 @@ export const phoneNumber = (options?: PhoneNumberOptions) => {
|
|||||||
await ctx.context.internalAdapter.createVerificationValue(
|
await ctx.context.internalAdapter.createVerificationValue(
|
||||||
{
|
{
|
||||||
value: `${code}:0`,
|
value: `${code}:0`,
|
||||||
identifier: `${ctx.body.phoneNumber}-forget-password`,
|
identifier: `${ctx.body.phoneNumber}-request-password-reset`,
|
||||||
expiresAt: getDate(opts.expiresIn, "sec"),
|
expiresAt: getDate(opts.expiresIn, "sec"),
|
||||||
},
|
},
|
||||||
ctx,
|
ctx,
|
||||||
@@ -763,6 +778,76 @@ export const phoneNumber = (options?: PhoneNumberOptions) => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
requestPasswordResetPhoneNumber: createAuthEndpoint(
|
||||||
|
"/phone-number/request-password-reset",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
body: z.object({
|
||||||
|
phoneNumber: z.string(),
|
||||||
|
}),
|
||||||
|
metadata: {
|
||||||
|
openapi: {
|
||||||
|
description: "Request OTP for password reset via phone number",
|
||||||
|
responses: {
|
||||||
|
"200": {
|
||||||
|
description: "OTP sent successfully for password reset",
|
||||||
|
content: {
|
||||||
|
"application/json": {
|
||||||
|
schema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
status: {
|
||||||
|
type: "boolean",
|
||||||
|
description:
|
||||||
|
"Indicates if the OTP was sent successfully",
|
||||||
|
enum: [true],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["status"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async (ctx) => {
|
||||||
|
const user = await ctx.context.adapter.findOne<UserWithPhoneNumber>({
|
||||||
|
model: "user",
|
||||||
|
where: [
|
||||||
|
{
|
||||||
|
value: ctx.body.phoneNumber,
|
||||||
|
field: opts.phoneNumber,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (!user) {
|
||||||
|
throw new APIError("BAD_REQUEST", {
|
||||||
|
message: "phone number isn't registered",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const code = generateOTP(opts.otpLength);
|
||||||
|
await ctx.context.internalAdapter.createVerificationValue(
|
||||||
|
{
|
||||||
|
value: `${code}:0`,
|
||||||
|
identifier: `${ctx.body.phoneNumber}-request-password-reset`,
|
||||||
|
expiresAt: getDate(opts.expiresIn, "sec"),
|
||||||
|
},
|
||||||
|
ctx,
|
||||||
|
);
|
||||||
|
await options?.sendPasswordResetOTP?.(
|
||||||
|
{
|
||||||
|
phoneNumber: ctx.body.phoneNumber,
|
||||||
|
code,
|
||||||
|
},
|
||||||
|
ctx.request,
|
||||||
|
);
|
||||||
|
return ctx.json({
|
||||||
|
status: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
resetPasswordPhoneNumber: createAuthEndpoint(
|
resetPasswordPhoneNumber: createAuthEndpoint(
|
||||||
"/phone-number/reset-password",
|
"/phone-number/reset-password",
|
||||||
{
|
{
|
||||||
@@ -802,7 +887,7 @@ export const phoneNumber = (options?: PhoneNumberOptions) => {
|
|||||||
async (ctx) => {
|
async (ctx) => {
|
||||||
const verification =
|
const verification =
|
||||||
await ctx.context.internalAdapter.findVerificationValue(
|
await ctx.context.internalAdapter.findVerificationValue(
|
||||||
`${ctx.body.phoneNumber}-forget-password`,
|
`${ctx.body.phoneNumber}-request-password-reset`,
|
||||||
);
|
);
|
||||||
if (!verification) {
|
if (!verification) {
|
||||||
throw new APIError("BAD_REQUEST", {
|
throw new APIError("BAD_REQUEST", {
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ describe("reset password flow attempts", async (it) => {
|
|||||||
code: otp,
|
code: otp,
|
||||||
});
|
});
|
||||||
|
|
||||||
await client.phoneNumber.forgetPassword({
|
await client.phoneNumber.requestPasswordReset({
|
||||||
phoneNumber: testPhoneNumber,
|
phoneNumber: testPhoneNumber,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user