@mrgnw/anahtar 0.0.29-diagnostic.0 → 0.0.29
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/dist/components/AuthFlow.svelte +3 -0
- package/dist/components/AuthPill.svelte +3 -0
- package/dist/db/d1.js +10 -8
- package/dist/db/postgres.js +8 -6
- package/dist/db/sqlite.js +10 -8
- package/dist/email.d.ts +1 -0
- package/dist/email.js +6 -0
- package/package.json +2 -1
|
@@ -412,6 +412,9 @@ async function removePasskey(id: string) {
|
|
|
412
412
|
placeholder={m.emailPlaceholder}
|
|
413
413
|
class="anahtar-pill-email-input"
|
|
414
414
|
autocomplete="username webauthn"
|
|
415
|
+
autocapitalize="none"
|
|
416
|
+
autocorrect="off"
|
|
417
|
+
spellcheck="false"
|
|
415
418
|
disabled={loading}
|
|
416
419
|
/>
|
|
417
420
|
<button type="submit" class="anahtar-pill-go" disabled={loading || !email.includes('@')}>
|
package/dist/db/d1.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeEmail } from '../email.js';
|
|
1
2
|
export function d1Adapter(db, options = {}) {
|
|
2
3
|
const p = options.tablePrefix ?? 'auth_';
|
|
3
4
|
const t = {
|
|
@@ -45,8 +46,8 @@ export function d1Adapter(db, options = {}) {
|
|
|
45
46
|
},
|
|
46
47
|
async getUserByEmail(email) {
|
|
47
48
|
const row = await db
|
|
48
|
-
.prepare(`SELECT id, email, skip_passkey_prompt, created_at FROM ${t.users} WHERE email =
|
|
49
|
-
.bind(email)
|
|
49
|
+
.prepare(`SELECT id, email, skip_passkey_prompt, created_at FROM ${t.users} WHERE email = ? COLLATE NOCASE`)
|
|
50
|
+
.bind(normalizeEmail(email))
|
|
50
51
|
.first();
|
|
51
52
|
if (!row)
|
|
52
53
|
return null;
|
|
@@ -59,10 +60,11 @@ export function d1Adapter(db, options = {}) {
|
|
|
59
60
|
},
|
|
60
61
|
async createUser(email) {
|
|
61
62
|
const id = crypto.randomUUID();
|
|
62
|
-
|
|
63
|
+
const normalized = normalizeEmail(email);
|
|
64
|
+
await db.prepare(`INSERT INTO ${t.users} (id, email) VALUES (?, ?)`).bind(id, normalized).run();
|
|
63
65
|
return {
|
|
64
66
|
id,
|
|
65
|
-
email,
|
|
67
|
+
email: normalized,
|
|
66
68
|
skipPasskeyPrompt: false,
|
|
67
69
|
createdAt: Math.floor(Date.now() / 1000)
|
|
68
70
|
};
|
|
@@ -102,13 +104,13 @@ export function d1Adapter(db, options = {}) {
|
|
|
102
104
|
async storeOTP(email, id, code, expiresAt) {
|
|
103
105
|
await db
|
|
104
106
|
.prepare(`INSERT INTO ${t.otpCodes} (id, email, code, expires_at) VALUES (?, ?, ?, ?)`)
|
|
105
|
-
.bind(id, email, code, expiresAt)
|
|
107
|
+
.bind(id, normalizeEmail(email), code, expiresAt)
|
|
106
108
|
.run();
|
|
107
109
|
},
|
|
108
110
|
async getLatestOTP(email) {
|
|
109
111
|
const row = await db
|
|
110
|
-
.prepare(`SELECT id, email, code, attempts, expires_at FROM ${t.otpCodes} WHERE email = ? ORDER BY created_at DESC LIMIT 1`)
|
|
111
|
-
.bind(email)
|
|
112
|
+
.prepare(`SELECT id, email, code, attempts, expires_at FROM ${t.otpCodes} WHERE email = ? COLLATE NOCASE ORDER BY created_at DESC LIMIT 1`)
|
|
113
|
+
.bind(normalizeEmail(email))
|
|
112
114
|
.first();
|
|
113
115
|
if (!row)
|
|
114
116
|
return null;
|
|
@@ -127,7 +129,7 @@ export function d1Adapter(db, options = {}) {
|
|
|
127
129
|
await db.prepare(`DELETE FROM ${t.otpCodes} WHERE id = ?`).bind(id).run();
|
|
128
130
|
},
|
|
129
131
|
async deleteOTPsForEmail(email) {
|
|
130
|
-
await db.prepare(`DELETE FROM ${t.otpCodes} WHERE email =
|
|
132
|
+
await db.prepare(`DELETE FROM ${t.otpCodes} WHERE email = ? COLLATE NOCASE`).bind(normalizeEmail(email)).run();
|
|
131
133
|
},
|
|
132
134
|
async storeChallenge(challenge, userId, expiresAt) {
|
|
133
135
|
await db.prepare(`DELETE FROM ${t.challenges} WHERE expires_at < ?`).bind(Date.now()).run();
|
package/dist/db/postgres.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { normalizeEmail } from '../email.js';
|
|
2
3
|
export function postgresAdapter(pool, options = {}) {
|
|
3
4
|
const p = options.tablePrefix ?? 'auth_';
|
|
4
5
|
const t = {
|
|
@@ -58,7 +59,7 @@ export function postgresAdapter(pool, options = {}) {
|
|
|
58
59
|
`);
|
|
59
60
|
},
|
|
60
61
|
async getUserByEmail(email) {
|
|
61
|
-
const row = await queryOne(`SELECT id, email, skip_passkey_prompt, created_at FROM ${t.users} WHERE email = $1`, [email]);
|
|
62
|
+
const row = await queryOne(`SELECT id, email, skip_passkey_prompt, created_at FROM ${t.users} WHERE LOWER(email) = $1`, [normalizeEmail(email)]);
|
|
62
63
|
if (!row)
|
|
63
64
|
return null;
|
|
64
65
|
return {
|
|
@@ -70,10 +71,11 @@ export function postgresAdapter(pool, options = {}) {
|
|
|
70
71
|
},
|
|
71
72
|
async createUser(email) {
|
|
72
73
|
const id = randomUUID();
|
|
73
|
-
|
|
74
|
+
const normalized = normalizeEmail(email);
|
|
75
|
+
await pool.query(`INSERT INTO ${t.users} (id, email) VALUES ($1, $2)`, [id, normalized]);
|
|
74
76
|
return {
|
|
75
77
|
id,
|
|
76
|
-
email,
|
|
78
|
+
email: normalized,
|
|
77
79
|
skipPasskeyPrompt: false,
|
|
78
80
|
createdAt: Date.now()
|
|
79
81
|
};
|
|
@@ -108,13 +110,13 @@ export function postgresAdapter(pool, options = {}) {
|
|
|
108
110
|
async storeOTP(email, id, code, expiresAt) {
|
|
109
111
|
await pool.query(`INSERT INTO ${t.otpCodes} (id, email, code, expires_at) VALUES ($1, $2, $3, $4)`, [
|
|
110
112
|
id,
|
|
111
|
-
email,
|
|
113
|
+
normalizeEmail(email),
|
|
112
114
|
code,
|
|
113
115
|
expiresAt
|
|
114
116
|
]);
|
|
115
117
|
},
|
|
116
118
|
async getLatestOTP(email) {
|
|
117
|
-
const row = await queryOne(`SELECT id, email, code, attempts, expires_at FROM ${t.otpCodes} WHERE email = $1 ORDER BY created_at DESC LIMIT 1`, [email]);
|
|
119
|
+
const row = await queryOne(`SELECT id, email, code, attempts, expires_at FROM ${t.otpCodes} WHERE LOWER(email) = $1 ORDER BY created_at DESC LIMIT 1`, [normalizeEmail(email)]);
|
|
118
120
|
if (!row)
|
|
119
121
|
return null;
|
|
120
122
|
return {
|
|
@@ -132,7 +134,7 @@ export function postgresAdapter(pool, options = {}) {
|
|
|
132
134
|
await pool.query(`DELETE FROM ${t.otpCodes} WHERE id = $1`, [id]);
|
|
133
135
|
},
|
|
134
136
|
async deleteOTPsForEmail(email) {
|
|
135
|
-
await pool.query(`DELETE FROM ${t.otpCodes} WHERE email = $1`, [email]);
|
|
137
|
+
await pool.query(`DELETE FROM ${t.otpCodes} WHERE LOWER(email) = $1`, [normalizeEmail(email)]);
|
|
136
138
|
},
|
|
137
139
|
async storeChallenge(challenge, userId, expiresAt) {
|
|
138
140
|
await pool.query(`DELETE FROM ${t.challenges} WHERE expires_at < $1`, [Date.now()]);
|
package/dist/db/sqlite.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { normalizeEmail } from '../email.js';
|
|
2
3
|
export function sqliteAdapter(db, options = {}) {
|
|
3
4
|
const p = options.tablePrefix ?? 'auth_';
|
|
4
5
|
const t = {
|
|
@@ -53,8 +54,8 @@ export function sqliteAdapter(db, options = {}) {
|
|
|
53
54
|
},
|
|
54
55
|
getUserByEmail(email) {
|
|
55
56
|
const row = db
|
|
56
|
-
.prepare(`SELECT id, email, skip_passkey_prompt, created_at FROM ${t.users} WHERE email =
|
|
57
|
-
.get(email);
|
|
57
|
+
.prepare(`SELECT id, email, skip_passkey_prompt, created_at FROM ${t.users} WHERE email = ? COLLATE NOCASE`)
|
|
58
|
+
.get(normalizeEmail(email));
|
|
58
59
|
if (!row)
|
|
59
60
|
return null;
|
|
60
61
|
return {
|
|
@@ -66,10 +67,11 @@ export function sqliteAdapter(db, options = {}) {
|
|
|
66
67
|
},
|
|
67
68
|
createUser(email) {
|
|
68
69
|
const id = randomUUID();
|
|
69
|
-
|
|
70
|
+
const normalized = normalizeEmail(email);
|
|
71
|
+
db.prepare(`INSERT INTO ${t.users} (id, email) VALUES (?, ?)`).run(id, normalized);
|
|
70
72
|
return {
|
|
71
73
|
id,
|
|
72
|
-
email,
|
|
74
|
+
email: normalized,
|
|
73
75
|
skipPasskeyPrompt: false,
|
|
74
76
|
createdAt: Math.floor(Date.now() / 1000)
|
|
75
77
|
};
|
|
@@ -100,12 +102,12 @@ export function sqliteAdapter(db, options = {}) {
|
|
|
100
102
|
db.prepare(`DELETE FROM ${t.sessions} WHERE id = ?`).run(tokenHash);
|
|
101
103
|
},
|
|
102
104
|
storeOTP(email, id, code, expiresAt) {
|
|
103
|
-
db.prepare(`INSERT INTO ${t.otpCodes} (id, email, code, expires_at) VALUES (?, ?, ?, ?)`).run(id, email, code, expiresAt);
|
|
105
|
+
db.prepare(`INSERT INTO ${t.otpCodes} (id, email, code, expires_at) VALUES (?, ?, ?, ?)`).run(id, normalizeEmail(email), code, expiresAt);
|
|
104
106
|
},
|
|
105
107
|
getLatestOTP(email) {
|
|
106
108
|
const row = db
|
|
107
|
-
.prepare(`SELECT id, email, code, attempts, expires_at FROM ${t.otpCodes} WHERE email = ? ORDER BY created_at DESC LIMIT 1`)
|
|
108
|
-
.get(email);
|
|
109
|
+
.prepare(`SELECT id, email, code, attempts, expires_at FROM ${t.otpCodes} WHERE email = ? COLLATE NOCASE ORDER BY created_at DESC LIMIT 1`)
|
|
110
|
+
.get(normalizeEmail(email));
|
|
109
111
|
if (!row)
|
|
110
112
|
return null;
|
|
111
113
|
return {
|
|
@@ -123,7 +125,7 @@ export function sqliteAdapter(db, options = {}) {
|
|
|
123
125
|
db.prepare(`DELETE FROM ${t.otpCodes} WHERE id = ?`).run(id);
|
|
124
126
|
},
|
|
125
127
|
deleteOTPsForEmail(email) {
|
|
126
|
-
db.prepare(`DELETE FROM ${t.otpCodes} WHERE email =
|
|
128
|
+
db.prepare(`DELETE FROM ${t.otpCodes} WHERE email = ? COLLATE NOCASE`).run(normalizeEmail(email));
|
|
127
129
|
},
|
|
128
130
|
storeChallenge(challenge, userId, expiresAt) {
|
|
129
131
|
db.prepare(`DELETE FROM ${t.challenges} WHERE expires_at < ?`).run(Date.now());
|
package/dist/email.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function normalizeEmail(email: string): string;
|
package/dist/email.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Email is the user's identity key. Normalize it to one canonical form so the
|
|
2
|
+
// same address always maps to a single account regardless of how it was typed
|
|
3
|
+
// (mobile keyboards auto-capitalize, users add stray whitespace, etc.).
|
|
4
|
+
export function normalizeEmail(email) {
|
|
5
|
+
return email.trim().toLowerCase();
|
|
6
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrgnw/anahtar",
|
|
3
|
-
"version": "0.0.29
|
|
3
|
+
"version": "0.0.29",
|
|
4
4
|
"description": "Opinionated, reusable auth for SvelteKit. Email+OTP + passkeys.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
+
"@mrgnw/anahtar": "0.0.29-diagnostic.0",
|
|
67
68
|
"@oslojs/crypto": "^1.0.1",
|
|
68
69
|
"@oslojs/encoding": "^1.1.0",
|
|
69
70
|
"@simplewebauthn/server": "^13.2.2"
|