@mrgnw/anahtar 0.0.27 → 0.0.29-diagnostic.0

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 CHANGED
@@ -1,4 +1,4 @@
1
- # @mrgnw/anahtar
1
+ # anahtar: incredibly simple & secure auth with passkeys and email+otp fallback
2
2
 
3
3
  > **Pre-alpha.** API may change between releases.
4
4
 
@@ -13,6 +13,12 @@ Auth for SvelteKit. Email+OTP + optional passkeys.
13
13
  3. First login -> passkey registration prompt
14
14
  4. Future logins -> passkey autofill, OTP fallback
15
15
 
16
+ ## Roadmap: Proof-of-concept: everything can change
17
+ The goal is simplicity, we will likely stay with passkeys and email+OTP only.
18
+
19
+ - we may change the implemenation.
20
+ - we will likely split the UI elements from the API, making it easier to run headless, just use the UI, or use both.
21
+
16
22
  ## Quick start
17
23
 
18
24
  ```sh
@@ -25,6 +25,8 @@ interface Props {
25
25
  onStepChange?: (step: 'email' | 'otp' | 'authenticated') => void;
26
26
  /** Extra inline icons rendered before the sign-out button when authenticated. */
27
27
  actions?: Snippet;
28
+ /** Show `·` dot separators between pill segments. Defaults to false (flex gap only). */
29
+ separators?: boolean;
28
30
  }
29
31
 
30
32
  let {
@@ -39,6 +41,7 @@ let {
39
41
  getPasskeys,
40
42
  onStepChange,
41
43
  actions,
44
+ separators = false,
42
45
  }: Props = $props();
43
46
 
44
47
  let expanded = $state(false);
@@ -348,7 +351,7 @@ async function removePasskey(id: string) {
348
351
  </button>
349
352
  {:else if isAuthenticated}
350
353
  <span class="anahtar-pill-email">{user?.email}</span>
351
- <span class="anahtar-pill-sep">&middot;</span>
354
+ {#if separators}<span class="anahtar-pill-sep">&middot;</span>{/if}
352
355
  {#if getPasskeys}
353
356
  <button
354
357
  class="anahtar-pill-icon"
@@ -361,11 +364,11 @@ async function removePasskey(id: string) {
361
364
  <circle cx="7.5" cy="15.5" r="5.5"/><path d="m11.5 12 4-4"/><path d="m15 7 2 2"/><path d="m17.5 4.5 2 2"/>
362
365
  </svg>
363
366
  </button>
364
- <span class="anahtar-pill-sep">&middot;</span>
367
+ {#if separators}<span class="anahtar-pill-sep">&middot;</span>{/if}
365
368
  {/if}
366
369
  {#if actions}
367
370
  {@render actions()}
368
- <span class="anahtar-pill-sep">&middot;</span>
371
+ {#if separators}<span class="anahtar-pill-sep">&middot;</span>{/if}
369
372
  {/if}
370
373
  <button class="anahtar-pill-icon anahtar-pill-signout" onclick={handleSignOut} title="Sign out" disabled={loading}>
371
374
  <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
@@ -373,7 +376,7 @@ async function removePasskey(id: string) {
373
376
  </svg>
374
377
  </button>
375
378
  {#if compact}
376
- <span class="anahtar-pill-sep">&middot;</span>
379
+ {#if separators}<span class="anahtar-pill-sep">&middot;</span>{/if}
377
380
  <button class="anahtar-pill-icon" onclick={() => { expanded = false; showPasskeys = false; }} title="Collapse">
378
381
  <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
379
382
  <line x1="18" x2="6" y1="6" y2="18"/><line x1="6" x2="18" y1="6" y2="18"/>
@@ -383,7 +386,7 @@ async function removePasskey(id: string) {
383
386
 
384
387
  {:else if otpStep}
385
388
  <span class="anahtar-pill-otp-label">{email}</span>
386
- <span class="anahtar-pill-sep">&middot;</span>
389
+ {#if separators}<span class="anahtar-pill-sep">&middot;</span>{/if}
387
390
  <div class="anahtar-pill-otp-boxes">
388
391
  {#each otpDigits as _, i}
389
392
  <input
@@ -21,6 +21,8 @@ interface Props {
21
21
  onStepChange?: (step: 'email' | 'otp' | 'authenticated') => void;
22
22
  /** Extra inline icons rendered before the sign-out button when authenticated. */
23
23
  actions?: Snippet;
24
+ /** Show `·` dot separators between pill segments. Defaults to false (flex gap only). */
25
+ separators?: boolean;
24
26
  }
25
27
  declare const AuthPill: import("svelte").Component<Props, {}, "">;
26
28
  type AuthPill = ReturnType<typeof AuthPill>;
package/dist/passkey.js CHANGED
@@ -121,13 +121,25 @@ export async function generateAuthenticationChallenge(db, requestUrl, config) {
121
121
  }
122
122
  export async function verifyAuthenticationResponse(db, response, requestUrl, config) {
123
123
  const { rpID, origin } = getWebAuthnConfig(requestUrl, config);
124
+ const idHint = `${response.id.slice(0, 8)}…`;
124
125
  const passkey = await db.getPasskeyByCredentialId(response.id);
125
- if (!passkey)
126
+ if (!passkey) {
127
+ console.warn(`[anahtar login-finish] no stored passkey for credentialId=${idHint} (rpID=${rpID})`);
126
128
  return null;
127
- const challenge = JSON.parse(Buffer.from(response.response.clientDataJSON, "base64url").toString()).challenge;
129
+ }
130
+ let challenge;
131
+ try {
132
+ challenge = JSON.parse(Buffer.from(response.response.clientDataJSON, "base64url").toString()).challenge;
133
+ }
134
+ catch (e) {
135
+ console.warn(`[anahtar login-finish] clientDataJSON parse failed: ${e}`);
136
+ return null;
137
+ }
128
138
  const stored = await db.consumeChallenge(challenge);
129
- if (!stored)
139
+ if (!stored) {
140
+ console.warn(`[anahtar login-finish] challenge not found or expired (credentialId=${idHint})`);
130
141
  return null;
142
+ }
131
143
  try {
132
144
  const verification = await verifyAuthResponse({
133
145
  response,
@@ -144,14 +156,17 @@ export async function verifyAuthenticationResponse(db, response, requestUrl, con
144
156
  : undefined,
145
157
  },
146
158
  });
147
- if (!verification.verified)
159
+ if (!verification.verified) {
160
+ console.warn(`[anahtar login-finish] not verified (credentialId=${idHint}, expectedRPID=${rpID}, expectedOrigin=${origin})`);
148
161
  return null;
162
+ }
149
163
  await db.updatePasskeyCounter(passkey.id, verification.authenticationInfo.newCounter);
150
164
  return {
151
165
  user: { id: passkey.userId, email: passkey.email },
152
166
  };
153
167
  }
154
- catch {
168
+ catch (e) {
169
+ console.warn(`[anahtar login-finish] verification threw (credentialId=${idHint}, expectedRPID=${rpID}, expectedOrigin=${origin}): ${e}`);
155
170
  return null;
156
171
  }
157
172
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrgnw/anahtar",
3
- "version": "0.0.27",
3
+ "version": "0.0.29-diagnostic.0",
4
4
  "description": "Opinionated, reusable auth for SvelteKit. Email+OTP + passkeys.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -12,6 +12,14 @@
12
12
  "registry": "https://registry.npmjs.org/",
13
13
  "access": "public"
14
14
  },
15
+ "scripts": {
16
+ "build": "svelte-package",
17
+ "check": "svelte-check --tsconfig ./tsconfig.json",
18
+ "test": "vitest run --config vitest.unit.ts && vitest run --config vitest.browser.ts",
19
+ "test:unit": "vitest run --config vitest.unit.ts",
20
+ "test:browser": "vitest run --config vitest.browser.ts",
21
+ "prepublishOnly": "pnpm build"
22
+ },
15
23
  "svelte": "./dist/index.js",
16
24
  "types": "./dist/index.d.ts",
17
25
  "exports": {
@@ -60,6 +68,11 @@
60
68
  "@oslojs/encoding": "^1.1.0",
61
69
  "@simplewebauthn/server": "^13.2.2"
62
70
  },
71
+ "pnpm": {
72
+ "onlyBuiltDependencies": [
73
+ "better-sqlite3"
74
+ ]
75
+ },
63
76
  "devDependencies": {
64
77
  "@simplewebauthn/browser": "^13.2.2",
65
78
  "@sveltejs/kit": "^2.31.1",
@@ -75,12 +88,5 @@
75
88
  "svelte-check": "^4.3.1",
76
89
  "typescript": "^5.9.2",
77
90
  "vitest": "^3.2.1"
78
- },
79
- "scripts": {
80
- "build": "svelte-package",
81
- "check": "svelte-check --tsconfig ./tsconfig.json",
82
- "test": "vitest run --config vitest.unit.ts && vitest run --config vitest.browser.ts",
83
- "test:unit": "vitest run --config vitest.unit.ts",
84
- "test:browser": "vitest run --config vitest.browser.ts"
85
91
  }
86
- }
92
+ }