diff --git a/bun.lock b/bun.lock index 33a18ea..b811409 100644 --- a/bun.lock +++ b/bun.lock @@ -15,7 +15,7 @@ "@types/node": "^24", "autoprefixer": "^10.4.20", "better-auth": "~1.4.21", - "bits-ui": "^1.3.19", + "bits-ui": "^1.4.7", "clsx": "^2.1.1", "drizzle-kit": "^0.31.8", "drizzle-orm": "^0.45.2", diff --git a/components.json b/components.json index 260ea62..7413c04 100644 --- a/components.json +++ b/components.json @@ -1,8 +1,6 @@ { "$schema": "https://next.shadcn-svelte.com/schema.json", - "style": "new-york", "tailwind": { - "config": "tailwind.config.ts", "css": "src/app.css", "baseColor": "neutral" }, @@ -10,8 +8,9 @@ "components": "$lib/components", "utils": "$lib/utils", "ui": "$lib/components/ui", - "hooks": "$lib/hooks" + "hooks": "$lib/hooks", + "lib": "$lib" }, "typescript": true, - "registry": "https://next.shadcn-svelte.com/registry" + "registry": "https://tw3.shadcn-svelte.com/registry/new-york" } diff --git a/package.json b/package.json index 1a9cc67..3bf2968 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ }, "devDependencies": { "@better-auth/cli": "~1.4.21", + "@better-auth/passkey": "~1.4.21", "@cloudflare/workers-types": "^4.20250517.0", "@lucide/svelte": "^0.492.0", "@sveltejs/adapter-cloudflare": "^7.2.6", @@ -32,9 +33,12 @@ "@sveltejs/vite-plugin-svelte": "^5.0.0", "@types/node": "^24", "autoprefixer": "^10.4.20", - "bits-ui": "^1.3.19", + "better-auth": "~1.4.21", + "bits-ui": "^1.4.7", "clsx": "^2.1.1", "drizzle-kit": "^0.31.8", + "drizzle-orm": "^0.45.2", + "mode-watcher": "^1.0.5", "prettier": "^3.5.3", "prettier-plugin-svelte": "^3.3.3", "svelte": "^5.0.0", @@ -46,11 +50,7 @@ "tailwindcss-animate": "^1.0.7", "typescript": "^5.0.0", "vite": "^6.0.0", - "wrangler": "^4.63.0", - "better-auth": "~1.4.21", - "drizzle-orm": "^0.45.2", - "mode-watcher": "^1.0.5", - "@better-auth/passkey": "~1.4.21" + "wrangler": "^4.63.0" }, "packageManager": "bun@1.3.5" } diff --git a/src/lib/auth-client.ts b/src/lib/auth-client.ts index 64713ba..eb0d5a5 100644 --- a/src/lib/auth-client.ts +++ b/src/lib/auth-client.ts @@ -1,7 +1,22 @@ +import type { BetterAuthClientPlugin } from 'better-auth/client'; +import { anonymousClient, inferAdditionalFields } from 'better-auth/client/plugins'; import { createAuthClient } from 'better-auth/svelte'; import { passkeyClient } from '@better-auth/passkey/client'; +import type { auth } from '$lib/server/auth'; + +const accountNumberClient = { + id: 'account-number', + getActions: ($fetch) => ({ + signInAccountNumber: async (accountNumber: string) => + $fetch('/sign-in/account-number', { + method: 'POST', + body: { + accountNumber, + }, + }), + }), +} satisfies BetterAuthClientPlugin; export const authClient = createAuthClient({ - plugins: [passkeyClient()], + plugins: [inferAdditionalFields(), anonymousClient(), passkeyClient(), accountNumberClient], }); - diff --git a/src/lib/components/app/auth-bar.svelte b/src/lib/components/app/auth-bar.svelte index ce45dcd..0e173fc 100644 --- a/src/lib/components/app/auth-bar.svelte +++ b/src/lib/components/app/auth-bar.svelte @@ -1,39 +1,9 @@
- {#if $session.data} -
-

Signed in as {$session.data.user.name}

- {#if getAccountNumber()} -

#{getAccountNumber()}

- {/if} -
- - {:else} - - {/if} +
diff --git a/src/lib/components/app/auth-dialog.svelte b/src/lib/components/app/auth-dialog.svelte new file mode 100644 index 0000000..cbd808f --- /dev/null +++ b/src/lib/components/app/auth-dialog.svelte @@ -0,0 +1,261 @@ + + + + + + + + + + {user ? 'Your account' : 'Log into lofi.srizan.dev'} + + + {#if user} +
+
+
+
+ +
+
+

{user.name}

+

Account #{user.accountNumber}

+
+
+
+ + {#if passkeys.length > 0} +
+

Your passkeys

+
+ {#each passkeys as passkey (passkey.id)} +
+ +
+

{passkey.name}

+

Added on {new Date(passkey.createdAt).toLocaleDateString()}

+
+ +
+ {/each} +
+
+ {/if} + +
+

Security

+ + + {#if passkeyMessage} +

{passkeyMessage}

+ {/if} +
+ + +
+ {:else} +
+
+ +
+ + +
+
+ + + + {#if authMessage} +

{authMessage}

+ {/if} +
+ {/if} +
+
diff --git a/src/lib/components/ui/input/index.ts b/src/lib/components/ui/input/index.ts new file mode 100644 index 0000000..f47b6d3 --- /dev/null +++ b/src/lib/components/ui/input/index.ts @@ -0,0 +1,7 @@ +import Root from "./input.svelte"; + +export { + Root, + // + Root as Input, +}; diff --git a/src/lib/components/ui/input/input.svelte b/src/lib/components/ui/input/input.svelte new file mode 100644 index 0000000..78c7760 --- /dev/null +++ b/src/lib/components/ui/input/input.svelte @@ -0,0 +1,46 @@ + + +{#if type === "file"} + +{:else} + +{/if} diff --git a/src/lib/components/ui/label/index.ts b/src/lib/components/ui/label/index.ts new file mode 100644 index 0000000..8bfca0b --- /dev/null +++ b/src/lib/components/ui/label/index.ts @@ -0,0 +1,7 @@ +import Root from "./label.svelte"; + +export { + Root, + // + Root as Label, +}; diff --git a/src/lib/components/ui/label/label.svelte b/src/lib/components/ui/label/label.svelte new file mode 100644 index 0000000..247d23c --- /dev/null +++ b/src/lib/components/ui/label/label.svelte @@ -0,0 +1,19 @@ + + + diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index 8e97d31..3d04738 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -14,6 +14,8 @@ import * as z from 'zod'; const generateAccountNumber = () => Array.from(crypto.getRandomValues(new Uint8Array(16)), (value) => (value % 10).toString()).join(''); +const generateOpaqueIdentifier = () => `${crypto.randomUUID()}@internal.invalid`; + const accountNumber = () => ({ id: 'account-number', @@ -35,17 +37,7 @@ const accountNumber = () => value: ctx.body.accountNumber, }, ], - })) as - | ({ - id: string; - createdAt: Date; - updatedAt: Date; - email: string; - emailVerified: boolean; - name: string; - image?: string | null; - } & Record) - | null; + })) as (Record | null); if (!user) { throw new APIError('UNAUTHORIZED', { @@ -60,7 +52,10 @@ const accountNumber = () => }); } - await setSessionCookie(ctx, { session, user }); + await setSessionCookie( + ctx, + { session, user } as Parameters[1], + ); return ctx.json({ token: session.token, @@ -74,7 +69,6 @@ const accountNumber = () => const authConfig = { baseURL: env.ORIGIN, secret: env.BETTER_AUTH_SECRET, - emailAndPassword: { enabled: false }, user: { additionalFields: { accountNumber: { @@ -98,7 +92,7 @@ const authConfig = { plugins: [ anonymous({ generateName: () => 'Chillhop listener', - emailDomainName: 'accounts.chillhop.local', + generateRandomEmail: generateOpaqueIdentifier, }), accountNumber(), passkey({ diff --git a/src/routes/demo/better-auth/login/+page.server.ts b/src/routes/demo/better-auth/login/+page.server.ts index 4669822..107ef62 100644 --- a/src/routes/demo/better-auth/login/+page.server.ts +++ b/src/routes/demo/better-auth/login/+page.server.ts @@ -1,6 +1,5 @@ import { fail, redirect } from '@sveltejs/kit'; -import type { Actions } from './$types'; -import type { PageServerLoad } from './$types'; +import type { Actions, PageServerLoad } from './$types'; import { APIError } from 'better-auth';