wiki-viewer 1.4.1 → 1.4.3
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -27
- package/.next/standalone/.next/server/app/_global-error/page.js +2 -2
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +4 -4
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/agent/activity/route.js +5 -5
- package/.next/standalone/.next/server/app/api/agent/activity/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/admin/agents/[agentId]/revoke/route.js +6 -6
- package/.next/standalone/.next/server/app/api/agent/admin/agents/[agentId]/revoke/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/admin/agents/route.js +6 -6
- package/.next/standalone/.next/server/app/api/agent/admin/agents/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/admin/registrations/[regId]/approve/route.js +6 -6
- package/.next/standalone/.next/server/app/api/agent/admin/registrations/[regId]/approve/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/admin/registrations/[regId]/deny/route.js +4 -4
- package/.next/standalone/.next/server/app/api/agent/admin/registrations/[regId]/deny/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/admin/registrations/route.js +4 -4
- package/.next/standalone/.next/server/app/api/agent/admin/registrations/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/events/[...path]/route.js +5 -5
- package/.next/standalone/.next/server/app/api/agent/events/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js +4 -4
- package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/internal/span/route.js +8 -8
- package/.next/standalone/.next/server/app/api/agent/internal/span/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/settings/route.js +7 -7
- package/.next/standalone/.next/server/app/api/agent/settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js +7 -7
- package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/app-proxy/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/[...all]/route.js +3 -3
- package/.next/standalone/.next/server/app/api/auth/[...all]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/auth-config/route.js +5 -5
- package/.next/standalone/.next/server/app/api/system/auth-config/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/auth-settings/route.js +4 -4
- package/.next/standalone/.next/server/app/api/system/auth-settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/browse/route.js +6 -6
- package/.next/standalone/.next/server/app/api/system/browse/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/clear-root/route.js +5 -5
- package/.next/standalone/.next/server/app/api/system/clear-root/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/config/route.js +5 -5
- package/.next/standalone/.next/server/app/api/system/config/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/pins/route.js +4 -4
- package/.next/standalone/.next/server/app/api/system/pins/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/reveal/route.js +4 -4
- package/.next/standalone/.next/server/app/api/system/reveal/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/root-status/route.js +4 -4
- package/.next/standalone/.next/server/app/api/system/root-status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/set-root/route.js +5 -5
- package/.next/standalone/.next/server/app/api/system/set-root/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/app/route.js +6 -6
- package/.next/standalone/.next/server/app/api/wiki/app/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/content/route.js +7 -7
- package/.next/standalone/.next/server/app/api/wiki/content/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/folder/route.js +5 -5
- package/.next/standalone/.next/server/app/api/wiki/folder/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/move/route.js +5 -5
- package/.next/standalone/.next/server/app/api/wiki/move/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/new-file/route.js +5 -5
- package/.next/standalone/.next/server/app/api/wiki/new-file/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/page/route.js +5 -5
- package/.next/standalone/.next/server/app/api/wiki/page/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/route.js +5 -5
- package/.next/standalone/.next/server/app/api/wiki/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/slugs/route.js +5 -5
- package/.next/standalone/.next/server/app/api/wiki/slugs/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/upload/route.js +5 -5
- package/.next/standalone/.next/server/app/api/wiki/upload/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/watch/route.js +6 -6
- package/.next/standalone/.next/server/app/api/wiki/watch/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page.js +4 -4
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/signin/page.js +9 -4
- package/.next/standalone/.next/server/app/signin/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/signin/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__03h3~3e._.js → [root-of-the-server]__04udrya._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__04g0j8i._.js → [root-of-the-server]__0kdn1le._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0d89o98._.js → src_lib_auth_server_ts_00f4e0h._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/0biy_@better-auth_core_dist_121m_lh._.js +5 -0
- package/.next/standalone/.next/server/chunks/ssr/0dhv_better-auth_dist_adapters_kysely-adapter_index_mjs_08ymlcb._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/0dti_kysely_dist_esm_0-t3o9q._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/0dti_kysely_dist_esm_01._58q._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/0p91_next_0f40gy0._.js +6 -0
- package/.next/standalone/.next/server/chunks/ssr/{0p91_next_dist_0~2d1tl._.js → 0p91_next_dist_10ud_sa._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/0sag_@better-auth_memory-adapter_dist_index_mjs_07-_ka6._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/0ugq_@better-auth_kysely-adapter_dist_bun-sqlite-dialect-DzNwOpKv_mjs_0lruvb8._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/0ugq_@better-auth_kysely-adapter_dist_d1-sqlite-dialect-C2B7YsIT_mjs_03n6~tc._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/0ugq_@better-auth_kysely-adapter_dist_index_mjs_0gi5w.w._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/0ugq_@better-auth_kysely-adapter_dist_node-sqlite-dialect_mjs_0l4.y~0._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__026s8~z._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__06mzg2t._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0btcd4o._.js +12 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0c~og-9._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__134_q7l._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0pqaawz._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules__pnpm_051tcva._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules__pnpm_0o~d81.._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_app_signin_00yk0vm._.js +452 -0
- package/.next/standalone/.next/server/chunks/ssr/src_app_signin_signin-form_tsx_0eznlbd._.js +3 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware-manifest.json +5 -5
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/09-jtayuhrq~b.js +1 -0
- package/.next/standalone/.next/static/chunks/0uv7ozzpiyp2_.js +1 -0
- package/.next/standalone/.next/static/chunks/135c~rq_ux73w.js +1 -0
- package/.next/standalone/docs/agent-fs-plan.md +230 -0
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/src/app/signin/page.tsx +10 -225
- package/.next/standalone/src/app/signin/signin-form.tsx +238 -0
- package/.next/standalone/src/lib/auth/server.ts +14 -7
- package/.next/standalone/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/.next/standalone/.next/server/app/signin.html +0 -1
- package/.next/standalone/.next/server/app/signin.meta +0 -15
- package/.next/standalone/.next/server/app/signin.rsc +0 -23
- package/.next/standalone/.next/server/app/signin.segments/_full.segment.rsc +0 -23
- package/.next/standalone/.next/server/app/signin.segments/_head.segment.rsc +0 -6
- package/.next/standalone/.next/server/app/signin.segments/_index.segment.rsc +0 -7
- package/.next/standalone/.next/server/app/signin.segments/_tree.segment.rsc +0 -3
- package/.next/standalone/.next/server/app/signin.segments/signin/__PAGE__.segment.rsc +0 -9
- package/.next/standalone/.next/server/app/signin.segments/signin.segment.rsc +0 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__088hqzy._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0c147fo._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0z.1v1z._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12zp2dm._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules__pnpm_0exqvyf._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_app_signin_page_tsx_1143y49._.js +0 -3
- package/.next/standalone/.next/static/chunks/0-_39e4awbk8y.js +0 -1
- package/.next/standalone/.next/static/chunks/02bxx~2cg~s6~.js +0 -1
- package/.next/standalone/.next/static/chunks/0mj_f8ncf2tks.js +0 -1
- /package/.next/standalone/.next/static/{bNZLQMXPxlDzQicfJAh2f → kzOuSAQArtvD7uiAJ6ZLh}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{bNZLQMXPxlDzQicfJAh2f → kzOuSAQArtvD7uiAJ6ZLh}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{bNZLQMXPxlDzQicfJAh2f → kzOuSAQArtvD7uiAJ6ZLh}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, type FormEvent } from "react";
|
|
4
|
+
import { authClient } from "@/lib/auth/client";
|
|
5
|
+
|
|
6
|
+
export default function SignInForm({
|
|
7
|
+
initialPasswordAuth,
|
|
8
|
+
initialGoogle,
|
|
9
|
+
}: {
|
|
10
|
+
initialPasswordAuth: boolean;
|
|
11
|
+
initialGoogle: boolean;
|
|
12
|
+
}) {
|
|
13
|
+
const [mode, setMode] = useState<"signin" | "signup">("signin");
|
|
14
|
+
const [email, setEmail] = useState("");
|
|
15
|
+
const [password, setPassword] = useState("");
|
|
16
|
+
const [name, setName] = useState("");
|
|
17
|
+
const [error, setError] = useState<string | null>(null);
|
|
18
|
+
const [loading, setLoading] = useState(false);
|
|
19
|
+
// Seed from server-rendered values so the first paint already matches the
|
|
20
|
+
// configured auth methods. This avoids a flash of the password form when it
|
|
21
|
+
// is disabled. The fetch below only refreshes in case config changed.
|
|
22
|
+
const [hasGoogle, setHasGoogle] = useState(initialGoogle);
|
|
23
|
+
const [passwordAuth, setPasswordAuth] = useState(initialPasswordAuth);
|
|
24
|
+
const [callbackURL, setCallbackURL] = useState("/");
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
// Refresh which auth methods to show. Public endpoint, no session needed.
|
|
28
|
+
fetch("/api/system/auth-config", { credentials: "include" })
|
|
29
|
+
.then((r) => r.json())
|
|
30
|
+
.then((data: unknown) => {
|
|
31
|
+
if (data && typeof data === "object") {
|
|
32
|
+
const cfg = data as { google?: boolean; passwordAuth?: boolean };
|
|
33
|
+
setHasGoogle(Boolean(cfg.google));
|
|
34
|
+
setPasswordAuth(cfg.passwordAuth !== false);
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
.catch(() => {});
|
|
38
|
+
|
|
39
|
+
// Honour ?next= redirect param
|
|
40
|
+
const params = new URLSearchParams(window.location.search);
|
|
41
|
+
const next = params.get("next");
|
|
42
|
+
if (next && next.startsWith("/")) setCallbackURL(next);
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
async function handleSubmit(e: FormEvent) {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
setError(null);
|
|
48
|
+
setLoading(true);
|
|
49
|
+
try {
|
|
50
|
+
if (mode === "signup") {
|
|
51
|
+
const res = await authClient.signUp.email({
|
|
52
|
+
email,
|
|
53
|
+
password,
|
|
54
|
+
name,
|
|
55
|
+
callbackURL,
|
|
56
|
+
});
|
|
57
|
+
if (res.error) {
|
|
58
|
+
setError(res.error.message ?? "Sign-up failed");
|
|
59
|
+
} else {
|
|
60
|
+
window.location.href = callbackURL;
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
const res = await authClient.signIn.email({
|
|
64
|
+
email,
|
|
65
|
+
password,
|
|
66
|
+
callbackURL,
|
|
67
|
+
});
|
|
68
|
+
if (res.error) {
|
|
69
|
+
const status = res.error.status;
|
|
70
|
+
if (status === 429) {
|
|
71
|
+
setError("Too many sign-in attempts. Wait a minute and try again.");
|
|
72
|
+
} else {
|
|
73
|
+
setError(res.error.message ?? "Sign-in failed");
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
window.location.href = callbackURL;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} catch (err: unknown) {
|
|
80
|
+
setError(err instanceof Error ? err.message : "Unexpected error");
|
|
81
|
+
} finally {
|
|
82
|
+
setLoading(false);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function handleGoogle() {
|
|
87
|
+
setError(null);
|
|
88
|
+
setLoading(true);
|
|
89
|
+
try {
|
|
90
|
+
await authClient.signIn.social({ provider: "google", callbackURL });
|
|
91
|
+
} catch (err: unknown) {
|
|
92
|
+
setError(err instanceof Error ? err.message : "Google sign-in failed");
|
|
93
|
+
setLoading(false);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<div className="min-h-screen flex items-center justify-center bg-background">
|
|
99
|
+
<div className="w-full max-w-sm rounded-lg border border-border bg-card p-8 shadow-sm">
|
|
100
|
+
<h1 className="mb-6 text-xl font-semibold text-foreground">Sign in to wiki-viewer</h1>
|
|
101
|
+
|
|
102
|
+
{hasGoogle && (
|
|
103
|
+
<>
|
|
104
|
+
<button
|
|
105
|
+
type="button"
|
|
106
|
+
onClick={handleGoogle}
|
|
107
|
+
disabled={loading}
|
|
108
|
+
className="flex w-full items-center justify-center gap-2 rounded-md border border-border px-4 py-2 text-sm font-medium hover:bg-accent disabled:opacity-50"
|
|
109
|
+
>
|
|
110
|
+
<svg viewBox="0 0 24 24" className="h-4 w-4" aria-hidden="true">
|
|
111
|
+
<path
|
|
112
|
+
fill="#4285F4"
|
|
113
|
+
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
|
114
|
+
/>
|
|
115
|
+
<path
|
|
116
|
+
fill="#34A853"
|
|
117
|
+
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
|
118
|
+
/>
|
|
119
|
+
<path
|
|
120
|
+
fill="#FBBC05"
|
|
121
|
+
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z"
|
|
122
|
+
/>
|
|
123
|
+
<path
|
|
124
|
+
fill="#EA4335"
|
|
125
|
+
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
|
126
|
+
/>
|
|
127
|
+
</svg>
|
|
128
|
+
Continue with Google
|
|
129
|
+
</button>
|
|
130
|
+
{passwordAuth && (
|
|
131
|
+
<div className="my-4 flex items-center gap-3">
|
|
132
|
+
<hr className="flex-1 border-border" />
|
|
133
|
+
<span className="text-xs text-muted-foreground">or</span>
|
|
134
|
+
<hr className="flex-1 border-border" />
|
|
135
|
+
</div>
|
|
136
|
+
)}
|
|
137
|
+
</>
|
|
138
|
+
)}
|
|
139
|
+
|
|
140
|
+
{!passwordAuth && !hasGoogle && (
|
|
141
|
+
<p className="rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive">
|
|
142
|
+
No sign-in method is configured. Set GOOGLE_CLIENT_ID and
|
|
143
|
+
GOOGLE_CLIENT_SECRET, or enable email/password auth.
|
|
144
|
+
</p>
|
|
145
|
+
)}
|
|
146
|
+
|
|
147
|
+
{passwordAuth && (
|
|
148
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
149
|
+
{mode === "signup" && (
|
|
150
|
+
<div>
|
|
151
|
+
<label className="mb-1 block text-sm font-medium text-foreground" htmlFor="name">
|
|
152
|
+
Name
|
|
153
|
+
</label>
|
|
154
|
+
<input
|
|
155
|
+
id="name"
|
|
156
|
+
type="text"
|
|
157
|
+
autoComplete="name"
|
|
158
|
+
value={name}
|
|
159
|
+
onChange={(e) => setName(e.target.value)}
|
|
160
|
+
required
|
|
161
|
+
className="w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
162
|
+
/>
|
|
163
|
+
</div>
|
|
164
|
+
)}
|
|
165
|
+
<div>
|
|
166
|
+
<label className="mb-1 block text-sm font-medium text-foreground" htmlFor="email">
|
|
167
|
+
Email
|
|
168
|
+
</label>
|
|
169
|
+
<input
|
|
170
|
+
id="email"
|
|
171
|
+
type="email"
|
|
172
|
+
autoComplete="email"
|
|
173
|
+
value={email}
|
|
174
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
175
|
+
required
|
|
176
|
+
className="w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
177
|
+
/>
|
|
178
|
+
</div>
|
|
179
|
+
<div>
|
|
180
|
+
<label
|
|
181
|
+
className="mb-1 block text-sm font-medium text-foreground"
|
|
182
|
+
htmlFor="password"
|
|
183
|
+
>
|
|
184
|
+
Password
|
|
185
|
+
</label>
|
|
186
|
+
<input
|
|
187
|
+
id="password"
|
|
188
|
+
type="password"
|
|
189
|
+
autoComplete={mode === "signup" ? "new-password" : "current-password"}
|
|
190
|
+
value={password}
|
|
191
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
192
|
+
required
|
|
193
|
+
minLength={8}
|
|
194
|
+
className="w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
195
|
+
/>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
{error && (
|
|
199
|
+
<p className="rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive">
|
|
200
|
+
{error}
|
|
201
|
+
</p>
|
|
202
|
+
)}
|
|
203
|
+
|
|
204
|
+
<button
|
|
205
|
+
type="submit"
|
|
206
|
+
disabled={loading}
|
|
207
|
+
className="w-full rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
|
208
|
+
>
|
|
209
|
+
{loading
|
|
210
|
+
? mode === "signup"
|
|
211
|
+
? "Creating account..."
|
|
212
|
+
: "Signing in..."
|
|
213
|
+
: mode === "signup"
|
|
214
|
+
? "Create account"
|
|
215
|
+
: "Sign in"}
|
|
216
|
+
</button>
|
|
217
|
+
</form>
|
|
218
|
+
)}
|
|
219
|
+
|
|
220
|
+
{passwordAuth && (
|
|
221
|
+
<p className="mt-4 text-center text-sm text-muted-foreground">
|
|
222
|
+
{mode === "signin" ? "No account?" : "Already have an account?"}{" "}
|
|
223
|
+
<button
|
|
224
|
+
type="button"
|
|
225
|
+
onClick={() => {
|
|
226
|
+
setMode(mode === "signin" ? "signup" : "signin");
|
|
227
|
+
setError(null);
|
|
228
|
+
}}
|
|
229
|
+
className="font-medium text-foreground underline-offset-4 hover:underline"
|
|
230
|
+
>
|
|
231
|
+
{mode === "signin" ? "Sign up" : "Sign in"}
|
|
232
|
+
</button>
|
|
233
|
+
</p>
|
|
234
|
+
)}
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
);
|
|
238
|
+
}
|
|
@@ -93,11 +93,16 @@ if (passwordDisabled && !hasSocialProvider) {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
// Forced account linking. By default Better Auth only auto-links a social
|
|
96
|
-
// login to an existing account when
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
// existing account
|
|
100
|
-
//
|
|
96
|
+
// login to an existing account when BOTH sides are email-verified. Two
|
|
97
|
+
// separate checks gate this (see better-auth oauth2/link-account):
|
|
98
|
+
// 1. the provider must report the email verified, OR the provider is trusted
|
|
99
|
+
// 2. the existing local account must be email-verified, unless
|
|
100
|
+
// requireLocalEmailVerified is false
|
|
101
|
+
// A pre-existing password account with emailVerified=0 fails check 2 even when
|
|
102
|
+
// the provider is trusted. So listing a provider in AUTH_TRUSTED_PROVIDERS also
|
|
103
|
+
// turns off requireLocalEmailVerified, otherwise Google sign-in still returns
|
|
104
|
+
// account_not_linked. This trades a little account-takeover risk for the
|
|
105
|
+
// ability to consolidate onto Google sign-in, so it is opt-in.
|
|
101
106
|
const trustedProviders = (process.env.AUTH_TRUSTED_PROVIDERS ?? "")
|
|
102
107
|
.split(",")
|
|
103
108
|
.map((p) => p.trim())
|
|
@@ -110,8 +115,10 @@ export const auth = betterAuth({
|
|
|
110
115
|
account: {
|
|
111
116
|
accountLinking: {
|
|
112
117
|
enabled: true,
|
|
113
|
-
...(trustedProviders.length
|
|
114
|
-
|
|
118
|
+
...(trustedProviders.length
|
|
119
|
+
? { trustedProviders, requireLocalEmailVerified: false }
|
|
120
|
+
: {}),
|
|
121
|
+
} as Record<string, unknown>,
|
|
115
122
|
},
|
|
116
123
|
emailAndPassword: {
|
|
117
124
|
enabled: passwordAuthEnabled,
|