@vonall/auth 0.3.0 → 0.3.1
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/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/dist/messages.d.ts +1 -0
- package/dist/messages.js +2 -0
- package/dist/ui/DeletionTab.svelte +2 -1
- package/dist/ui/LoginForm.svelte +9 -6
- package/dist/ui/RegisterForm.svelte +1 -1
- package/dist/ui/SettingsPage.svelte +1 -2
- package/dist/ui/SocialButton.svelte +3 -2
- package/dist/ui/TwoFactorAuthenticationCard.svelte +9 -5
- package/dist/util/fresh.js +1 -1
- package/package.json +9 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @vonall/auth
|
|
2
2
|
|
|
3
|
+
## 0.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- The code review's fixes: `svelte-sonner`, `bits-ui`, `@lucide/svelte` and `mode-watcher` are peer dependencies (one copy per app, so toasts reach the Product's Toaster); the login page no longer echoes unknown `?error=` codes and clears the URL's reason on a new attempt; every path goes through the base path; the settings page stays where the Product mounted it; copying the backup codes reports a failure.
|
|
8
|
+
|
|
3
9
|
## 0.3.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ better-auth plugins and rate limit rule come from `@vonall/mcp` through `plugins
|
|
|
54
54
|
<!-- src/routes/auth/signup/+page.svelte --> <RegisterForm />
|
|
55
55
|
<!-- src/routes/auth/reset-password --> <ResetPasswordForm />
|
|
56
56
|
<!-- src/routes/einladung/[token] --> <EinladungPage {data} tagline={APP_TAGLINE} />
|
|
57
|
-
<!-- src/routes/home/settings --> <SettingsPage extraTabs={[
|
|
57
|
+
<!-- src/routes/home/settings --> <SettingsPage extraTabs={[{ id: "agents", label, hint, icon: Bot }]}>{#snippet extra(id)}…{/snippet}</SettingsPage>
|
|
58
58
|
<!-- src/routes/home/manage_user --> <AdminView currentUserId={data.currentUserId} />
|
|
59
59
|
```
|
|
60
60
|
|
package/dist/messages.d.ts
CHANGED
package/dist/messages.js
CHANGED
|
@@ -63,6 +63,7 @@ export const en = {
|
|
|
63
63
|
noNewAccountOpen: "Accounts can't be created at the moment. Please try again later.",
|
|
64
64
|
loginTooOld: "Your login is more than a day old. Please log in again for this action.",
|
|
65
65
|
confirmEmailFirst: "Confirm your email address first: we just sent you a new link.",
|
|
66
|
+
loginFailed: "Login failed.",
|
|
66
67
|
resetLinkNotSent: "The reset link could not be sent.",
|
|
67
68
|
resetLinkSent: "If an account with this email address exists, a link to reset the password has been sent.",
|
|
68
69
|
resetLinkNotRequested: "The reset link could not be requested.",
|
|
@@ -306,6 +307,7 @@ export const de = {
|
|
|
306
307
|
noNewAccountOpen: "Im Moment können keine Konten erstellt werden. Bitte versuch es später wieder.",
|
|
307
308
|
loginTooOld: "Deine Anmeldung ist älter als einen Tag. Melde dich für diese Aktion bitte neu an.",
|
|
308
309
|
confirmEmailFirst: "Bestätige zuerst deine E-Mail-Adresse: Wir haben dir gerade einen neuen Link geschickt.",
|
|
310
|
+
loginFailed: "Die Anmeldung hat nicht geklappt.",
|
|
309
311
|
resetLinkNotSent: "Der Link zum Zurücksetzen konnte nicht verschickt werden.",
|
|
310
312
|
resetLinkSent: "Wenn es ein Konto mit dieser E-Mail-Adresse gibt, ist ein Link zum Zurücksetzen des Passworts unterwegs.",
|
|
311
313
|
resetLinkNotRequested: "Der Link zum Zurücksetzen konnte nicht angefordert werden.",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { m } from "../messages.js";
|
|
9
9
|
import { deleteOwnAccount } from "../remote/app_user.remote.js";
|
|
10
10
|
import { authUi } from "./context.js";
|
|
11
|
+
import { href } from "./href.js";
|
|
11
12
|
import { messageOf } from "../util/messageOf.js";
|
|
12
13
|
|
|
13
14
|
/** Deleting the own account; `agents` adds the sentence about connected AI assistants (the MCP endpoint). */
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
if (res.success) {
|
|
28
29
|
toast.success(m().accountDeleted);
|
|
29
30
|
await authClient.signOut().catch(() => undefined);
|
|
30
|
-
window.location.href = "/";
|
|
31
|
+
window.location.href = href("/");
|
|
31
32
|
} else {
|
|
32
33
|
toast.error(m().accountNotDeleted);
|
|
33
34
|
}
|
package/dist/ui/LoginForm.svelte
CHANGED
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
const providers = $derived(page.data.providers as { github?: boolean; google?: boolean } | undefined);
|
|
38
38
|
const mail = $derived(page.data.mail as boolean);
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
// The reason the URL brought along (a refused social signup, a stale login); a fresh attempt replaces it
|
|
41
|
+
let urlError = $derived(page.url.searchParams.get("error"));
|
|
41
42
|
// A new account through GitHub or Google was refused: why depends on the signup mode
|
|
42
43
|
const noNewAccount = $derived(signupMode === "open" ? m().noNewAccountOpen : m().noNewAccountInvite);
|
|
43
44
|
const URL_ERRORS = $derived<Record<string, string>>({
|
|
@@ -47,7 +48,8 @@
|
|
|
47
48
|
session_not_fresh: m().loginTooOld,
|
|
48
49
|
});
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
// Only known reasons are shown; an unknown code is not echoed (a link could plant any text here)
|
|
52
|
+
const displayError = $derived(urlError ? (URL_ERRORS[urlError.toLowerCase()] ?? m().loginFailed) : errorMessage);
|
|
51
53
|
|
|
52
54
|
async function requestPasswordReset(e: SubmitEvent) {
|
|
53
55
|
e.preventDefault();
|
|
@@ -57,7 +59,7 @@
|
|
|
57
59
|
}
|
|
58
60
|
sendingResetLink = true;
|
|
59
61
|
try {
|
|
60
|
-
const res = await authClient.requestPasswordReset({ email: forgotPasswordEmail, redirectTo: `${window.location.origin}/auth/reset-password` });
|
|
62
|
+
const res = await authClient.requestPasswordReset({ email: forgotPasswordEmail, redirectTo: `${window.location.origin}${href("/auth/reset-password")}` });
|
|
61
63
|
if (res.error) {
|
|
62
64
|
toast.error(res.error.message || m().resetLinkNotSent);
|
|
63
65
|
} else {
|
|
@@ -84,7 +86,7 @@
|
|
|
84
86
|
if (oauthRequest) {
|
|
85
87
|
// Fallback: with a session, the authorize endpoint continues the flow in the browser
|
|
86
88
|
const query = [...page.url.searchParams].filter(([key]) => key !== "sig" && key !== "exp");
|
|
87
|
-
window.location.href =
|
|
89
|
+
window.location.href = `${href("/api/auth/oauth2/authorize")}?${query.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&")}`;
|
|
88
90
|
return;
|
|
89
91
|
}
|
|
90
92
|
// A new login: queries cached before it (one that sent here to log in again resolved empty) load again
|
|
@@ -99,6 +101,7 @@
|
|
|
99
101
|
onRequest: () => {
|
|
100
102
|
loading = true;
|
|
101
103
|
errorMessage = "";
|
|
104
|
+
urlError = null;
|
|
102
105
|
},
|
|
103
106
|
onSuccess: async (ctx) => {
|
|
104
107
|
loading = false;
|
|
@@ -169,8 +172,8 @@
|
|
|
169
172
|
<div class="grid gap-6">
|
|
170
173
|
{#if providers?.github || providers?.google}
|
|
171
174
|
<div class="flex flex-col gap-4">
|
|
172
|
-
{#if providers.github}<SocialButton provider="github" callbackURL={redirectTo ?? "/home"} />{/if}
|
|
173
|
-
{#if providers.google}<SocialButton provider="google" callbackURL={redirectTo ?? "/home"} />{/if}
|
|
175
|
+
{#if providers.github}<SocialButton provider="github" callbackURL={redirectTo ?? href("/home")} />{/if}
|
|
176
|
+
{#if providers.google}<SocialButton provider="google" callbackURL={redirectTo ?? href("/home")} />{/if}
|
|
174
177
|
</div>
|
|
175
178
|
<div class="after:border-border relative text-center text-sm after:absolute after:inset-0 after:top-1/2 after:z-0 after:flex after:items-center after:border-t">
|
|
176
179
|
<span class="bg-card text-muted-foreground relative z-10 px-2">{m().orWithEmail}</span>
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
}
|
|
40
40
|
const name = email.split("@")[0]; // the server turns it into a readable name
|
|
41
41
|
await authClient.signUp.email(
|
|
42
|
-
{ email, password, name, callbackURL: "/home" },
|
|
42
|
+
{ email, password, name, callbackURL: href("/home") },
|
|
43
43
|
{
|
|
44
44
|
onRequest: () => {
|
|
45
45
|
loading = true;
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
import type { AppUserClient } from "../types.js";
|
|
9
9
|
import { authUi } from "./context.js";
|
|
10
10
|
import DeletionTab from "./DeletionTab.svelte";
|
|
11
|
-
import { href } from "./href.js";
|
|
12
11
|
import InvitationsTab from "./InvitationsTab.svelte";
|
|
13
12
|
import LinksTab from "./LinksTab.svelte";
|
|
14
13
|
import MobileSettingsMenu from "./MobileSettingsMenu.svelte";
|
|
@@ -48,7 +47,7 @@
|
|
|
48
47
|
const url = new URL(page.url.href);
|
|
49
48
|
if (tabName) url.searchParams.set("tab", tabName);
|
|
50
49
|
else url.searchParams.delete("tab");
|
|
51
|
-
goto(`${
|
|
50
|
+
goto(`${page.url.pathname}${url.search}`); // wherever the Product mounted this page
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
const memberSince = $derived(user.createdAt ? new Date(user.createdAt).toLocaleDateString(locale, { year: "numeric", month: "long" }) : "");
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
import { Button } from "../components/ui/button/index.js";
|
|
3
3
|
import { m } from "../messages.js";
|
|
4
4
|
import { authUi } from "./context.js";
|
|
5
|
+
import { href } from "./href.js";
|
|
5
6
|
import IconGithub from "./IconGithub.svelte";
|
|
6
7
|
import IconGoogle from "./IconGoogle.svelte";
|
|
7
8
|
|
|
8
9
|
/** "Continue with GitHub/Google": starts the social sign-in with the Product's client. */
|
|
9
|
-
let { provider, callbackURL = "/home" }: { provider: "github" | "google"; callbackURL?: string } = $props();
|
|
10
|
+
let { provider, callbackURL = href("/home") }: { provider: "github" | "google"; callbackURL?: string } = $props();
|
|
10
11
|
const { authClient } = authUi();
|
|
11
12
|
const names = { github: "GitHub", google: "Google" };
|
|
12
13
|
|
|
13
14
|
async function login() {
|
|
14
|
-
await authClient.signIn.social({ provider, callbackURL, errorCallbackURL: "/auth/login" });
|
|
15
|
+
await authClient.signIn.social({ provider, callbackURL, errorCallbackURL: href("/auth/login") });
|
|
15
16
|
}
|
|
16
17
|
</script>
|
|
17
18
|
|
|
@@ -98,11 +98,15 @@
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
function handleCopyBackupCodes() {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
async function handleCopyBackupCodes() {
|
|
102
|
+
try {
|
|
103
|
+
await navigator.clipboard.writeText(backupCodes.join("\n"));
|
|
104
|
+
isCopied = true;
|
|
105
|
+
toast.success(m().backupCodesCopied);
|
|
106
|
+
setTimeout(() => (isCopied = false), 2000);
|
|
107
|
+
} catch {
|
|
108
|
+
toast.error(m().copyLinkFailed);
|
|
109
|
+
}
|
|
106
110
|
}
|
|
107
111
|
</script>
|
|
108
112
|
|
package/dist/util/fresh.js
CHANGED
|
@@ -12,7 +12,7 @@ export function isSessionNotFresh(e) {
|
|
|
12
12
|
}
|
|
13
13
|
/** The login page that signs out a stale session and then returns to `redirectTo`. */
|
|
14
14
|
export function reauthPath(redirectTo) {
|
|
15
|
-
return `/auth/login?error=${SESSION_NOT_FRESH.toLowerCase()}&redirectTo=${encodeURIComponent(redirectTo)}`;
|
|
15
|
+
return `/auth/login?error=${SESSION_NOT_FRESH.toLowerCase()}&redirectTo=${encodeURIComponent(redirectTo)}`; // the Product's loads prefix the base path
|
|
16
16
|
}
|
|
17
17
|
/** better-auth's own check: a `freshAge` of 0 turns it off. */
|
|
18
18
|
export function isStale(createdAt, freshAgeSeconds, now = new Date()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vonall/auth",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Accounts for a Web App Factory Product: better-auth with the hooks, the protected helpers, Einladungen, Konto, the auth pages and the settings; Admin as an option",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -58,7 +58,10 @@
|
|
|
58
58
|
"neverthrow": "^8.2.0",
|
|
59
59
|
"pg": "^8.23.1",
|
|
60
60
|
"svelte": "^5.57.2",
|
|
61
|
-
"zod": "^4.6.7"
|
|
61
|
+
"zod": "^4.6.7",
|
|
62
|
+
"svelte-sonner": "^1.2.1",
|
|
63
|
+
"bits-ui": "^2.19.5",
|
|
64
|
+
"@lucide/svelte": "^1.22.0"
|
|
62
65
|
},
|
|
63
66
|
"devDependencies": {
|
|
64
67
|
"@better-auth/mcp": "^1.7.12",
|
|
@@ -78,7 +81,10 @@
|
|
|
78
81
|
"typescript": "^6.0.3",
|
|
79
82
|
"vite": "8.3.4",
|
|
80
83
|
"vitest": "^5.0.5",
|
|
81
|
-
"zod": "^4.6.7"
|
|
84
|
+
"zod": "^4.6.7",
|
|
85
|
+
"svelte-sonner": "^1.2.1",
|
|
86
|
+
"bits-ui": "^2.19.5",
|
|
87
|
+
"@lucide/svelte": "^1.22.0"
|
|
82
88
|
},
|
|
83
89
|
"imports": {
|
|
84
90
|
"#lib": "./src/lib/index.js",
|
|
@@ -91,11 +97,8 @@
|
|
|
91
97
|
"access": "public"
|
|
92
98
|
},
|
|
93
99
|
"dependencies": {
|
|
94
|
-
"@lucide/svelte": "1.22.0",
|
|
95
100
|
"better-auth": "^1.7.12",
|
|
96
|
-
"bits-ui": "^2.20.2",
|
|
97
101
|
"clsx": "^2.1.1",
|
|
98
|
-
"svelte-sonner": "^1.2.1",
|
|
99
102
|
"tailwind-merge": "^3.7.0",
|
|
100
103
|
"tailwind-variants": "^3.3.1",
|
|
101
104
|
"uqr": "^0.1.3",
|