@mrgnw/anahtar 0.0.12 → 0.0.14

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.
Files changed (173) hide show
  1. package/dist/components/AuthFlow.svelte +35 -1
  2. package/dist/components/OtpInput.svelte +15 -1
  3. package/dist/components/PasskeyPrompt.svelte +21 -3
  4. package/dist/i18n/af.d.ts +3 -0
  5. package/dist/i18n/af.js +28 -0
  6. package/dist/i18n/ak.d.ts +3 -0
  7. package/dist/i18n/ak.js +28 -0
  8. package/dist/i18n/am.d.ts +3 -0
  9. package/dist/i18n/am.js +28 -0
  10. package/dist/i18n/ar.js +1 -1
  11. package/dist/i18n/as.d.ts +3 -0
  12. package/dist/i18n/as.js +28 -0
  13. package/dist/i18n/az.d.ts +3 -0
  14. package/dist/i18n/az.js +28 -0
  15. package/dist/i18n/be.d.ts +3 -0
  16. package/dist/i18n/be.js +28 -0
  17. package/dist/i18n/bg.d.ts +3 -0
  18. package/dist/i18n/bg.js +28 -0
  19. package/dist/i18n/bn.d.ts +3 -0
  20. package/dist/i18n/bn.js +28 -0
  21. package/dist/i18n/ca.d.ts +3 -0
  22. package/dist/i18n/ca.js +28 -0
  23. package/dist/i18n/cs.d.ts +3 -0
  24. package/dist/i18n/cs.js +28 -0
  25. package/dist/i18n/da.d.ts +3 -0
  26. package/dist/i18n/da.js +28 -0
  27. package/dist/i18n/el.d.ts +3 -0
  28. package/dist/i18n/el.js +28 -0
  29. package/dist/i18n/es.js +1 -1
  30. package/dist/i18n/fa.d.ts +3 -0
  31. package/dist/i18n/fa.js +28 -0
  32. package/dist/i18n/ff.d.ts +3 -0
  33. package/dist/i18n/ff.js +28 -0
  34. package/dist/i18n/fi.d.ts +3 -0
  35. package/dist/i18n/fi.js +28 -0
  36. package/dist/i18n/fil.d.ts +3 -0
  37. package/dist/i18n/fil.js +28 -0
  38. package/dist/i18n/fr.js +1 -1
  39. package/dist/i18n/gu.d.ts +3 -0
  40. package/dist/i18n/gu.js +28 -0
  41. package/dist/i18n/ha.d.ts +3 -0
  42. package/dist/i18n/ha.js +28 -0
  43. package/dist/i18n/he.d.ts +3 -0
  44. package/dist/i18n/he.js +28 -0
  45. package/dist/i18n/hi.d.ts +3 -0
  46. package/dist/i18n/hi.js +28 -0
  47. package/dist/i18n/hr.d.ts +3 -0
  48. package/dist/i18n/hr.js +28 -0
  49. package/dist/i18n/ht.d.ts +3 -0
  50. package/dist/i18n/ht.js +28 -0
  51. package/dist/i18n/hu.d.ts +3 -0
  52. package/dist/i18n/hu.js +28 -0
  53. package/dist/i18n/hy.d.ts +3 -0
  54. package/dist/i18n/hy.js +28 -0
  55. package/dist/i18n/id.d.ts +3 -0
  56. package/dist/i18n/id.js +28 -0
  57. package/dist/i18n/ig.d.ts +3 -0
  58. package/dist/i18n/ig.js +28 -0
  59. package/dist/i18n/index.js +157 -1
  60. package/dist/i18n/it.d.ts +3 -0
  61. package/dist/i18n/it.js +28 -0
  62. package/dist/i18n/ja.js +1 -1
  63. package/dist/i18n/jv.d.ts +3 -0
  64. package/dist/i18n/jv.js +28 -0
  65. package/dist/i18n/kk.d.ts +3 -0
  66. package/dist/i18n/kk.js +28 -0
  67. package/dist/i18n/km.d.ts +3 -0
  68. package/dist/i18n/km.js +28 -0
  69. package/dist/i18n/kn.d.ts +3 -0
  70. package/dist/i18n/kn.js +28 -0
  71. package/dist/i18n/ko.js +1 -1
  72. package/dist/i18n/ku.d.ts +3 -0
  73. package/dist/i18n/ku.js +28 -0
  74. package/dist/i18n/ln.d.ts +3 -0
  75. package/dist/i18n/ln.js +28 -0
  76. package/dist/i18n/lo.d.ts +3 -0
  77. package/dist/i18n/lo.js +28 -0
  78. package/dist/i18n/mg.d.ts +3 -0
  79. package/dist/i18n/mg.js +28 -0
  80. package/dist/i18n/ml.d.ts +3 -0
  81. package/dist/i18n/ml.js +28 -0
  82. package/dist/i18n/mn.d.ts +3 -0
  83. package/dist/i18n/mn.js +28 -0
  84. package/dist/i18n/mr.d.ts +3 -0
  85. package/dist/i18n/mr.js +28 -0
  86. package/dist/i18n/ms.d.ts +3 -0
  87. package/dist/i18n/ms.js +28 -0
  88. package/dist/i18n/my.d.ts +3 -0
  89. package/dist/i18n/my.js +28 -0
  90. package/dist/i18n/ne.d.ts +3 -0
  91. package/dist/i18n/ne.js +28 -0
  92. package/dist/i18n/nl.d.ts +3 -0
  93. package/dist/i18n/nl.js +28 -0
  94. package/dist/i18n/no.d.ts +3 -0
  95. package/dist/i18n/no.js +28 -0
  96. package/dist/i18n/ny.d.ts +3 -0
  97. package/dist/i18n/ny.js +28 -0
  98. package/dist/i18n/om.d.ts +3 -0
  99. package/dist/i18n/om.js +28 -0
  100. package/dist/i18n/or.d.ts +3 -0
  101. package/dist/i18n/or.js +28 -0
  102. package/dist/i18n/pa.d.ts +3 -0
  103. package/dist/i18n/pa.js +28 -0
  104. package/dist/i18n/pl.d.ts +3 -0
  105. package/dist/i18n/pl.js +28 -0
  106. package/dist/i18n/ps.d.ts +3 -0
  107. package/dist/i18n/ps.js +28 -0
  108. package/dist/i18n/pt.js +1 -1
  109. package/dist/i18n/rn.d.ts +3 -0
  110. package/dist/i18n/rn.js +28 -0
  111. package/dist/i18n/ro.d.ts +3 -0
  112. package/dist/i18n/ro.js +28 -0
  113. package/dist/i18n/ru.d.ts +3 -0
  114. package/dist/i18n/ru.js +28 -0
  115. package/dist/i18n/rw.d.ts +3 -0
  116. package/dist/i18n/rw.js +28 -0
  117. package/dist/i18n/sd.d.ts +3 -0
  118. package/dist/i18n/sd.js +28 -0
  119. package/dist/i18n/si.d.ts +3 -0
  120. package/dist/i18n/si.js +28 -0
  121. package/dist/i18n/sk.d.ts +3 -0
  122. package/dist/i18n/sk.js +28 -0
  123. package/dist/i18n/so.d.ts +3 -0
  124. package/dist/i18n/so.js +28 -0
  125. package/dist/i18n/sq.d.ts +3 -0
  126. package/dist/i18n/sq.js +28 -0
  127. package/dist/i18n/sr.d.ts +3 -0
  128. package/dist/i18n/sr.js +28 -0
  129. package/dist/i18n/st.d.ts +3 -0
  130. package/dist/i18n/st.js +28 -0
  131. package/dist/i18n/su.d.ts +3 -0
  132. package/dist/i18n/su.js +28 -0
  133. package/dist/i18n/sv.d.ts +3 -0
  134. package/dist/i18n/sv.js +28 -0
  135. package/dist/i18n/sw.d.ts +3 -0
  136. package/dist/i18n/sw.js +28 -0
  137. package/dist/i18n/ta.d.ts +3 -0
  138. package/dist/i18n/ta.js +28 -0
  139. package/dist/i18n/te.d.ts +3 -0
  140. package/dist/i18n/te.js +28 -0
  141. package/dist/i18n/tg.d.ts +3 -0
  142. package/dist/i18n/tg.js +28 -0
  143. package/dist/i18n/th.d.ts +3 -0
  144. package/dist/i18n/th.js +28 -0
  145. package/dist/i18n/ti.d.ts +3 -0
  146. package/dist/i18n/ti.js +28 -0
  147. package/dist/i18n/tk.d.ts +3 -0
  148. package/dist/i18n/tk.js +28 -0
  149. package/dist/i18n/tr.js +1 -1
  150. package/dist/i18n/ts.d.ts +3 -0
  151. package/dist/i18n/ts.js +28 -0
  152. package/dist/i18n/tt.d.ts +3 -0
  153. package/dist/i18n/tt.js +28 -0
  154. package/dist/i18n/ug.d.ts +3 -0
  155. package/dist/i18n/ug.js +28 -0
  156. package/dist/i18n/uk.d.ts +3 -0
  157. package/dist/i18n/uk.js +28 -0
  158. package/dist/i18n/ur.d.ts +3 -0
  159. package/dist/i18n/ur.js +28 -0
  160. package/dist/i18n/uz.d.ts +3 -0
  161. package/dist/i18n/uz.js +28 -0
  162. package/dist/i18n/vi.d.ts +3 -0
  163. package/dist/i18n/vi.js +28 -0
  164. package/dist/i18n/yo.d.ts +3 -0
  165. package/dist/i18n/yo.js +28 -0
  166. package/dist/i18n/zh.js +1 -1
  167. package/dist/i18n/zu.d.ts +3 -0
  168. package/dist/i18n/zu.js +28 -0
  169. package/dist/kit/handlers.d.ts +2 -2
  170. package/dist/kit/handlers.js +61 -46
  171. package/dist/passkey.d.ts +1 -0
  172. package/dist/passkey.js +22 -0
  173. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
