@lalternative/auth 0.4.0 → 0.4.2
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/README.md +3 -2
- package/dist/chunk-73BHM4PZ.js +72 -0
- package/dist/chunk-73BHM4PZ.js.map +1 -0
- package/dist/client.d.ts +15 -0
- package/dist/client.js +13 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +81 -0
- package/dist/index.js +778 -0
- package/dist/index.js.map +1 -0
- package/dist/invitation-CzfLna7q.d.ts +82 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.js +132 -0
- package/dist/server.js.map +1 -0
- package/dist/types-DxllDyOa.d.ts +196 -0
- package/package.json +3 -2
package/dist/index.js
ADDED
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isInvitationFailure
|
|
3
|
+
} from "./chunk-73BHM4PZ.js";
|
|
4
|
+
|
|
5
|
+
// src/hooks/use-session.ts
|
|
6
|
+
function useSession(authClient) {
|
|
7
|
+
return authClient.useSession();
|
|
8
|
+
}
|
|
9
|
+
function useLogout(authClient) {
|
|
10
|
+
const signOut = async () => {
|
|
11
|
+
await authClient.signOut();
|
|
12
|
+
};
|
|
13
|
+
return signOut;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/components/login-form.tsx
|
|
17
|
+
import { useState } from "react";
|
|
18
|
+
|
|
19
|
+
// src/components/social-buttons.tsx
|
|
20
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
+
var LABELS = {
|
|
22
|
+
google: "Continue with Google",
|
|
23
|
+
github: "Continue with GitHub"
|
|
24
|
+
};
|
|
25
|
+
function SocialButtons({
|
|
26
|
+
providers,
|
|
27
|
+
onSelect,
|
|
28
|
+
disabled = false
|
|
29
|
+
}) {
|
|
30
|
+
if (providers.length === 0) return null;
|
|
31
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
32
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
33
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center", children: /* @__PURE__ */ jsx("div", { className: "w-full border-t border-input" }) }),
|
|
34
|
+
/* @__PURE__ */ jsx("div", { className: "relative flex justify-center", children: /* @__PURE__ */ jsx("span", { className: "bg-background px-2 text-xs uppercase tracking-wider text-muted-foreground", children: "or" }) })
|
|
35
|
+
] }),
|
|
36
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: providers.map((provider) => /* @__PURE__ */ jsx(
|
|
37
|
+
"button",
|
|
38
|
+
{
|
|
39
|
+
type: "button",
|
|
40
|
+
onClick: () => onSelect(provider),
|
|
41
|
+
disabled,
|
|
42
|
+
className: "inline-flex h-11 w-full items-center justify-center rounded-md border border-input bg-background px-4 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50",
|
|
43
|
+
children: LABELS[provider] ?? provider
|
|
44
|
+
},
|
|
45
|
+
provider
|
|
46
|
+
)) })
|
|
47
|
+
] });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/components/login-form.tsx
|
|
51
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
52
|
+
function oauthErrorMessage(code) {
|
|
53
|
+
switch (code) {
|
|
54
|
+
case "account_not_linked":
|
|
55
|
+
return "This email is already registered with a password. Sign in with your password instead.";
|
|
56
|
+
case "access_denied":
|
|
57
|
+
return "Sign-in was cancelled.";
|
|
58
|
+
default:
|
|
59
|
+
return "Sign-in failed. Please try again.";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function LoginForm({
|
|
63
|
+
onSuccess,
|
|
64
|
+
registerUrl = "/register",
|
|
65
|
+
forgotPasswordUrl = "/forgot-password",
|
|
66
|
+
socialCallbackUrl = "/",
|
|
67
|
+
socialProviders = [],
|
|
68
|
+
coreTokenUrl = "/api/auth/core-token",
|
|
69
|
+
authClient
|
|
70
|
+
}) {
|
|
71
|
+
const [email, setEmail] = useState("");
|
|
72
|
+
const [password, setPassword] = useState("");
|
|
73
|
+
const [error, setError] = useState();
|
|
74
|
+
const [isPending, setIsPending] = useState(false);
|
|
75
|
+
const handleSubmit = async (e) => {
|
|
76
|
+
e.preventDefault();
|
|
77
|
+
if (!email.trim()) {
|
|
78
|
+
setError("Please enter your email address");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (!password) {
|
|
82
|
+
setError("Please enter your password");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
setError(void 0);
|
|
86
|
+
setIsPending(true);
|
|
87
|
+
try {
|
|
88
|
+
const res = await authClient.signIn.email({
|
|
89
|
+
email: email.trim(),
|
|
90
|
+
password
|
|
91
|
+
});
|
|
92
|
+
if (res?.error) {
|
|
93
|
+
setError(res.error.message ?? "Invalid email or password");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (coreTokenUrl) {
|
|
97
|
+
await fetch(coreTokenUrl, { credentials: "include" });
|
|
98
|
+
}
|
|
99
|
+
onSuccess?.();
|
|
100
|
+
} catch (err) {
|
|
101
|
+
setError(err instanceof Error ? err.message : "Invalid email or password");
|
|
102
|
+
} finally {
|
|
103
|
+
setIsPending(false);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const handleSocial = async (provider) => {
|
|
107
|
+
setError(void 0);
|
|
108
|
+
try {
|
|
109
|
+
await authClient.signIn.social({
|
|
110
|
+
provider,
|
|
111
|
+
callbackURL: socialCallbackUrl
|
|
112
|
+
});
|
|
113
|
+
} catch (err) {
|
|
114
|
+
setError(
|
|
115
|
+
err instanceof Error ? oauthErrorMessage(err.message) : oauthErrorMessage("")
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
return /* @__PURE__ */ jsxs2("div", { className: "space-y-8", children: [
|
|
120
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
121
|
+
/* @__PURE__ */ jsx2("h1", { className: "text-2xl font-bold tracking-tight", children: "Sign in" }),
|
|
122
|
+
/* @__PURE__ */ jsx2("p", { className: "mt-1 text-sm text-muted-foreground", children: "Welcome back. Enter your credentials to continue." })
|
|
123
|
+
] }),
|
|
124
|
+
error && /* @__PURE__ */ jsx2("div", { className: "rounded-lg border border-destructive/20 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: error }),
|
|
125
|
+
/* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className: "space-y-4", noValidate: true, children: [
|
|
126
|
+
/* @__PURE__ */ jsx2(
|
|
127
|
+
"input",
|
|
128
|
+
{
|
|
129
|
+
type: "email",
|
|
130
|
+
value: email,
|
|
131
|
+
onChange: (e) => setEmail(e.target.value),
|
|
132
|
+
placeholder: "Email address",
|
|
133
|
+
required: true,
|
|
134
|
+
disabled: isPending,
|
|
135
|
+
autoComplete: "email",
|
|
136
|
+
className: "flex h-11 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed"
|
|
137
|
+
}
|
|
138
|
+
),
|
|
139
|
+
/* @__PURE__ */ jsxs2("div", { className: "space-y-1", children: [
|
|
140
|
+
/* @__PURE__ */ jsx2(
|
|
141
|
+
"input",
|
|
142
|
+
{
|
|
143
|
+
type: "password",
|
|
144
|
+
value: password,
|
|
145
|
+
onChange: (e) => setPassword(e.target.value),
|
|
146
|
+
placeholder: "Password",
|
|
147
|
+
required: true,
|
|
148
|
+
disabled: isPending,
|
|
149
|
+
autoComplete: "current-password",
|
|
150
|
+
className: "flex h-11 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed"
|
|
151
|
+
}
|
|
152
|
+
),
|
|
153
|
+
/* @__PURE__ */ jsx2("div", { className: "text-right", children: /* @__PURE__ */ jsx2(
|
|
154
|
+
"a",
|
|
155
|
+
{
|
|
156
|
+
href: forgotPasswordUrl,
|
|
157
|
+
className: "text-xs text-muted-foreground underline underline-offset-4 hover:text-foreground",
|
|
158
|
+
children: "Forgot your password?"
|
|
159
|
+
}
|
|
160
|
+
) })
|
|
161
|
+
] }),
|
|
162
|
+
/* @__PURE__ */ jsx2(
|
|
163
|
+
"button",
|
|
164
|
+
{
|
|
165
|
+
type: "submit",
|
|
166
|
+
disabled: isPending || !email.trim() || !password,
|
|
167
|
+
className: "inline-flex h-11 w-full items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50",
|
|
168
|
+
children: isPending ? "Signing in..." : "Sign in"
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
] }),
|
|
172
|
+
/* @__PURE__ */ jsx2(
|
|
173
|
+
SocialButtons,
|
|
174
|
+
{
|
|
175
|
+
providers: socialProviders,
|
|
176
|
+
onSelect: handleSocial,
|
|
177
|
+
disabled: isPending
|
|
178
|
+
}
|
|
179
|
+
),
|
|
180
|
+
/* @__PURE__ */ jsxs2("p", { className: "text-center text-sm text-muted-foreground", children: [
|
|
181
|
+
"Don't have an account?",
|
|
182
|
+
" ",
|
|
183
|
+
/* @__PURE__ */ jsx2(
|
|
184
|
+
"a",
|
|
185
|
+
{
|
|
186
|
+
href: registerUrl,
|
|
187
|
+
className: "font-medium text-foreground underline underline-offset-4 hover:text-foreground/80",
|
|
188
|
+
children: "Sign up"
|
|
189
|
+
}
|
|
190
|
+
)
|
|
191
|
+
] })
|
|
192
|
+
] });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// src/components/register-form.tsx
|
|
196
|
+
import { useState as useState2 } from "react";
|
|
197
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
198
|
+
var MIN_PASSWORD_LENGTH = 8;
|
|
199
|
+
function RegisterForm({
|
|
200
|
+
onSuccess,
|
|
201
|
+
loginUrl = "/login",
|
|
202
|
+
legal,
|
|
203
|
+
socialCallbackUrl = "/",
|
|
204
|
+
socialProviders = [],
|
|
205
|
+
authClient
|
|
206
|
+
}) {
|
|
207
|
+
const [name, setName] = useState2("");
|
|
208
|
+
const [email, setEmail] = useState2("");
|
|
209
|
+
const [password, setPassword] = useState2("");
|
|
210
|
+
const [error, setError] = useState2();
|
|
211
|
+
const [isPending, setIsPending] = useState2(false);
|
|
212
|
+
const handleSubmit = async (e) => {
|
|
213
|
+
e.preventDefault();
|
|
214
|
+
if (!name.trim()) {
|
|
215
|
+
setError("Please enter your name");
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (!email.trim()) {
|
|
219
|
+
setError("Please enter your email address");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (password.length < MIN_PASSWORD_LENGTH) {
|
|
223
|
+
setError(`Password must be at least ${MIN_PASSWORD_LENGTH} characters`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
setError(void 0);
|
|
227
|
+
setIsPending(true);
|
|
228
|
+
try {
|
|
229
|
+
const res = await authClient.signUp.email({
|
|
230
|
+
name: name.trim(),
|
|
231
|
+
email: email.trim(),
|
|
232
|
+
password
|
|
233
|
+
});
|
|
234
|
+
if (res?.error) {
|
|
235
|
+
setError(res.error.message ?? "Could not create your account");
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
onSuccess?.(email.trim());
|
|
239
|
+
} catch (err) {
|
|
240
|
+
setError(
|
|
241
|
+
err instanceof Error ? err.message : "Could not create your account"
|
|
242
|
+
);
|
|
243
|
+
} finally {
|
|
244
|
+
setIsPending(false);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
const handleSocial = async (provider) => {
|
|
248
|
+
setError(void 0);
|
|
249
|
+
try {
|
|
250
|
+
await authClient.signIn.social({
|
|
251
|
+
provider,
|
|
252
|
+
callbackURL: socialCallbackUrl
|
|
253
|
+
});
|
|
254
|
+
} catch (err) {
|
|
255
|
+
setError(err instanceof Error ? err.message : "Sign-up failed");
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
return /* @__PURE__ */ jsxs3("div", { className: "space-y-8", children: [
|
|
259
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
260
|
+
/* @__PURE__ */ jsx3("h1", { className: "text-2xl font-bold tracking-tight", children: "Create an account" }),
|
|
261
|
+
/* @__PURE__ */ jsx3("p", { className: "mt-1 text-sm text-muted-foreground", children: "We'll send you a code to confirm your email address." })
|
|
262
|
+
] }),
|
|
263
|
+
error && /* @__PURE__ */ jsx3("div", { className: "rounded-lg border border-destructive/20 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: error }),
|
|
264
|
+
/* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className: "space-y-4", noValidate: true, children: [
|
|
265
|
+
/* @__PURE__ */ jsx3(
|
|
266
|
+
"input",
|
|
267
|
+
{
|
|
268
|
+
type: "text",
|
|
269
|
+
value: name,
|
|
270
|
+
onChange: (e) => setName(e.target.value),
|
|
271
|
+
placeholder: "Full name",
|
|
272
|
+
required: true,
|
|
273
|
+
disabled: isPending,
|
|
274
|
+
autoComplete: "name",
|
|
275
|
+
className: "flex h-11 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed"
|
|
276
|
+
}
|
|
277
|
+
),
|
|
278
|
+
/* @__PURE__ */ jsx3(
|
|
279
|
+
"input",
|
|
280
|
+
{
|
|
281
|
+
type: "email",
|
|
282
|
+
value: email,
|
|
283
|
+
onChange: (e) => setEmail(e.target.value),
|
|
284
|
+
placeholder: "Email address",
|
|
285
|
+
required: true,
|
|
286
|
+
disabled: isPending,
|
|
287
|
+
autoComplete: "email",
|
|
288
|
+
className: "flex h-11 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed"
|
|
289
|
+
}
|
|
290
|
+
),
|
|
291
|
+
/* @__PURE__ */ jsxs3("div", { className: "space-y-1", children: [
|
|
292
|
+
/* @__PURE__ */ jsx3(
|
|
293
|
+
"input",
|
|
294
|
+
{
|
|
295
|
+
type: "password",
|
|
296
|
+
value: password,
|
|
297
|
+
onChange: (e) => setPassword(e.target.value),
|
|
298
|
+
placeholder: "Password",
|
|
299
|
+
required: true,
|
|
300
|
+
disabled: isPending,
|
|
301
|
+
autoComplete: "new-password",
|
|
302
|
+
"aria-describedby": "register-password-hint",
|
|
303
|
+
className: "flex h-11 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed"
|
|
304
|
+
}
|
|
305
|
+
),
|
|
306
|
+
/* @__PURE__ */ jsxs3("p", { id: "register-password-hint", className: "text-xs text-muted-foreground", children: [
|
|
307
|
+
"At least ",
|
|
308
|
+
MIN_PASSWORD_LENGTH,
|
|
309
|
+
" characters."
|
|
310
|
+
] })
|
|
311
|
+
] }),
|
|
312
|
+
/* @__PURE__ */ jsx3(
|
|
313
|
+
"button",
|
|
314
|
+
{
|
|
315
|
+
type: "submit",
|
|
316
|
+
disabled: isPending,
|
|
317
|
+
className: "inline-flex h-11 w-full items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50",
|
|
318
|
+
children: isPending ? "Creating account..." : "Create account"
|
|
319
|
+
}
|
|
320
|
+
),
|
|
321
|
+
legal && /* @__PURE__ */ jsx3("div", { className: "text-center text-xs text-muted-foreground", children: legal })
|
|
322
|
+
] }),
|
|
323
|
+
/* @__PURE__ */ jsx3(
|
|
324
|
+
SocialButtons,
|
|
325
|
+
{
|
|
326
|
+
providers: socialProviders,
|
|
327
|
+
onSelect: handleSocial,
|
|
328
|
+
disabled: isPending
|
|
329
|
+
}
|
|
330
|
+
),
|
|
331
|
+
/* @__PURE__ */ jsxs3("p", { className: "text-center text-sm text-muted-foreground", children: [
|
|
332
|
+
"Already have an account?",
|
|
333
|
+
" ",
|
|
334
|
+
/* @__PURE__ */ jsx3(
|
|
335
|
+
"a",
|
|
336
|
+
{
|
|
337
|
+
href: loginUrl,
|
|
338
|
+
className: "font-medium text-foreground underline underline-offset-4 hover:text-foreground/80",
|
|
339
|
+
children: "Sign in"
|
|
340
|
+
}
|
|
341
|
+
)
|
|
342
|
+
] })
|
|
343
|
+
] });
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/components/verify-email-form.tsx
|
|
347
|
+
import { useState as useState3 } from "react";
|
|
348
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
349
|
+
function VerifyEmailForm({
|
|
350
|
+
email,
|
|
351
|
+
onSuccess,
|
|
352
|
+
authClient
|
|
353
|
+
}) {
|
|
354
|
+
const [otp, setOtp] = useState3("");
|
|
355
|
+
const [isVerifying, setIsVerifying] = useState3(false);
|
|
356
|
+
const [isResending, setIsResending] = useState3(false);
|
|
357
|
+
const [resendMessage, setResendMessage] = useState3();
|
|
358
|
+
const [error, setError] = useState3();
|
|
359
|
+
const handleVerify = async (e) => {
|
|
360
|
+
e.preventDefault();
|
|
361
|
+
if (!otp.trim() || otp.length < 6) {
|
|
362
|
+
setError("Please enter the 6-digit code");
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
setError(void 0);
|
|
366
|
+
setIsVerifying(true);
|
|
367
|
+
try {
|
|
368
|
+
const res = await authClient.emailOtp.verifyEmail({ email, otp });
|
|
369
|
+
console.log("[verify-email] response:", JSON.stringify(res?.data), "error:", JSON.stringify(res?.error));
|
|
370
|
+
if (res?.error) {
|
|
371
|
+
setError(res.error.message ?? "Invalid code. Please try again.");
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
onSuccess?.();
|
|
375
|
+
} catch (err) {
|
|
376
|
+
setError(err instanceof Error ? err.message : "Invalid code. Please try again.");
|
|
377
|
+
} finally {
|
|
378
|
+
setIsVerifying(false);
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
const handleResend = async () => {
|
|
382
|
+
if (!email) {
|
|
383
|
+
setError("Email address is not available. Please register again.");
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
setError(void 0);
|
|
387
|
+
setResendMessage(void 0);
|
|
388
|
+
setIsResending(true);
|
|
389
|
+
try {
|
|
390
|
+
await authClient.emailOtp.sendVerificationOtp({
|
|
391
|
+
email,
|
|
392
|
+
type: "email-verification"
|
|
393
|
+
});
|
|
394
|
+
setResendMessage("A new code has been sent to your inbox.");
|
|
395
|
+
} catch (err) {
|
|
396
|
+
setError(err instanceof Error ? err.message : "Failed to resend code.");
|
|
397
|
+
} finally {
|
|
398
|
+
setIsResending(false);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
return /* @__PURE__ */ jsxs4("div", { className: "space-y-8", children: [
|
|
402
|
+
/* @__PURE__ */ jsxs4("div", { children: [
|
|
403
|
+
/* @__PURE__ */ jsx4("h1", { className: "text-2xl font-bold tracking-tight", children: "Verify your email" }),
|
|
404
|
+
/* @__PURE__ */ jsx4("p", { className: "mt-1 text-sm text-muted-foreground", children: email ? /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
405
|
+
"Enter the 6-digit code sent to",
|
|
406
|
+
" ",
|
|
407
|
+
/* @__PURE__ */ jsx4("span", { className: "font-medium text-foreground", children: email })
|
|
408
|
+
] }) : "Enter the 6-digit code sent to your email" })
|
|
409
|
+
] }),
|
|
410
|
+
error && /* @__PURE__ */ jsx4("div", { className: "rounded-lg border border-destructive/20 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: error }),
|
|
411
|
+
resendMessage && /* @__PURE__ */ jsx4("div", { className: "rounded-lg border border-green-200 bg-green-50 px-3 py-2 text-xs text-green-700", children: resendMessage }),
|
|
412
|
+
/* @__PURE__ */ jsxs4("form", { onSubmit: handleVerify, className: "space-y-4", noValidate: true, children: [
|
|
413
|
+
/* @__PURE__ */ jsx4(
|
|
414
|
+
"input",
|
|
415
|
+
{
|
|
416
|
+
type: "text",
|
|
417
|
+
inputMode: "numeric",
|
|
418
|
+
pattern: "[0-9]*",
|
|
419
|
+
maxLength: 6,
|
|
420
|
+
value: otp,
|
|
421
|
+
onChange: (e) => setOtp(e.target.value.replace(/\D/g, "")),
|
|
422
|
+
placeholder: "000000",
|
|
423
|
+
required: true,
|
|
424
|
+
disabled: isVerifying,
|
|
425
|
+
autoComplete: "one-time-code",
|
|
426
|
+
className: "flex h-12 w-full rounded-md border border-input bg-background px-3 py-2 text-center text-lg tracking-[0.4em] font-mono ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed"
|
|
427
|
+
}
|
|
428
|
+
),
|
|
429
|
+
/* @__PURE__ */ jsx4(
|
|
430
|
+
"button",
|
|
431
|
+
{
|
|
432
|
+
type: "submit",
|
|
433
|
+
disabled: isVerifying || otp.length < 6,
|
|
434
|
+
className: "inline-flex h-11 w-full items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50",
|
|
435
|
+
children: isVerifying ? "Verifying..." : "Verify email"
|
|
436
|
+
}
|
|
437
|
+
),
|
|
438
|
+
/* @__PURE__ */ jsx4(
|
|
439
|
+
"button",
|
|
440
|
+
{
|
|
441
|
+
type: "button",
|
|
442
|
+
onClick: handleResend,
|
|
443
|
+
disabled: isResending,
|
|
444
|
+
className: "inline-flex h-11 w-full items-center justify-center rounded-md border border-input bg-background px-4 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50",
|
|
445
|
+
children: isResending ? "Sending..." : "Resend code"
|
|
446
|
+
}
|
|
447
|
+
)
|
|
448
|
+
] }),
|
|
449
|
+
/* @__PURE__ */ jsxs4("p", { className: "text-center text-sm text-muted-foreground", children: [
|
|
450
|
+
"Already verified?",
|
|
451
|
+
" ",
|
|
452
|
+
/* @__PURE__ */ jsx4(
|
|
453
|
+
"a",
|
|
454
|
+
{
|
|
455
|
+
href: "/login",
|
|
456
|
+
className: "font-medium text-foreground underline underline-offset-4 hover:text-foreground/80",
|
|
457
|
+
children: "Sign in"
|
|
458
|
+
}
|
|
459
|
+
)
|
|
460
|
+
] })
|
|
461
|
+
] });
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// src/components/forgot-password-form.tsx
|
|
465
|
+
import { useState as useState4 } from "react";
|
|
466
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
467
|
+
function ForgotPasswordForm({
|
|
468
|
+
onSuccess,
|
|
469
|
+
loginUrl = "/login",
|
|
470
|
+
authClient
|
|
471
|
+
}) {
|
|
472
|
+
const [email, setEmail] = useState4("");
|
|
473
|
+
const [error, setError] = useState4();
|
|
474
|
+
const [isPending, setIsPending] = useState4(false);
|
|
475
|
+
const handleSubmit = async (e) => {
|
|
476
|
+
e.preventDefault();
|
|
477
|
+
if (!email.trim()) {
|
|
478
|
+
setError("Please enter your email address");
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
setError(void 0);
|
|
482
|
+
setIsPending(true);
|
|
483
|
+
try {
|
|
484
|
+
const res = await authClient.emailOtp.sendVerificationOtp({
|
|
485
|
+
email,
|
|
486
|
+
type: "forget-password"
|
|
487
|
+
});
|
|
488
|
+
if (res?.error) {
|
|
489
|
+
setError(res.error.message ?? "Failed to send reset code");
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
onSuccess?.(email);
|
|
493
|
+
} catch (err) {
|
|
494
|
+
setError(err instanceof Error ? err.message : "Failed to send reset code");
|
|
495
|
+
} finally {
|
|
496
|
+
setIsPending(false);
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
return /* @__PURE__ */ jsxs5("div", { className: "space-y-8", children: [
|
|
500
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
501
|
+
/* @__PURE__ */ jsx5("h1", { className: "text-2xl font-bold tracking-tight", children: "Forgot your password?" }),
|
|
502
|
+
/* @__PURE__ */ jsx5("p", { className: "mt-1 text-sm text-muted-foreground", children: "Enter your email address and we'll send you a code to reset your password." })
|
|
503
|
+
] }),
|
|
504
|
+
error && /* @__PURE__ */ jsx5("div", { className: "rounded-lg border border-destructive/20 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: error }),
|
|
505
|
+
/* @__PURE__ */ jsxs5("form", { onSubmit: handleSubmit, className: "space-y-4", noValidate: true, children: [
|
|
506
|
+
/* @__PURE__ */ jsx5(
|
|
507
|
+
"input",
|
|
508
|
+
{
|
|
509
|
+
type: "email",
|
|
510
|
+
value: email,
|
|
511
|
+
onChange: (e) => setEmail(e.target.value),
|
|
512
|
+
placeholder: "Email address",
|
|
513
|
+
required: true,
|
|
514
|
+
disabled: isPending,
|
|
515
|
+
autoComplete: "email",
|
|
516
|
+
className: "flex h-11 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed"
|
|
517
|
+
}
|
|
518
|
+
),
|
|
519
|
+
/* @__PURE__ */ jsx5(
|
|
520
|
+
"button",
|
|
521
|
+
{
|
|
522
|
+
type: "submit",
|
|
523
|
+
disabled: isPending || !email.trim(),
|
|
524
|
+
className: "inline-flex h-11 w-full items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50",
|
|
525
|
+
children: isPending ? "Sending..." : "Send reset code"
|
|
526
|
+
}
|
|
527
|
+
)
|
|
528
|
+
] }),
|
|
529
|
+
/* @__PURE__ */ jsxs5("p", { className: "text-center text-sm text-muted-foreground", children: [
|
|
530
|
+
"Remember your password?",
|
|
531
|
+
" ",
|
|
532
|
+
/* @__PURE__ */ jsx5(
|
|
533
|
+
"a",
|
|
534
|
+
{
|
|
535
|
+
href: loginUrl,
|
|
536
|
+
className: "font-medium text-foreground underline underline-offset-4 hover:text-foreground/80",
|
|
537
|
+
children: "Sign in"
|
|
538
|
+
}
|
|
539
|
+
)
|
|
540
|
+
] })
|
|
541
|
+
] });
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// src/components/reset-password-form.tsx
|
|
545
|
+
import { useState as useState5 } from "react";
|
|
546
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
547
|
+
function ResetPasswordForm({
|
|
548
|
+
email,
|
|
549
|
+
onSuccess,
|
|
550
|
+
loginUrl = "/login",
|
|
551
|
+
authClient
|
|
552
|
+
}) {
|
|
553
|
+
const [otp, setOtp] = useState5("");
|
|
554
|
+
const [password, setPassword] = useState5("");
|
|
555
|
+
const [confirmPassword, setConfirmPassword] = useState5("");
|
|
556
|
+
const [error, setError] = useState5();
|
|
557
|
+
const [isResetting, setIsResetting] = useState5(false);
|
|
558
|
+
const [isResending, setIsResending] = useState5(false);
|
|
559
|
+
const [resendMessage, setResendMessage] = useState5();
|
|
560
|
+
const handleSubmit = async (e) => {
|
|
561
|
+
e.preventDefault();
|
|
562
|
+
if (!otp.trim() || otp.length < 6) {
|
|
563
|
+
setError("Please enter the 6-digit code");
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
if (password.length < 8) {
|
|
567
|
+
setError("Password must be at least 8 characters");
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
if (password !== confirmPassword) {
|
|
571
|
+
setError("Passwords do not match");
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
setError(void 0);
|
|
575
|
+
setIsResetting(true);
|
|
576
|
+
try {
|
|
577
|
+
const res = await fetch("/api/auth/email-otp/reset-password", {
|
|
578
|
+
method: "POST",
|
|
579
|
+
headers: { "Content-Type": "application/json" },
|
|
580
|
+
body: JSON.stringify({ email, otp, password })
|
|
581
|
+
});
|
|
582
|
+
if (!res.ok) {
|
|
583
|
+
const body = await res.json().catch(() => null);
|
|
584
|
+
setError(body?.message ?? "Failed to reset password");
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
onSuccess?.();
|
|
588
|
+
} catch (err) {
|
|
589
|
+
setError(
|
|
590
|
+
err instanceof Error ? err.message : "Failed to reset password"
|
|
591
|
+
);
|
|
592
|
+
} finally {
|
|
593
|
+
setIsResetting(false);
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
const handleResend = async () => {
|
|
597
|
+
setError(void 0);
|
|
598
|
+
setResendMessage(void 0);
|
|
599
|
+
setIsResending(true);
|
|
600
|
+
try {
|
|
601
|
+
await authClient.emailOtp.sendVerificationOtp({
|
|
602
|
+
email,
|
|
603
|
+
type: "forget-password"
|
|
604
|
+
});
|
|
605
|
+
setResendMessage("A new code has been sent to your inbox.");
|
|
606
|
+
} catch (err) {
|
|
607
|
+
setError(err instanceof Error ? err.message : "Failed to resend code.");
|
|
608
|
+
} finally {
|
|
609
|
+
setIsResending(false);
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
return /* @__PURE__ */ jsxs6("div", { className: "space-y-8", children: [
|
|
613
|
+
/* @__PURE__ */ jsxs6("div", { children: [
|
|
614
|
+
/* @__PURE__ */ jsx6("h1", { className: "text-2xl font-bold tracking-tight", children: "Reset your password" }),
|
|
615
|
+
/* @__PURE__ */ jsxs6("p", { className: "mt-1 text-sm text-muted-foreground", children: [
|
|
616
|
+
"Enter the 6-digit code sent to",
|
|
617
|
+
" ",
|
|
618
|
+
/* @__PURE__ */ jsx6("span", { className: "font-medium text-foreground", children: email }),
|
|
619
|
+
" and your new password."
|
|
620
|
+
] })
|
|
621
|
+
] }),
|
|
622
|
+
error && /* @__PURE__ */ jsx6("div", { className: "rounded-lg border border-destructive/20 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: error }),
|
|
623
|
+
resendMessage && /* @__PURE__ */ jsx6("div", { className: "rounded-lg border border-green-200 bg-green-50 px-3 py-2 text-xs text-green-700", children: resendMessage }),
|
|
624
|
+
/* @__PURE__ */ jsxs6("form", { onSubmit: handleSubmit, className: "space-y-4", noValidate: true, children: [
|
|
625
|
+
/* @__PURE__ */ jsx6(
|
|
626
|
+
"input",
|
|
627
|
+
{
|
|
628
|
+
type: "text",
|
|
629
|
+
inputMode: "numeric",
|
|
630
|
+
pattern: "[0-9]*",
|
|
631
|
+
maxLength: 6,
|
|
632
|
+
value: otp,
|
|
633
|
+
onChange: (e) => setOtp(e.target.value.replace(/\D/g, "")),
|
|
634
|
+
placeholder: "000000",
|
|
635
|
+
required: true,
|
|
636
|
+
disabled: isResetting,
|
|
637
|
+
autoComplete: "one-time-code",
|
|
638
|
+
className: "flex h-12 w-full rounded-md border border-input bg-background px-3 py-2 text-center text-lg tracking-[0.4em] font-mono ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed"
|
|
639
|
+
}
|
|
640
|
+
),
|
|
641
|
+
/* @__PURE__ */ jsx6(
|
|
642
|
+
"input",
|
|
643
|
+
{
|
|
644
|
+
type: "password",
|
|
645
|
+
value: password,
|
|
646
|
+
onChange: (e) => setPassword(e.target.value),
|
|
647
|
+
placeholder: "New password",
|
|
648
|
+
required: true,
|
|
649
|
+
disabled: isResetting,
|
|
650
|
+
autoComplete: "new-password",
|
|
651
|
+
className: "flex h-11 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed"
|
|
652
|
+
}
|
|
653
|
+
),
|
|
654
|
+
/* @__PURE__ */ jsx6(
|
|
655
|
+
"input",
|
|
656
|
+
{
|
|
657
|
+
type: "password",
|
|
658
|
+
value: confirmPassword,
|
|
659
|
+
onChange: (e) => setConfirmPassword(e.target.value),
|
|
660
|
+
placeholder: "Confirm new password",
|
|
661
|
+
required: true,
|
|
662
|
+
disabled: isResetting,
|
|
663
|
+
autoComplete: "new-password",
|
|
664
|
+
className: "flex h-11 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed"
|
|
665
|
+
}
|
|
666
|
+
),
|
|
667
|
+
/* @__PURE__ */ jsx6(
|
|
668
|
+
"button",
|
|
669
|
+
{
|
|
670
|
+
type: "submit",
|
|
671
|
+
disabled: isResetting || otp.length < 6 || !password,
|
|
672
|
+
className: "inline-flex h-11 w-full items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50",
|
|
673
|
+
children: isResetting ? "Resetting..." : "Reset password"
|
|
674
|
+
}
|
|
675
|
+
),
|
|
676
|
+
/* @__PURE__ */ jsx6(
|
|
677
|
+
"button",
|
|
678
|
+
{
|
|
679
|
+
type: "button",
|
|
680
|
+
onClick: handleResend,
|
|
681
|
+
disabled: isResending,
|
|
682
|
+
className: "inline-flex h-11 w-full items-center justify-center rounded-md border border-input bg-background px-4 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50",
|
|
683
|
+
children: isResending ? "Sending..." : "Resend code"
|
|
684
|
+
}
|
|
685
|
+
)
|
|
686
|
+
] }),
|
|
687
|
+
/* @__PURE__ */ jsxs6("p", { className: "text-center text-sm text-muted-foreground", children: [
|
|
688
|
+
"Remember your password?",
|
|
689
|
+
" ",
|
|
690
|
+
/* @__PURE__ */ jsx6(
|
|
691
|
+
"a",
|
|
692
|
+
{
|
|
693
|
+
href: loginUrl,
|
|
694
|
+
className: "font-medium text-foreground underline underline-offset-4 hover:text-foreground/80",
|
|
695
|
+
children: "Sign in"
|
|
696
|
+
}
|
|
697
|
+
)
|
|
698
|
+
] })
|
|
699
|
+
] });
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// src/components/auth-layout.tsx
|
|
703
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
704
|
+
function AuthLayout({
|
|
705
|
+
logo,
|
|
706
|
+
title,
|
|
707
|
+
subtitle,
|
|
708
|
+
children,
|
|
709
|
+
footer
|
|
710
|
+
}) {
|
|
711
|
+
return /* @__PURE__ */ jsx7("div", { className: "flex min-h-screen items-center justify-center bg-background", children: /* @__PURE__ */ jsxs7("div", { className: "w-full max-w-md", children: [
|
|
712
|
+
/* @__PURE__ */ jsxs7("div", { className: "mb-10 text-center", children: [
|
|
713
|
+
logo && /* @__PURE__ */ jsx7("div", { className: "mb-6 flex justify-center", children: logo }),
|
|
714
|
+
/* @__PURE__ */ jsx7("h1", { className: "text-[32px] font-light tracking-tight", children: title }),
|
|
715
|
+
subtitle && /* @__PURE__ */ jsx7("p", { className: "mt-2 text-sm text-muted-foreground", children: subtitle })
|
|
716
|
+
] }),
|
|
717
|
+
/* @__PURE__ */ jsx7("div", { className: "rounded-xl border bg-card p-8 shadow-sm", children }),
|
|
718
|
+
footer && /* @__PURE__ */ jsx7("div", { className: "mt-6 text-center text-xs text-muted-foreground", children: footer })
|
|
719
|
+
] }) });
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// src/components/invitation-notice.tsx
|
|
723
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
724
|
+
function defaultSupportEmail() {
|
|
725
|
+
if (typeof window === "undefined") return void 0;
|
|
726
|
+
const host = window.location.hostname;
|
|
727
|
+
if (!host || host === "localhost" || /^[\d.]+$/.test(host)) return void 0;
|
|
728
|
+
const apex = host.split(".").slice(-2).join(".");
|
|
729
|
+
return `contact@${apex}`;
|
|
730
|
+
}
|
|
731
|
+
var REASON_MESSAGE = {
|
|
732
|
+
expired: "Cette invitation a expir\xE9.",
|
|
733
|
+
claimed: "Cette invitation a d\xE9j\xE0 \xE9t\xE9 utilis\xE9e.",
|
|
734
|
+
unknown: "Ce lien d'invitation n'est pas valide."
|
|
735
|
+
};
|
|
736
|
+
function InvitationNotice({
|
|
737
|
+
reason = "unknown",
|
|
738
|
+
supportEmail,
|
|
739
|
+
title = "Invitation indisponible",
|
|
740
|
+
action
|
|
741
|
+
}) {
|
|
742
|
+
const contact = supportEmail ?? defaultSupportEmail();
|
|
743
|
+
return /* @__PURE__ */ jsxs8("div", { className: "space-y-8", children: [
|
|
744
|
+
/* @__PURE__ */ jsxs8("div", { children: [
|
|
745
|
+
/* @__PURE__ */ jsx8("h1", { className: "text-2xl font-bold tracking-tight", children: title }),
|
|
746
|
+
/* @__PURE__ */ jsx8("p", { className: "mt-1 text-sm text-muted-foreground", children: REASON_MESSAGE[reason] ?? REASON_MESSAGE.unknown })
|
|
747
|
+
] }),
|
|
748
|
+
contact ? /* @__PURE__ */ jsxs8("p", { className: "text-sm text-muted-foreground", children: [
|
|
749
|
+
"\xC9crivez-nous \xE0",
|
|
750
|
+
" ",
|
|
751
|
+
/* @__PURE__ */ jsx8(
|
|
752
|
+
"a",
|
|
753
|
+
{
|
|
754
|
+
href: `mailto:${contact}`,
|
|
755
|
+
className: "font-medium text-foreground underline underline-offset-4 hover:text-foreground/80",
|
|
756
|
+
children: contact
|
|
757
|
+
}
|
|
758
|
+
),
|
|
759
|
+
" ",
|
|
760
|
+
"pour en recevoir une nouvelle."
|
|
761
|
+
] }) : null,
|
|
762
|
+
action
|
|
763
|
+
] });
|
|
764
|
+
}
|
|
765
|
+
export {
|
|
766
|
+
AuthLayout,
|
|
767
|
+
ForgotPasswordForm,
|
|
768
|
+
InvitationNotice,
|
|
769
|
+
LoginForm,
|
|
770
|
+
RegisterForm,
|
|
771
|
+
ResetPasswordForm,
|
|
772
|
+
SocialButtons,
|
|
773
|
+
VerifyEmailForm,
|
|
774
|
+
isInvitationFailure,
|
|
775
|
+
useLogout,
|
|
776
|
+
useSession
|
|
777
|
+
};
|
|
778
|
+
//# sourceMappingURL=index.js.map
|