@rpcbase/auth 0.58.0 → 0.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/resend-otp/handler.d.ts +5 -0
- package/dist/api/resend-otp/handler.d.ts.map +1 -0
- package/dist/api/resend-otp/index.d.ts +12 -0
- package/dist/api/resend-otp/index.d.ts.map +1 -0
- package/dist/components/SignInForm/index.d.ts.map +1 -1
- package/dist/components/SignUpForm/index.d.ts.map +1 -1
- package/dist/handler-BxA7Jhrs.js +51 -0
- package/dist/index.js +53 -25
- package/dist/routes.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/api/resend-otp/handler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAmB,MAAM,cAAc,CAAA;AAInD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;yBAqDlC,KAAK,GAAG,CAAC,eAAe,CAAC;AAAzC,wBAEC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from '../../../../vite/node_modules/zod';
|
|
2
|
+
export declare const Route = "/api/rb/auth/resend-otp";
|
|
3
|
+
export declare const requestSchema: z.ZodObject<{
|
|
4
|
+
email: z.ZodString;
|
|
5
|
+
}, z.core.$strip>;
|
|
6
|
+
export type RequestPayload = z.infer<typeof requestSchema>;
|
|
7
|
+
export declare const responseSchema: z.ZodObject<{
|
|
8
|
+
success: z.ZodBoolean;
|
|
9
|
+
error: z.ZodOptional<z.ZodString>;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
export type ResponsePayload = z.infer<typeof responseSchema>;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/api/resend-otp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,KAAK,4BAA4B,CAAA;AAE9C,eAAO,MAAM,aAAa;;iBAExB,CAAA;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAA;AAE1D,eAAO,MAAM,cAAc;;;iBAGzB,CAAA;AAEF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SignInForm/index.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SignInForm/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AA6CjC,eAAO,MAAM,UAAU,GAAI,yBAGxB;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,4CAuDA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SignUpForm/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SignUpForm/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAW,MAAM,OAAO,CAAA;AAYzC,eAAO,MAAM,UAAU,GAAI,sCAIxB;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,4CAuEA,CAAA"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
2
|
+
import { loadModel } from "@rpcbase/db";
|
|
3
|
+
import { sendEmail } from "@rpcbase/server";
|
|
4
|
+
import { o as object, s as string, b as boolean } from "./schemas-KL7REOdt.js";
|
|
5
|
+
const Route = "/api/rb/auth/resend-otp";
|
|
6
|
+
const requestSchema = object({
|
|
7
|
+
email: string().email()
|
|
8
|
+
});
|
|
9
|
+
object({
|
|
10
|
+
success: boolean(),
|
|
11
|
+
error: string().optional()
|
|
12
|
+
});
|
|
13
|
+
const resendOtp = async (payload, ctx) => {
|
|
14
|
+
const User = await loadModel("RBUser", ctx);
|
|
15
|
+
const parsed = requestSchema.safeParse(payload);
|
|
16
|
+
if (!parsed.success) {
|
|
17
|
+
ctx.res.status(400);
|
|
18
|
+
return { success: false, error: "invalid_payload" };
|
|
19
|
+
}
|
|
20
|
+
const { email } = parsed.data;
|
|
21
|
+
const user = await User.findOne({ email });
|
|
22
|
+
if (!user) {
|
|
23
|
+
ctx.res.status(404);
|
|
24
|
+
return { success: false, error: "user_not_found" };
|
|
25
|
+
}
|
|
26
|
+
const emailVerificationCode = crypto.randomInt(0, 1e6).toString().padStart(6, "0");
|
|
27
|
+
const emailVerificationExpiresAt = new Date(Date.now() + 10 * 60 * 1e3);
|
|
28
|
+
user.email_verification_code = emailVerificationCode;
|
|
29
|
+
user.email_verification_expires_at = emailVerificationExpiresAt;
|
|
30
|
+
await user.save();
|
|
31
|
+
try {
|
|
32
|
+
await sendEmail({
|
|
33
|
+
to: email,
|
|
34
|
+
subject: `Your verification code: ${emailVerificationCode}`,
|
|
35
|
+
html: `
|
|
36
|
+
<p>Your verification code is <strong>${emailVerificationCode}</strong>. It expires in 10 minutes.</p>
|
|
37
|
+
<p>If you didn't request this, you can ignore this message.</p>
|
|
38
|
+
`,
|
|
39
|
+
text: `Your verification code is ${emailVerificationCode}. It expires in 10 minutes. If you didn't request this, you can ignore this message.`
|
|
40
|
+
});
|
|
41
|
+
} catch (err) {
|
|
42
|
+
console.warn("failed to resend otp email", err);
|
|
43
|
+
}
|
|
44
|
+
return { success: true };
|
|
45
|
+
};
|
|
46
|
+
const handler = (api) => {
|
|
47
|
+
api.post(Route, resendOtp);
|
|
48
|
+
};
|
|
49
|
+
export {
|
|
50
|
+
handler as default
|
|
51
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useLocation, Link, useNavigate } from "@rpcbase/router";
|
|
2
|
+
import { useLocation, Link, useNavigate, useSearchParams } from "@rpcbase/router";
|
|
3
3
|
import clsx from "clsx";
|
|
4
4
|
import { useFormContext, useForm, zodResolver, FormProvider } from "@rpcbase/form";
|
|
5
|
-
import * as React from "react";
|
|
6
|
-
import { useEffect, useState, useCallback, useMemo } from "react";
|
|
7
5
|
import { r as requestSchema } from "./index-Bdcryyvv.js";
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { useState, useEffect, useCallback, useMemo } from "react";
|
|
8
8
|
import { r as requestSchema$1 } from "./index-DwX0Y2YV.js";
|
|
9
9
|
import { b, a, r } from "./middleware-BiMXO6Dq.js";
|
|
10
10
|
const LINKS_REDIRECTION_MAP = {
|
|
@@ -108,10 +108,40 @@ const EmailInput = ({
|
|
|
108
108
|
errorMessage ? /* @__PURE__ */ jsx("p", { className: "mt-1 -mb-2 text-sm/6 text-red-500", children: errorMessage }) : null
|
|
109
109
|
] });
|
|
110
110
|
};
|
|
111
|
+
const hasUnsafeNextPathChars = (value) => {
|
|
112
|
+
for (let i = 0; i < value.length; i++) {
|
|
113
|
+
const code = value.charCodeAt(i);
|
|
114
|
+
if (code === 92 || code <= 31 || code === 127) return true;
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
};
|
|
118
|
+
const sanitizeNextPath = (raw) => {
|
|
119
|
+
if (!raw) return null;
|
|
120
|
+
const nextPath = raw.trim();
|
|
121
|
+
if (!nextPath) return null;
|
|
122
|
+
if (!nextPath.startsWith("/") || nextPath.startsWith("//")) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
if (hasUnsafeNextPathChars(nextPath)) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
const base = new URL("http://localhost");
|
|
130
|
+
const url = new URL(nextPath, base);
|
|
131
|
+
if (url.origin !== base.origin) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
return `${url.pathname}${url.search}${url.hash}`;
|
|
135
|
+
} catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
111
139
|
const SignInForm = ({
|
|
112
140
|
children,
|
|
113
141
|
className
|
|
114
142
|
}) => {
|
|
143
|
+
const navigate = useNavigate();
|
|
144
|
+
const [searchParams] = useSearchParams();
|
|
115
145
|
const methods = useForm({
|
|
116
146
|
defaultValues: {
|
|
117
147
|
email: "",
|
|
@@ -121,28 +151,32 @@ const SignInForm = ({
|
|
|
121
151
|
resolver: zodResolver(requestSchema)
|
|
122
152
|
});
|
|
123
153
|
const onSubmit = async (data) => {
|
|
124
|
-
|
|
154
|
+
methods.clearErrors("root");
|
|
125
155
|
try {
|
|
126
156
|
const res = await fetch("/api/rb/auth/sign-in", {
|
|
127
157
|
method: "POST",
|
|
128
158
|
headers: {
|
|
129
159
|
"Content-Type": "application/json"
|
|
130
160
|
},
|
|
131
|
-
body: JSON.stringify(data)
|
|
161
|
+
body: JSON.stringify(data),
|
|
162
|
+
credentials: "include"
|
|
132
163
|
});
|
|
133
|
-
const json = await res.json();
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
164
|
+
const json = await res.json().catch(() => null);
|
|
165
|
+
if (!res.ok || !json?.success) {
|
|
166
|
+
const message = json?.error === "invalid_credentials" ? "Invalid email or password." : "Sign-in failed. Please try again.";
|
|
167
|
+
methods.setError("root", { type: "server", message });
|
|
168
|
+
return;
|
|
137
169
|
}
|
|
170
|
+
const nextPath = sanitizeNextPath(searchParams.get("next")) ?? "/";
|
|
171
|
+
navigate(nextPath, { replace: true });
|
|
138
172
|
} catch (err) {
|
|
139
|
-
|
|
173
|
+
methods.setError("root", { type: "server", message: "Network error. Please try again." });
|
|
140
174
|
}
|
|
141
175
|
};
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
176
|
+
return /* @__PURE__ */ jsx(FormProvider, { ...methods, children: /* @__PURE__ */ jsxs("form", { method: "post", noValidate: true, className, onSubmit: methods.handleSubmit(onSubmit), children: [
|
|
177
|
+
children,
|
|
178
|
+
methods.formState.errors.root?.message && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-red-600", role: "alert", children: methods.formState.errors.root.message })
|
|
179
|
+
] }) });
|
|
146
180
|
};
|
|
147
181
|
const SignUpForm = ({
|
|
148
182
|
children,
|
|
@@ -162,19 +196,17 @@ const SignUpForm = ({
|
|
|
162
196
|
const onSubmit = async (data) => {
|
|
163
197
|
setServerMessage(null);
|
|
164
198
|
methods.clearErrors("root");
|
|
165
|
-
console.log("SUBMIT SIGNUp", data);
|
|
166
199
|
try {
|
|
167
200
|
const res = await fetch("/api/rb/auth/sign-up", {
|
|
168
201
|
method: "POST",
|
|
169
202
|
headers: {
|
|
170
203
|
"Content-Type": "application/json"
|
|
171
204
|
},
|
|
172
|
-
body: JSON.stringify(data)
|
|
205
|
+
body: JSON.stringify(data),
|
|
206
|
+
credentials: "include"
|
|
173
207
|
});
|
|
174
208
|
const json = await res.json();
|
|
175
|
-
console.log("SIGN UP RESPONSE", json);
|
|
176
209
|
if (!res.ok) {
|
|
177
|
-
console.error("Sign-up failed", json);
|
|
178
210
|
const message = json.error === "user_exists" ? "An account already exists with this email." : "Sign-up failed. Please try again.";
|
|
179
211
|
methods.setError("root", { type: "server", message });
|
|
180
212
|
return;
|
|
@@ -190,13 +222,9 @@ const SignUpForm = ({
|
|
|
190
222
|
navigate(`/auth/sign-up-otp?${search.toString()}`);
|
|
191
223
|
setServerMessage("Account created. Check your inbox to verify your email.");
|
|
192
224
|
} catch (err) {
|
|
193
|
-
console.error("Sign-up request error", err);
|
|
194
225
|
methods.setError("root", { type: "server", message: "Network error. Please try again." });
|
|
195
226
|
}
|
|
196
227
|
};
|
|
197
|
-
useEffect(() => {
|
|
198
|
-
console.log(methods.formState.errors);
|
|
199
|
-
}, [methods.formState.errors]);
|
|
200
228
|
return /* @__PURE__ */ jsx(FormProvider, { ...methods, children: /* @__PURE__ */ jsxs("form", { method: "post", noValidate: true, className, onSubmit: methods.handleSubmit(onSubmit), children: [
|
|
201
229
|
children,
|
|
202
230
|
methods.formState.errors.root?.message && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-red-600", role: "alert", children: methods.formState.errors.root.message }),
|
|
@@ -326,13 +354,13 @@ const useResendCountdown = (seconds = 60) => {
|
|
|
326
354
|
setRemaining((prev) => {
|
|
327
355
|
if (prev <= 1) {
|
|
328
356
|
setIsCountingDown(false);
|
|
329
|
-
return
|
|
357
|
+
return seconds;
|
|
330
358
|
}
|
|
331
359
|
return prev - 1;
|
|
332
360
|
});
|
|
333
361
|
}, 1e3);
|
|
334
362
|
return () => clearInterval(timer);
|
|
335
|
-
}, [isCountingDown]);
|
|
363
|
+
}, [isCountingDown, seconds]);
|
|
336
364
|
const restart = useCallback((nextSeconds) => {
|
|
337
365
|
const value = typeof nextSeconds === "number" ? nextSeconds : seconds;
|
|
338
366
|
setRemaining(value);
|
|
@@ -343,7 +371,7 @@ const useResendCountdown = (seconds = 60) => {
|
|
|
343
371
|
const secs = remaining % 60;
|
|
344
372
|
return `${minutes}:${secs.toString().padStart(2, "0")}`;
|
|
345
373
|
}, [remaining]);
|
|
346
|
-
const canResend = !isCountingDown
|
|
374
|
+
const canResend = !isCountingDown;
|
|
347
375
|
return { remaining, formatted, isCountingDown, canResend, restart };
|
|
348
376
|
};
|
|
349
377
|
const ResendCodeButton = ({
|
package/dist/routes.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const routes = Object.entries({
|
|
2
|
-
.../* @__PURE__ */ Object.assign({ "./api/me/handler.ts": () => import("./handler-C9aNvw-Q.js"), "./api/sign-in/handler.ts": () => import("./handler-ByODvDJo.js"), "./api/sign-out/handler.ts": () => import("./handler-CNHucHrj.js"), "./api/sign-up/handler.ts": () => import("./handler-DgTUP3cD.js"), "./api/verify-otp/handler.ts": () => import("./handler-49961uAb.js") })
|
|
2
|
+
.../* @__PURE__ */ Object.assign({ "./api/me/handler.ts": () => import("./handler-C9aNvw-Q.js"), "./api/resend-otp/handler.ts": () => import("./handler-BxA7Jhrs.js"), "./api/sign-in/handler.ts": () => import("./handler-ByODvDJo.js"), "./api/sign-out/handler.ts": () => import("./handler-CNHucHrj.js"), "./api/sign-up/handler.ts": () => import("./handler-DgTUP3cD.js"), "./api/verify-otp/handler.ts": () => import("./handler-49961uAb.js") })
|
|
3
3
|
}).reduce((acc, [path, mod]) => {
|
|
4
4
|
acc[path.replace("./api/", "@rpcbase/auth/api/")] = mod;
|
|
5
5
|
return acc;
|