@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 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={[agentsTab()]}>{#snippet extra(id)}…{/snippet}</SettingsPage>
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
 
@@ -57,6 +57,7 @@ export declare const en: {
57
57
  noNewAccountOpen: string;
58
58
  loginTooOld: string;
59
59
  confirmEmailFirst: string;
60
+ loginFailed: string;
60
61
  resetLinkNotSent: string;
61
62
  resetLinkSent: string;
62
63
  resetLinkNotRequested: string;
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
  }
@@ -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
- const urlError = $derived(page.url.searchParams.get("error"));
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
- const displayError = $derived(urlError ? (URL_ERRORS[urlError.toLowerCase()] ?? urlError) : errorMessage);
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 = `/api/auth/oauth2/authorize?${query.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&")}`;
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(`${href("/home/settings")}${url.search}`);
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
- navigator.clipboard.writeText(backupCodes.join("\n"));
103
- isCopied = true;
104
- toast.success(m().backupCodesCopied);
105
- setTimeout(() => (isCopied = false), 2000);
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
 
@@ -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.0",
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",