- import { json } from '@sveltejs/kit';
2
- import { generateOTP, verifyOTP } from '../otp.js';
3
- import { generateAuthenticationChallenge, generateRegistrationChallenge, removePasskey, verifyAuthenticationResponse, verifyRegistrationResponse } from '../passkey.js';
4
- import { createSession, invalidateSession, validateSession } from '../session.js';
5
- import { resolveMessages, detectLocaleServer } from '../i18n/index.js';
1
+ import { json } from "@sveltejs/kit";
2
+ import { generateOTP, verifyOTP } from "../otp.js";
3
+ import { generateAuthenticationChallenge, generateAuthenticationChallengeForUser, generateRegistrationChallenge, removePasskey, verifyAuthenticationResponse, verifyRegistrationResponse, } from "../passkey.js";
4
+ import { createSession, invalidateSession, validateSession, } from "../session.js";
5
+ import { resolveMessages, detectLocaleServer, } from "../i18n/index.js";
6
6
  const SESSION_MAX_AGE_SECONDS = 30 * 24 * 60 * 60;
7
7
  function getMessages(event, config) {
8
8
  const locale = config.locale ?? detectLocaleServer(event.request);
@@ -19,32 +19,36 @@ export function createHandlers(config) {
19
19
  function cookieOpts(event) {
20
20
  return {
21
21
  httpOnly: true,
22
- secure: event.url.protocol === 'https:',
23
- sameSite: 'lax',
24
- path: '/',
25
- maxAge
22
+ secure: event.url.protocol === "https:",
23
+ sameSite: "lax",
24
+ path: "/",
25
+ maxAge,
26
26
  };
27
27
  }
28
28
  const routes = {
29
29
  start: {
30
- method: 'POST',
30
+ method: "POST",
31
31
  handler: async (event) => {
32
32
  const m = getMessages(event, config);
33
33
  const body = await event.request.json().catch(() => null);
34
- if (!body || typeof body.email !== 'string' || !body.email.includes('@')) {
34
+ if (!body ||
35
+ typeof body.email !== "string" ||
36
+ !body.email.includes("@")) {
35
37
  return json({ error: m.errorInvalidEmail }, { status: 400 });
36
38
  }
37
39
  const { code } = await generateOTP(config.db, body.email, config);
38
40
  await config.onSendOTP(body.email, code);
39
41
  return json({ success: true });
40
- }
42
+ },
41
43
  },
42
44
  verify: {
43
- method: 'POST',
45
+ method: "POST",
44
46
  handler: async (event) => {
45
47
  const m = getMessages(event, config);
46
48
  const body = await event.request.json().catch(() => null);
47
- if (!body || typeof body.email !== 'string' || typeof body.code !== 'string') {
49
+ if (!body ||
50
+ typeof body.email !== "string" ||
51
+ typeof body.code !== "string") {
48
52
  return json({ error: m.errorInvalidInput }, { status: 400 });
49
53
  }
50
54
  const otp = await verifyOTP(config.db, body.email, body.code, config);
@@ -54,7 +58,7 @@ export function createHandlers(config) {
54
58
  expired: m.errorCodeExpired,
55
59
  rate_limited: m.errorTooManyAttempts,
56
60
  };
57
- return json({ error: messages[otp.error] }, { status: otp.error === 'rate_limited' ? 429 : 400 });
61
+ return json({ error: messages[otp.error] }, { status: otp.error === "rate_limited" ? 429 : 400 });
58
62
  }
59
63
  let user = await config.db.getUserByEmail(body.email);
60
64
  if (!user) {
@@ -66,12 +70,12 @@ export function createHandlers(config) {
66
70
  return json({
67
71
  user: { id: user.id, email: user.email },
68
72
  hasPasskey: passkeys.length > 0,
69
- skipPasskeyPrompt: user.skipPasskeyPrompt
73
+ skipPasskeyPrompt: user.skipPasskeyPrompt,
70
74
  });
71
- }
75
+ },
72
76
  },
73
77
  logout: {
74
- method: 'POST',
78
+ method: "POST",
75
79
  handler: async (event) => {
76
80
  const token = event.cookies.get(config.cookie);
77
81
  if (token) {
@@ -79,20 +83,31 @@ export function createHandlers(config) {
79
83
  if (result) {
80
84
  await invalidateSession(config.db, result.session.id);
81
85
  }
82
- event.cookies.delete(config.cookie, { path: '/' });
86
+ event.cookies.delete(config.cookie, { path: "/" });
83
87
  }
84
88
  return json({ ok: true });
85
- }
89
+ },
86
90
  },
87
- 'passkey/login-start': {
88
- method: 'GET',
91
+ "passkey/login-start": {
92
+ method: "GET",
89
93
  handler: async (event) => {
90
94
  const options = await generateAuthenticationChallenge(config.db, event.url);
91
95
  return json(options);
92
- }
96
+ },
97
+ },
98
+ "passkey/check-email": {
99
+ method: "POST",
100
+ handler: async (event) => {
101
+ const body = await event.request.json().catch(() => null);
102
+ const email = body?.email;
103
+ if (!email || typeof email !== "string")
104
+ return json({ allowCredentials: [] });
105
+ const options = await generateAuthenticationChallengeForUser(config.db, email, event.url);
106
+ return json(options);
107
+ },
93
108
  },
94
- 'passkey/login-finish': {
95
- method: 'POST',
109
+ "passkey/login-finish": {
110
+ method: "POST",
96
111
  handler: async (event) => {
97
112
  const m = getMessages(event, config);
98
113
  const body = await event.request.json().catch(() => null);
@@ -104,10 +119,10 @@ export function createHandlers(config) {
104
119
  const session = await createSession(config.db, result.user.id, config);
105
120
  event.cookies.set(config.cookie, session.sessionToken, cookieOpts(event));
106
121
  return json({ user: result.user });
107
- }
122
+ },
108
123
  },
109
- 'passkey/register-start': {
110
- method: 'POST',
124
+ "passkey/register-start": {
125
+ method: "POST",
111
126
  handler: async (event) => {
112
127
  const m = getMessages(event, config);
113
128
  const user = requireAuth(event, m);
@@ -115,10 +130,10 @@ export function createHandlers(config) {
115
130
  return user;
116
131
  const options = await generateRegistrationChallenge(config.db, user, event.url, config);
117
132
  return json(options);
118
- }
133
+ },
119
134
  },
120
- 'passkey/register-finish': {
121
- method: 'POST',
135
+ "passkey/register-finish": {
136
+ method: "POST",
122
137
  handler: async (event) => {
123
138
  const m = getMessages(event, config);
124
139
  const user = requireAuth(event, m);
@@ -128,34 +143,34 @@ export function createHandlers(config) {
128
143
  if (!body)
129
144
  return json({ error: m.errorInvalidInput }, { status: 400 });
130
145
  const { name, ...response } = body;
131
- const passkeyName = typeof name === 'string' && name.trim() ? name.trim() : null;
146
+ const passkeyName = typeof name === "string" && name.trim() ? name.trim() : null;
132
147
  const result = await verifyRegistrationResponse(config.db, user.id, response, event.url, passkeyName);
133
148
  if (!result.ok) {
134
- console.error('register-finish failed:', result.reason);
149
+ console.error("register-finish failed:", result.reason);
135
150
  return json({ error: m.errorPasskeyRegFailed, reason: result.reason }, { status: 400 });
136
151
  }
137
152
  return json({ success: true });
138
- }
153
+ },
139
154
  },
140
- 'passkey/remove': {
141
- method: 'POST',
155
+ "passkey/remove": {
156
+ method: "POST",
142
157
  handler: async (event) => {
143
158
  const m = getMessages(event, config);
144
159
  const user = requireAuth(event, m);
145
160
  if (user instanceof Response)
146
161
  return user;
147
162
  const body = await event.request.json().catch(() => null);
148
- if (!body || typeof body.passkeyId !== 'string') {
163
+ if (!body || typeof body.passkeyId !== "string") {
149
164
  return json({ error: m.errorInvalidInput }, { status: 400 });
150
165
  }
151
166
  const success = await removePasskey(config.db, body.passkeyId, user.id);
152
167
  if (!success)
153
168
  return json({ error: m.errorPasskeyNotFound }, { status: 404 });
154
169
  return json({ success: true });
155
- }
170
+ },
156
171
  },
157
- 'skip-passkey': {
158
- method: 'POST',
172
+ "skip-passkey": {
173
+ method: "POST",
159
174
  handler: async (event) => {
160
175
  const m = getMessages(event, config);
161
176
  const user = requireAuth(event, m);
@@ -163,12 +178,12 @@ export function createHandlers(config) {
163
178
  return user;
164
179
  await config.db.setSkipPasskeyPrompt(user.id, true);
165
180
  return json({ success: true });
166
- }
167
- }
181
+ },
182
+ },
168
183
  };
169
184
  function getRoute(event) {
170
185
  const path = event.params.path;
171
- if (typeof path === 'string')
186
+ if (typeof path === "string")
172
187
  return path;
173
188
  return null;
174
189
  }
@@ -179,7 +194,7 @@ export function createHandlers(config) {
179
194
  if (!path)
180
195
  return json({ error: m.errorNotFound }, { status: 404 });
181
196
  const route = routes[path];
182
- if (!route || route.method !== 'GET') {
197
+ if (!route || route.method !== "GET") {
183
198
  return json({ error: m.errorNotFound }, { status: 404 });
184
199
  }
185
200
  return route.handler(event);
@@ -190,10 +205,10 @@ export function createHandlers(config) {
190
205
  if (!path)
191
206
  return json({ error: m.errorNotFound }, { status: 404 });
192
207
  const route = routes[path];
193
- if (!route || route.method !== 'POST') {
208
+ if (!route || route.method !== "POST") {
194
209
  return json({ error: m.errorNotFound }, { status: 404 });
195
210
  }
196
211
  return route.handler(event);
197
- }
212
+ },
198
213
  };
199
214
  }
package/dist/passkey.d.ts CHANGED
@@ -14,6 +14,7 @@ export declare function verifyRegistrationResponse(db: AuthDB, userId: string, r
14
14
  ok: false;
15
15
  reason: string;
16
16
  }>;
17
+ export declare function generateAuthenticationChallengeForUser(db: AuthDB, email: string, requestUrl: URL): Promise<import("@simplewebauthn/server").PublicKeyCredentialRequestOptionsJSON>;
17
18
  export declare function generateAuthenticationChallenge(db: AuthDB, requestUrl: URL): Promise<import("@simplewebauthn/server").PublicKeyCredentialRequestOptionsJSON>;
18
19
  export declare function verifyAuthenticationResponse(db: AuthDB, response: AuthenticationResponseJSON, requestUrl: URL): Promise<{
19
20
  user: {
package/dist/passkey.js CHANGED
@@ -81,6 +81,28 @@ export async function verifyRegistrationResponse(db, userId, response, requestUr
81
81
  return { ok: false, reason: `verification threw: ${e}` };
82
82
  }
83
83
  }
84
+ export async function generateAuthenticationChallengeForUser(db, email, requestUrl) {
85
+ const { rpID } = getWebAuthnConfig(requestUrl);
86
+ const user = await db.getUserByEmail(email);
87
+ if (!user)
88
+ return generateAuthenticationChallenge(db, requestUrl);
89
+ const passkeys = await db.getUserPasskeys(user.id);
90
+ if (passkeys.length === 0)
91
+ return generateAuthenticationChallenge(db, requestUrl);
92
+ const allowCredentials = passkeys.map((pk) => ({
93
+ id: pk.credentialId,
94
+ transports: pk.transports
95
+ ? JSON.parse(pk.transports)
96
+ : undefined,
97
+ }));
98
+ const options = await generateAuthenticationOptions({
99
+ rpID,
100
+ allowCredentials,
101
+ userVerification: "preferred",
102
+ });
103
+ await db.storeChallenge(options.challenge, user.id, Date.now() + CHALLENGE_EXPIRY_MS);
104
+ return options;
105
+ }
84
106
  export async function generateAuthenticationChallenge(db, requestUrl) {
85
107
  const { rpID } = getWebAuthnConfig(requestUrl);
86
108
  const options = await generateAuthenticationOptions({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrgnw/anahtar",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "description": "Opinionated, reusable auth for SvelteKit. Email+OTP + passkeys.",
5
5
  "license": "MIT",
6
6
  "type": "module",