swift-auth 1.6.1 → 1.6.2

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
@@ -37,6 +37,7 @@ Built for **Next.js 15/16** with **Upstash Redis**, optimized for type safety an
37
37
  | 📦 **Minimal Footprint** | You control what's stored in Redis |
38
38
  | 🚀 **Zero Crypto Deps** | No external dependencies for hashing |
39
39
  | ✅ **Production-Ready** | Battle-tested session management |
40
+ | 🔄 **Flexible Hashing** | Use built-in scrypt or bring your own encryption |
40
41
 
41
42
  ---
42
43
 
@@ -79,9 +80,13 @@ REDIS_URL=https://your-instance.upstash.io
79
80
  REDIS_TOKEN=your_auth_token
80
81
  ```
81
82
 
82
- ### Step 3️⃣ Database Setup
83
+ ### Step 3️⃣ Database Setup & Password Hashing
83
84
 
84
- Configure your Prisma schema with the `user` model:
85
+ Choose your password hashing approach:
86
+
87
+ #### Option A: Use Swift Auth's Built-in Hashing (Recommended)
88
+
89
+ Configure your Prisma schema with the `user` model including salt:
85
90
 
86
91
  ```prisma
87
92
  // prisma/schema.prisma
@@ -101,7 +106,7 @@ model user {
101
106
  name String
102
107
  email String @unique
103
108
  password String
104
- salt String // ⚠️ MUST be STRING
109
+ salt String // Required for built-in hashing
105
110
  created_at DateTime @default(now())
106
111
  updated_at DateTime @updatedAt
107
112
  }
@@ -109,6 +114,82 @@ model user {
109
114
 
110
115
  > ⚠️ **Critical**: Salt must be stored as a `String`, not Buffer or Bytes.
111
116
 
117
+ #### Option B: Use Your Own Encryption Method
118
+
119
+ If you prefer your own password hashing logic, omit the `salt` field:
120
+
121
+ ```prisma
122
+ model user {
123
+ id String @id @default(uuid())
124
+ name String
125
+ email String @unique
126
+ password String // Your pre-encrypted password
127
+ created_at DateTime @default(now())
128
+ updated_at DateTime @updatedAt
129
+ }
130
+ ```
131
+
132
+ ---
133
+
134
+ ## 🔒 Password Management
135
+
136
+ ### Option A: Swift Auth Built-in Hashing
137
+
138
+ #### Generate Salt & Hash Password
139
+
140
+ ```typescript
141
+ const salt = auth.generateSalt();
142
+ const hashedPassword = await auth.hashPassword("user-password", salt);
143
+
144
+ // Store both hashedPassword and salt as STRINGS in your database
145
+ await prisma.user.create({
146
+ data: {
147
+ email: "user@example.com",
148
+ password: hashedPassword,
149
+ salt: salt,
150
+ name: "John Doe",
151
+ },
152
+ });
153
+ ```
154
+
155
+ #### Verify Password During Login
156
+
157
+ ```typescript
158
+ const isValid = await auth.comparePassword({
159
+ password: "user-password",
160
+ salt,
161
+ hashedPassword,
162
+ });
163
+
164
+ if (!isValid) {
165
+ return { success: false, message: "Invalid password" };
166
+ }
167
+ ```
168
+
169
+ ### Option B: Custom Encryption
170
+
171
+ Use your own encryption method before storing in the database:
172
+
173
+ ```typescript
174
+ import bcrypt from "bcrypt"; // or any other method
175
+
176
+ // During registration
177
+ const hashedPassword = await bcrypt.hash("user-password", 10);
178
+
179
+ await prisma.user.create({
180
+ data: {
181
+ email: "user@example.com",
182
+ password: hashedPassword,
183
+ name: "John Doe",
184
+ },
185
+ });
186
+
187
+ // During login verification
188
+ const isValid = await bcrypt.compare("user-password", user.password);
189
+ ```
190
+
191
+ ---
192
+
112
193
  ### Step 4️⃣ Create Auth Instance
113
194
 
114
195
  ```typescript
@@ -129,18 +210,18 @@ export const auth = createAuth<User>({
129
210
  token: process.env.REDIS_TOKEN!,
130
211
  },
131
212
  ttl: 60 * 60 * 24 * 7, // 7 days
132
- sessionFields: ["id", "name", "email", "created_at"],
213
+ payload: ["id", "name", "email", "created_at"],
133
214
  });
134
215
  ```
135
216
 
136
217
  **Configuration Options:**
137
218
 
138
- | Option | Type | Description |
139
- | --------------- | ---------- | ------------------------- |
140
- | `redis.url` | `string` | Upstash Redis URL |
141
- | `redis.token` | `string` | Upstash Redis token |
142
- | `ttl` | `number` | Session TTL in seconds |
143
- | `sessionFields` | `string[]` | Fields persisted in Redis |
219
+ | Option | Type | Description |
220
+ | ------------- | ---------- | ------------------------- |
221
+ | `redis.url` | `string` | Upstash Redis URL |
222
+ | `redis.token` | `string` | Upstash Redis token |
223
+ | `ttl` | `number` | Session TTL in seconds |
224
+ | `payload` | `string[]` | Fields persisted in Redis |
144
225
 
145
226
  ---
146
227
 
@@ -206,43 +287,6 @@ export async function signOut() {
206
287
 
207
288
  ---
208
289
 
209
- ## 🔒 Password Security
210
-
211
- Swift Auth provides built-in password hashing and verification with scrypt.
212
-
213
- ### Register User
214
-
215
- ```typescript
216
- const salt = auth.generateSalt();
217
- const hashedPassword = await auth.hashPassword("user-password", salt);
218
-
219
- // Store both hashedPassword and salt as STRINGS in your database
220
- await prisma.user.create({
221
- data: {
222
- email: "user@example.com",
223
- password: hashedPassword,
224
- salt: salt,
225
- name: "John Doe",
226
- },
227
- });
228
- ```
229
-
230
- ### Verify Password During Login
231
-
232
- ```typescript
233
- const isValid = await auth.comparePassword({
234
- password: "user-password",
235
- salt,
236
- hashedPassword,
237
- });
238
-
239
- if (!isValid) {
240
- return { success: false, message: "Invalid password" };
241
- }
242
- ```
243
-
244
- ---
245
-
246
290
  ## 📚 Full Login Example
247
291
 
248
292
  Complete login flow with Prisma + Zod validation:
@@ -297,13 +341,17 @@ export async function signIn(formData: unknown) {
297
341
  return { success: false, message: "Account not found" };
298
342
  }
299
343
 
300
- // 3. Verify password (timing-safe)
344
+ // 3. Verify password (choose your method)
345
+ // Option A: Using Swift Auth's built-in method
301
346
  const isCorrectPassword = await auth.comparePassword({
302
347
  hashedPassword: user.password,
303
348
  password,
304
- salt: user.salt, // must be string
349
+ salt: user.salt, // if using built-in hashing
305
350
  });
306
351
 
352
+ // Option B: Using custom encryption (e.g., bcrypt)
353
+ // const isCorrectPassword = await bcrypt.compare(password, user.password);
354
+
307
355
  if (!isCorrectPassword) {
308
356
  return { success: false, message: "Invalid password" };
309
357
  }
@@ -344,7 +392,7 @@ export async function signIn(formData: unknown) {
344
392
  | `updateUserSession()` | `user`, `cookieStore` | `Promise<void>` | Update existing session |
345
393
  | `removeUserFromSession()` | `cookieStore` | `Promise<void>` | Logout user |
346
394
 
347
- ### Password Management
395
+ ### Password Management (Optional - Built-in only)
348
396
 
349
397
  | Method | Parameters | Returns | Description |
350
398
  | ------------------- | ------------------------------------ | ------------------ | -------------------------------------- |
@@ -358,7 +406,8 @@ export async function signIn(formData: unknown) {
358
406
 
359
407
  ✅ **Do:**
360
408
 
361
- - Store salt as a **STRING** in your database
409
+ - Choose a hashing method before building your database schema
410
+ - Store salt as a **STRING** if using Swift Auth's hashing
362
411
  - Use environment variables for Redis credentials
363
412
  - Call `signOut()` before navigating to login page
364
413
  - Update session after profile changes
@@ -366,9 +415,10 @@ export async function signIn(formData: unknown) {
366
415
 
367
416
  ❌ **Don't:**
368
417
 
369
- - Store salt as Buffer or Bytes
418
+ - Mix hashing methods (pick one and stick with it)
419
+ - Store salt as Buffer or Bytes (if using built-in hashing)
370
420
  - Hardcode Redis credentials
371
- - Compare passwords with `===`
421
+ - Compare passwords manually with `===`
372
422
  - Expose session data to client components
373
423
  - Use TTL shorter than 1 hour for user experience
374
424
 
@@ -395,7 +445,7 @@ echo $REDIS_URL
395
445
  **Solution:**
396
446
 
397
447
  - Check if TTL has expired
398
- - Verify `sessionFields` includes all required user data
448
+ - Verify `payload` includes all required user data
399
449
  - Ensure cookie store is being awaited properly
400
450
 
401
451
  ```typescript
@@ -422,7 +472,7 @@ export type User = {
422
472
  import type { User } from "@/lib/auth";
423
473
  ```
424
474
 
425
- ### Password Comparison Always Fails
475
+ ### Password Comparison Always Fails (Built-in Hashing)
426
476
 
427
477
  **Problem**: `comparePassword()` returns false for valid password
428
478
 
@@ -464,3 +514,4 @@ MIT © Taimoor Safdar
464
514
  [⬆ back to top](#-swift-auth)
465
515
 
466
516
  </div>
517
+
package/dist/index.cjs CHANGED
@@ -37,6 +37,19 @@ module.exports = __toCommonJS(index_exports);
37
37
  // src/create-auth.ts
38
38
  var import_crypto = __toESM(require("crypto"), 1);
39
39
 
40
+ // src/internal/redis-client.ts
41
+ var import_redis = require("@upstash/redis");
42
+ var createRedisClient = ({ url, token }) => {
43
+ if (!url || !token) {
44
+ throw new Error("Both REDIS URL and TOKEN are required to create Redis client");
45
+ }
46
+ const redisClient = new import_redis.Redis({
47
+ url,
48
+ token
49
+ });
50
+ return redisClient;
51
+ };
52
+
40
53
  // src/internal/keys.ts
41
54
  var COOKIE_SESSION_KEY = "session-id";
42
55
 
@@ -113,19 +126,6 @@ async function removeUserFromSession(auth, cookies) {
113
126
  deleteSessionCookie(cookies);
114
127
  }
115
128
 
116
- // src/internal/redis-client.ts
117
- var import_redis = require("@upstash/redis");
118
- var createRedisClient = ({ url, token }) => {
119
- if (!url || !token) {
120
- throw new Error("Both REDIS URL and TOKEN are required to create Redis client");
121
- }
122
- const redisClient = new import_redis.Redis({
123
- url,
124
- token
125
- });
126
- return redisClient;
127
- };
128
-
129
129
  // src/create-auth.ts
130
130
  function createAuth(options) {
131
131
  if (!options.payload.includes("id")) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/create-auth.ts","../src/internal/keys.ts","../src/internal/cookie.ts","../src/internal/utils.ts","../src/internal/session.ts","../src/internal/redis-client.ts"],"sourcesContent":["export { createAuth } from \"./create-auth.js\";\n","import crypto from \"crypto\";\r\nimport {\r\n getUserFromSession,\r\n createUserSession as internalCreateUserSession,\r\n removeUserFromSession as internalRemoveUserFromSession,\r\n updateUserSession as internalUpdateUserSession,\r\n} from \"./internal/session.js\";\r\nimport { AuthInstance, AuthPublic } from \"./internal/types.js\";\r\nimport { createRedisClient } from \"./internal/redis-client.js\";\r\nimport { Cookies } from \"./internal/cookie.js\";\r\n\r\ntype StrictRedisConfig = {\r\n url: string;\r\n token: string;\r\n} & {\r\n [K in Exclude<string, \"url\" | \"token\">]?: never;\r\n};\r\n\r\ntype CreateAuthOptions<UserType extends Record<string, unknown>> = {\r\n redis: StrictRedisConfig;\r\n ttl: number;\r\n payload: (keyof UserType)[];\r\n} & {\r\n [K in Exclude<string, \"redis\" | \"ttl\" | \"payload\">]?: never;\r\n};\r\n\r\n\r\n/**\r\n * Create an authentication instance\r\n */\r\nexport function createAuth<UserType extends Record<string, unknown>>(\r\n options: CreateAuthOptions<UserType>\r\n): AuthPublic<UserType> {\r\n if (!options.payload.includes(\"id\" as keyof UserType)) {\r\n throw new Error(\"payload must include `id`\");\r\n }\r\n\r\n const _redis = createRedisClient(options.redis);\r\n const _payload = options.payload;\r\n const _ttl = options.ttl;\r\n\r\n const auth: AuthInstance<UserType> = {\r\n _redis,\r\n _payload,\r\n _ttl,\r\n // -------------------\r\n // Session operations\r\n // -------------------\r\n getCurrentUser(cookies) {\r\n return getUserFromSession({ _redis, _payload, _ttl: _ttl }, cookies);\r\n },\r\n\r\n createUserSession(user, cookies) {\r\n const sessionData: Partial<UserType> = {} as any;\r\n for (const key of _payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n return internalCreateUserSession({ _redis, _payload, _ttl: _ttl }, sessionData, cookies);\r\n },\r\n\r\n updateUserSession(user, cookies: Pick<Cookies, \"get\" | \"set\">) {\r\n const sessionData: Partial<UserType> = {};\r\n for (const key of _payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n return internalUpdateUserSession({ _redis, _payload, _ttl }, sessionData, cookies);\r\n },\r\n\r\n async removeUserFromSession(cookies) {\r\n await internalRemoveUserFromSession({ _redis, _payload, _ttl: _ttl }, cookies);\r\n },\r\n\r\n // -------------------\r\n // Password helpers\r\n // -------------------\r\n hashPassword(password: string, salt: string): Promise<string> {\r\n return new Promise((resolve, reject) => {\r\n crypto.scrypt(\r\n password.normalize(),\r\n salt,\r\n 64,\r\n (err: Error | null, derivedKey: Buffer) => {\r\n if (err) return reject(err);\r\n resolve(derivedKey.toString(\"hex\").normalize());\r\n }\r\n );\r\n });\r\n },\r\n\r\n generateSalt(): string {\r\n return crypto.randomBytes(16).toString(\"hex\").normalize();\r\n },\r\n\r\n async comparePassword({\r\n password,\r\n salt,\r\n hashedPassword,\r\n }: {\r\n password: string;\r\n salt: string;\r\n hashedPassword: string;\r\n }): Promise<boolean> {\r\n const inputHashed = await auth.hashPassword(password, salt);\r\n return crypto.timingSafeEqual(\r\n Buffer.from(inputHashed, \"hex\"),\r\n Buffer.from(hashedPassword, \"hex\")\r\n );\r\n },\r\n };\r\n\r\n return auth;\r\n}\r\n","export const COOKIE_SESSION_KEY = \"session-id\"","import { COOKIE_SESSION_KEY } from \"./keys.js\";\r\n\r\nexport type Cookies = {\r\n set: (\r\n key: string,\r\n value: string,\r\n options?: {\r\n secure?: boolean;\r\n httpOnly?: boolean;\r\n sameSite?: \"strict\" | \"lax\";\r\n path: string;\r\n maxAge: number\r\n }\r\n ) => void;\r\n get: (key: string) => { name: string; value: string } | undefined;\r\n delete: (key: string) => void;\r\n};\r\n\r\nexport function getSessionId(\r\n cookies: Pick<Cookies, \"get\">\r\n): string | null {\r\n return cookies.get(COOKIE_SESSION_KEY)?.value ?? null;\r\n}\r\n\r\nexport function deleteSessionCookie(\r\n cookies: Pick<Cookies, \"delete\">\r\n) {\r\n cookies.delete(COOKIE_SESSION_KEY);\r\n}\r\n","export function stringifyBigInt<T>(obj: T): string {\r\n return JSON.stringify(obj, (_, value) =>\r\n typeof value === \"bigint\" ? value.toString() : value\r\n );\r\n}\r\n\r\nexport function generateSessionId(): string {\r\n const array = new Uint8Array(512);\r\n crypto.getRandomValues(array);\r\n\r\n return Array.from(array)\r\n .map((b) => b.toString(16).padStart(2, \"0\"))\r\n .join(\"\");\r\n}\r\n","import { Redis } from \"@upstash/redis\";\r\nimport {\r\n Cookies,\r\n deleteSessionCookie,\r\n getSessionId,\r\n} from \"./cookie.js\";\r\nimport { generateSessionId, stringifyBigInt } from \"./utils.js\";\r\nimport { COOKIE_SESSION_KEY } from \"./keys.js\";\r\n\r\nexport async function getUserFromSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n cookies: Pick<Cookies, \"get\">\r\n): Promise<Partial<UserType> | null> {\r\n const sessionId = cookies.get(COOKIE_SESSION_KEY)?.value;\r\n if (!sessionId) return null;\r\n\r\n const data = await auth._redis.get<string | object>(`session:${sessionId}`);\r\n if (!data) return null;\r\n\r\n // Only parse if it's still a string\r\n const sessionData: Partial<UserType> = typeof data === \"string\"\r\n ? JSON.parse(data)\r\n : (data as any);\r\n\r\n // Optional: pick only _payload to ensure type safety\r\n const result: Partial<UserType> = {};\r\n for (const key of auth._payload) {\r\n if (key in sessionData) result[key] = sessionData[key];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nexport async function createUserSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n user: Partial<UserType>,\r\n cookies: Pick<Cookies, \"set\">\r\n) {\r\n const sessionId = generateSessionId();\r\n\r\n // Pick only session fields\r\n const sessionData: Partial<UserType> = {};\r\n for (const key of auth._payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n\r\n await auth._redis.set(\r\n `session:${sessionId}`,\r\n stringifyBigInt(sessionData),\r\n { ex: auth._ttl }\r\n );\r\n\r\n cookies.set(COOKIE_SESSION_KEY, sessionId, {\r\n secure: process.env.NODE_ENV === \"production\",\r\n httpOnly: true,\r\n sameSite: \"lax\",\r\n path: \"/\",\r\n maxAge: auth._ttl,\r\n });\r\n}\r\n\r\n\r\nexport async function updateUserSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n user: Partial<UserType>, // ✅ allow partial here\r\n cookies: Pick<Cookies, \"get\" | \"set\">\r\n): Promise<void> {\r\n const sessionId = getSessionId(cookies);\r\n if (!sessionId) return; // ✅ just return void\r\n\r\n // Pick only session fields\r\n const sessionData: Partial<UserType> = {} as any;\r\n for (const key of auth._payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n\r\n await auth._redis.set(\r\n `session:${sessionId}`,\r\n stringifyBigInt(sessionData),\r\n { ex: auth._ttl }\r\n );\r\n}\r\n\r\nexport async function removeUserFromSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n cookies: Pick<Cookies, \"get\" | \"delete\">\r\n) {\r\n const sessionId = getSessionId(cookies);\r\n if (!sessionId) return null;\r\n\r\n await auth._redis.del(`session:${sessionId}`);\r\n deleteSessionCookie(cookies);\r\n}\r\n","import { Redis } from \"@upstash/redis\";\r\n\r\nexport type CreateRedisClientOptions = {\r\n url: string;\r\n token: string;\r\n};\r\n\r\nexport const createRedisClient = ({ url, token }: CreateRedisClientOptions) => {\r\n if (!url || !token) {\r\n throw new Error(\"Both REDIS URL and TOKEN are required to create Redis client\");\r\n }\r\n\r\n const redisClient = new Redis({\r\n url,\r\n token,\r\n });\r\n\r\n return redisClient;\r\n};\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAAmB;;;ACAZ,IAAM,qBAAqB;;;ACkB3B,SAAS,aACZ,SACa;AACb,SAAO,QAAQ,IAAI,kBAAkB,GAAG,SAAS;AACrD;AAEO,SAAS,oBACZ,SACF;AACE,UAAQ,OAAO,kBAAkB;AACrC;;;AC5BO,SAAS,gBAAmB,KAAgB;AAC/C,SAAO,KAAK;AAAA,IAAU;AAAA,IAAK,CAAC,GAAG,UAC3B,OAAO,UAAU,WAAW,MAAM,SAAS,IAAI;AAAA,EACnD;AACJ;AAEO,SAAS,oBAA4B;AACxC,QAAM,QAAQ,IAAI,WAAW,GAAG;AAChC,SAAO,gBAAgB,KAAK;AAE5B,SAAO,MAAM,KAAK,KAAK,EAClB,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAC1C,KAAK,EAAE;AAChB;;;ACJA,eAAsB,mBAClB,MACA,SACiC;AACjC,QAAM,YAAY,QAAQ,IAAI,kBAAkB,GAAG;AACnD,MAAI,CAAC,UAAW,QAAO;AAEvB,QAAM,OAAO,MAAM,KAAK,OAAO,IAAqB,WAAW,SAAS,EAAE;AAC1E,MAAI,CAAC,KAAM,QAAO;AAGlB,QAAM,cAAiC,OAAO,SAAS,WACjD,KAAK,MAAM,IAAI,IACd;AAGP,QAAM,SAA4B,CAAC;AACnC,aAAW,OAAO,KAAK,UAAU;AAC7B,QAAI,OAAO,YAAa,QAAO,GAAG,IAAI,YAAY,GAAG;AAAA,EACzD;AAEA,SAAO;AACX;AAEA,eAAsB,kBAClB,MACA,MACA,SACF;AACE,QAAM,YAAY,kBAAkB;AAGpC,QAAM,cAAiC,CAAC;AACxC,aAAW,OAAO,KAAK,UAAU;AAC7B,QAAI,OAAO,KAAM,aAAY,GAAG,IAAI,KAAK,GAAG;AAAA,EAChD;AAEA,QAAM,KAAK,OAAO;AAAA,IACd,WAAW,SAAS;AAAA,IACpB,gBAAgB,WAAW;AAAA,IAC3B,EAAE,IAAI,KAAK,KAAK;AAAA,EACpB;AAEA,UAAQ,IAAI,oBAAoB,WAAW;AAAA,IACvC,QAAQ,QAAQ,IAAI,aAAa;AAAA,IACjC,UAAU;AAAA,IACV,UAAU;AAAA,IACV,MAAM;AAAA,IACN,QAAQ,KAAK;AAAA,EACjB,CAAC;AACL;AAGA,eAAsB,kBAClB,MACA,MACA,SACa;AACb,QAAM,YAAY,aAAa,OAAO;AACtC,MAAI,CAAC,UAAW;AAGhB,QAAM,cAAiC,CAAC;AACxC,aAAW,OAAO,KAAK,UAAU;AAC7B,QAAI,OAAO,KAAM,aAAY,GAAG,IAAI,KAAK,GAAG;AAAA,EAChD;AAEA,QAAM,KAAK,OAAO;AAAA,IACd,WAAW,SAAS;AAAA,IACpB,gBAAgB,WAAW;AAAA,IAC3B,EAAE,IAAI,KAAK,KAAK;AAAA,EACpB;AACJ;AAEA,eAAsB,sBAClB,MACA,SACF;AACE,QAAM,YAAY,aAAa,OAAO;AACtC,MAAI,CAAC,UAAW,QAAO;AAEvB,QAAM,KAAK,OAAO,IAAI,WAAW,SAAS,EAAE;AAC5C,sBAAoB,OAAO;AAC/B;;;AC5FA,mBAAsB;AAOf,IAAM,oBAAoB,CAAC,EAAE,KAAK,MAAM,MAAgC;AAC3E,MAAI,CAAC,OAAO,CAAC,OAAO;AAChB,UAAM,IAAI,MAAM,8DAA8D;AAAA,EAClF;AAEA,QAAM,cAAc,IAAI,mBAAM;AAAA,IAC1B;AAAA,IACA;AAAA,EACJ,CAAC;AAED,SAAO;AACX;;;ALYO,SAAS,WACZ,SACoB;AACpB,MAAI,CAAC,QAAQ,QAAQ,SAAS,IAAsB,GAAG;AACnD,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC/C;AAEA,QAAM,SAAS,kBAAkB,QAAQ,KAAK;AAC9C,QAAM,WAAW,QAAQ;AACzB,QAAM,OAAO,QAAQ;AAErB,QAAM,OAA+B;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA,IAIA,eAAe,SAAS;AACpB,aAAO,mBAAmB,EAAE,QAAQ,UAAU,KAAW,GAAG,OAAO;AAAA,IACvE;AAAA,IAEA,kBAAkB,MAAM,SAAS;AAC7B,YAAM,cAAiC,CAAC;AACxC,iBAAW,OAAO,UAAU;AACxB,YAAI,OAAO,KAAM,aAAY,GAAG,IAAI,KAAK,GAAG;AAAA,MAChD;AACA,aAAO,kBAA0B,EAAE,QAAQ,UAAU,KAAW,GAAG,aAAa,OAAO;AAAA,IAC3F;AAAA,IAEA,kBAAkB,MAAM,SAAuC;AAC3D,YAAM,cAAiC,CAAC;AACxC,iBAAW,OAAO,UAAU;AACxB,YAAI,OAAO,KAAM,aAAY,GAAG,IAAI,KAAK,GAAG;AAAA,MAChD;AACA,aAAO,kBAA0B,EAAE,QAAQ,UAAU,KAAK,GAAG,aAAa,OAAO;AAAA,IACrF;AAAA,IAEA,MAAM,sBAAsB,SAAS;AACjC,YAAM,sBAA8B,EAAE,QAAQ,UAAU,KAAW,GAAG,OAAO;AAAA,IACjF;AAAA;AAAA;AAAA;AAAA,IAKA,aAAa,UAAkB,MAA+B;AAC1D,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACpC,sBAAAA,QAAO;AAAA,UACH,SAAS,UAAU;AAAA,UACnB;AAAA,UACA;AAAA,UACA,CAAC,KAAmB,eAAuB;AACvC,gBAAI,IAAK,QAAO,OAAO,GAAG;AAC1B,oBAAQ,WAAW,SAAS,KAAK,EAAE,UAAU,CAAC;AAAA,UAClD;AAAA,QACJ;AAAA,MACJ,CAAC;AAAA,IACL;AAAA,IAEA,eAAuB;AACnB,aAAO,cAAAA,QAAO,YAAY,EAAE,EAAE,SAAS,KAAK,EAAE,UAAU;AAAA,IAC5D;AAAA,IAEA,MAAM,gBAAgB;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,IACJ,GAIqB;AACjB,YAAM,cAAc,MAAM,KAAK,aAAa,UAAU,IAAI;AAC1D,aAAO,cAAAA,QAAO;AAAA,QACV,OAAO,KAAK,aAAa,KAAK;AAAA,QAC9B,OAAO,KAAK,gBAAgB,KAAK;AAAA,MACrC;AAAA,IACJ;AAAA,EACJ;AAEA,SAAO;AACX;","names":["crypto"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/create-auth.ts","../src/internal/redis-client.ts","../src/internal/keys.ts","../src/internal/cookie.ts","../src/internal/utils.ts","../src/internal/session.ts"],"sourcesContent":["export { createAuth } from \"./create-auth.js\";\n","import crypto from \"crypto\";\r\nimport { Cookies } from \"./internal/cookie.js\";\r\nimport { createRedisClient } from \"./internal/redis-client.js\";\r\nimport {\r\n getUserFromSession,\r\n createUserSession as internalCreateUserSession,\r\n removeUserFromSession as internalRemoveUserFromSession,\r\n updateUserSession as internalUpdateUserSession,\r\n} from \"./internal/session.js\";\r\nimport { AuthInstance, AuthPublic } from \"./internal/types.js\";\r\n\r\ntype RedisConfig = {\r\n url: string;\r\n token: string;\r\n}\r\n\r\ntype CreateAuthOptions<UserType extends Record<string, unknown>> = {\r\n redis: RedisConfig;\r\n ttl: number;\r\n payload: (keyof UserType)[];\r\n}\r\n\r\n/**\r\n * Create an authentication instance\r\n */\r\nexport function createAuth<UserType extends Record<string, unknown>>(\r\n options: CreateAuthOptions<UserType>\r\n): AuthPublic<UserType> {\r\n if (!options.payload.includes(\"id\" as keyof UserType)) {\r\n throw new Error(\"payload must include `id`\");\r\n }\r\n\r\n const _redis = createRedisClient(options.redis);\r\n const _payload = options.payload;\r\n const _ttl = options.ttl;\r\n\r\n const auth: AuthInstance<UserType> = {\r\n _redis,\r\n _payload,\r\n _ttl,\r\n // -------------------\r\n // Session operations\r\n // -------------------\r\n getCurrentUser(cookies) {\r\n return getUserFromSession({ _redis, _payload, _ttl: _ttl }, cookies);\r\n },\r\n\r\n createUserSession(user, cookies) {\r\n const sessionData: Partial<UserType> = {} as any;\r\n for (const key of _payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n return internalCreateUserSession({ _redis, _payload, _ttl: _ttl }, sessionData, cookies);\r\n },\r\n\r\n updateUserSession(user, cookies: Pick<Cookies, \"get\" | \"set\">) {\r\n const sessionData: Partial<UserType> = {};\r\n for (const key of _payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n return internalUpdateUserSession({ _redis, _payload, _ttl }, sessionData, cookies);\r\n },\r\n\r\n async removeUserFromSession(cookies) {\r\n await internalRemoveUserFromSession({ _redis, _payload, _ttl: _ttl }, cookies);\r\n },\r\n\r\n // -------------------\r\n // Password helpers\r\n // -------------------\r\n hashPassword(password: string, salt: string): Promise<string> {\r\n return new Promise((resolve, reject) => {\r\n crypto.scrypt(\r\n password.normalize(),\r\n salt,\r\n 64,\r\n (err: Error | null, derivedKey: Buffer) => {\r\n if (err) return reject(err);\r\n resolve(derivedKey.toString(\"hex\").normalize());\r\n }\r\n );\r\n });\r\n },\r\n\r\n generateSalt(): string {\r\n return crypto.randomBytes(16).toString(\"hex\").normalize();\r\n },\r\n\r\n async comparePassword({\r\n password,\r\n salt,\r\n hashedPassword,\r\n }: {\r\n password: string;\r\n salt: string;\r\n hashedPassword: string;\r\n }): Promise<boolean> {\r\n const inputHashed = await auth.hashPassword(password, salt);\r\n return crypto.timingSafeEqual(\r\n Buffer.from(inputHashed, \"hex\"),\r\n Buffer.from(hashedPassword, \"hex\")\r\n );\r\n },\r\n };\r\n\r\n return auth;\r\n}\r\n","import { Redis } from \"@upstash/redis\";\r\n\r\nexport type CreateRedisClientOptions = {\r\n url: string;\r\n token: string;\r\n};\r\n\r\nexport const createRedisClient = ({ url, token }: CreateRedisClientOptions) => {\r\n if (!url || !token) {\r\n throw new Error(\"Both REDIS URL and TOKEN are required to create Redis client\");\r\n }\r\n\r\n const redisClient = new Redis({\r\n url,\r\n token,\r\n });\r\n\r\n return redisClient;\r\n};\r\n","export const COOKIE_SESSION_KEY = \"session-id\"","import { COOKIE_SESSION_KEY } from \"./keys.js\";\r\n\r\nexport type Cookies = {\r\n set: (\r\n key: string,\r\n value: string,\r\n options?: {\r\n secure?: boolean;\r\n httpOnly?: boolean;\r\n sameSite?: \"strict\" | \"lax\";\r\n path: string;\r\n maxAge: number\r\n }\r\n ) => void;\r\n get: (key: string) => { name: string; value: string } | undefined;\r\n delete: (key: string) => void;\r\n};\r\n\r\nexport function getSessionId(\r\n cookies: Pick<Cookies, \"get\">\r\n): string | null {\r\n return cookies.get(COOKIE_SESSION_KEY)?.value ?? null;\r\n}\r\n\r\nexport function deleteSessionCookie(\r\n cookies: Pick<Cookies, \"delete\">\r\n) {\r\n cookies.delete(COOKIE_SESSION_KEY);\r\n}\r\n","export function stringifyBigInt<T>(obj: T): string {\r\n return JSON.stringify(obj, (_, value) =>\r\n typeof value === \"bigint\" ? value.toString() : value\r\n );\r\n}\r\n\r\nexport function generateSessionId(): string {\r\n const array = new Uint8Array(512);\r\n crypto.getRandomValues(array);\r\n\r\n return Array.from(array)\r\n .map((b) => b.toString(16).padStart(2, \"0\"))\r\n .join(\"\");\r\n}\r\n","import { Redis } from \"@upstash/redis\";\r\nimport {\r\n Cookies,\r\n deleteSessionCookie,\r\n getSessionId,\r\n} from \"./cookie.js\";\r\nimport { generateSessionId, stringifyBigInt } from \"./utils.js\";\r\nimport { COOKIE_SESSION_KEY } from \"./keys.js\";\r\n\r\nexport async function getUserFromSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n cookies: Pick<Cookies, \"get\">\r\n): Promise<Partial<UserType> | null> {\r\n const sessionId = cookies.get(COOKIE_SESSION_KEY)?.value;\r\n if (!sessionId) return null;\r\n\r\n const data = await auth._redis.get<string | object>(`session:${sessionId}`);\r\n if (!data) return null;\r\n\r\n // Only parse if it's still a string\r\n const sessionData: Partial<UserType> = typeof data === \"string\"\r\n ? JSON.parse(data)\r\n : (data as any);\r\n\r\n // Optional: pick only _payload to ensure type safety\r\n const result: Partial<UserType> = {};\r\n for (const key of auth._payload) {\r\n if (key in sessionData) result[key] = sessionData[key];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nexport async function createUserSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n user: Partial<UserType>,\r\n cookies: Pick<Cookies, \"set\">\r\n) {\r\n const sessionId = generateSessionId();\r\n\r\n // Pick only session fields\r\n const sessionData: Partial<UserType> = {};\r\n for (const key of auth._payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n\r\n await auth._redis.set(\r\n `session:${sessionId}`,\r\n stringifyBigInt(sessionData),\r\n { ex: auth._ttl }\r\n );\r\n\r\n cookies.set(COOKIE_SESSION_KEY, sessionId, {\r\n secure: process.env.NODE_ENV === \"production\",\r\n httpOnly: true,\r\n sameSite: \"lax\",\r\n path: \"/\",\r\n maxAge: auth._ttl,\r\n });\r\n}\r\n\r\n\r\nexport async function updateUserSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n user: Partial<UserType>, // ✅ allow partial here\r\n cookies: Pick<Cookies, \"get\" | \"set\">\r\n): Promise<void> {\r\n const sessionId = getSessionId(cookies);\r\n if (!sessionId) return; // ✅ just return void\r\n\r\n // Pick only session fields\r\n const sessionData: Partial<UserType> = {} as any;\r\n for (const key of auth._payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n\r\n await auth._redis.set(\r\n `session:${sessionId}`,\r\n stringifyBigInt(sessionData),\r\n { ex: auth._ttl }\r\n );\r\n}\r\n\r\nexport async function removeUserFromSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n cookies: Pick<Cookies, \"get\" | \"delete\">\r\n) {\r\n const sessionId = getSessionId(cookies);\r\n if (!sessionId) return null;\r\n\r\n await auth._redis.del(`session:${sessionId}`);\r\n deleteSessionCookie(cookies);\r\n}\r\n"],"mappings":"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","names":["crypto"]}
package/dist/index.d.cts CHANGED
@@ -27,18 +27,14 @@ type AuthPublic<UserType extends Record<string, unknown>> = {
27
27
  }): Promise<boolean>;
28
28
  };
29
29
 
30
- type StrictRedisConfig = {
30
+ type RedisConfig = {
31
31
  url: string;
32
32
  token: string;
33
- } & {
34
- [K in Exclude<string, "url" | "token">]?: never;
35
33
  };
36
34
  type CreateAuthOptions<UserType extends Record<string, unknown>> = {
37
- redis: StrictRedisConfig;
35
+ redis: RedisConfig;
38
36
  ttl: number;
39
37
  payload: (keyof UserType)[];
40
- } & {
41
- [K in Exclude<string, "redis" | "ttl" | "payload">]?: never;
42
38
  };
43
39
  /**
44
40
  * Create an authentication instance
package/dist/index.d.ts CHANGED
@@ -27,18 +27,14 @@ type AuthPublic<UserType extends Record<string, unknown>> = {
27
27
  }): Promise<boolean>;
28
28
  };
29
29
 
30
- type StrictRedisConfig = {
30
+ type RedisConfig = {
31
31
  url: string;
32
32
  token: string;
33
- } & {
34
- [K in Exclude<string, "url" | "token">]?: never;
35
33
  };
36
34
  type CreateAuthOptions<UserType extends Record<string, unknown>> = {
37
- redis: StrictRedisConfig;
35
+ redis: RedisConfig;
38
36
  ttl: number;
39
37
  payload: (keyof UserType)[];
40
- } & {
41
- [K in Exclude<string, "redis" | "ttl" | "payload">]?: never;
42
38
  };
43
39
  /**
44
40
  * Create an authentication instance
package/dist/index.js CHANGED
@@ -1,6 +1,19 @@
1
1
  // src/create-auth.ts
2
2
  import crypto2 from "crypto";
3
3
 
4
+ // src/internal/redis-client.ts
5
+ import { Redis } from "@upstash/redis";
6
+ var createRedisClient = ({ url, token }) => {
7
+ if (!url || !token) {
8
+ throw new Error("Both REDIS URL and TOKEN are required to create Redis client");
9
+ }
10
+ const redisClient = new Redis({
11
+ url,
12
+ token
13
+ });
14
+ return redisClient;
15
+ };
16
+
4
17
  // src/internal/keys.ts
5
18
  var COOKIE_SESSION_KEY = "session-id";
6
19
 
@@ -77,19 +90,6 @@ async function removeUserFromSession(auth, cookies) {
77
90
  deleteSessionCookie(cookies);
78
91
  }
79
92
 
80
- // src/internal/redis-client.ts
81
- import { Redis } from "@upstash/redis";
82
- var createRedisClient = ({ url, token }) => {
83
- if (!url || !token) {
84
- throw new Error("Both REDIS URL and TOKEN are required to create Redis client");
85
- }
86
- const redisClient = new Redis({
87
- url,
88
- token
89
- });
90
- return redisClient;
91
- };
92
-
93
93
  // src/create-auth.ts
94
94
  function createAuth(options) {
95
95
  if (!options.payload.includes("id")) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/create-auth.ts","../src/internal/keys.ts","../src/internal/cookie.ts","../src/internal/utils.ts","../src/internal/session.ts","../src/internal/redis-client.ts"],"sourcesContent":["import crypto from \"crypto\";\r\nimport {\r\n getUserFromSession,\r\n createUserSession as internalCreateUserSession,\r\n removeUserFromSession as internalRemoveUserFromSession,\r\n updateUserSession as internalUpdateUserSession,\r\n} from \"./internal/session.js\";\r\nimport { AuthInstance, AuthPublic } from \"./internal/types.js\";\r\nimport { createRedisClient } from \"./internal/redis-client.js\";\r\nimport { Cookies } from \"./internal/cookie.js\";\r\n\r\ntype StrictRedisConfig = {\r\n url: string;\r\n token: string;\r\n} & {\r\n [K in Exclude<string, \"url\" | \"token\">]?: never;\r\n};\r\n\r\ntype CreateAuthOptions<UserType extends Record<string, unknown>> = {\r\n redis: StrictRedisConfig;\r\n ttl: number;\r\n payload: (keyof UserType)[];\r\n} & {\r\n [K in Exclude<string, \"redis\" | \"ttl\" | \"payload\">]?: never;\r\n};\r\n\r\n\r\n/**\r\n * Create an authentication instance\r\n */\r\nexport function createAuth<UserType extends Record<string, unknown>>(\r\n options: CreateAuthOptions<UserType>\r\n): AuthPublic<UserType> {\r\n if (!options.payload.includes(\"id\" as keyof UserType)) {\r\n throw new Error(\"payload must include `id`\");\r\n }\r\n\r\n const _redis = createRedisClient(options.redis);\r\n const _payload = options.payload;\r\n const _ttl = options.ttl;\r\n\r\n const auth: AuthInstance<UserType> = {\r\n _redis,\r\n _payload,\r\n _ttl,\r\n // -------------------\r\n // Session operations\r\n // -------------------\r\n getCurrentUser(cookies) {\r\n return getUserFromSession({ _redis, _payload, _ttl: _ttl }, cookies);\r\n },\r\n\r\n createUserSession(user, cookies) {\r\n const sessionData: Partial<UserType> = {} as any;\r\n for (const key of _payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n return internalCreateUserSession({ _redis, _payload, _ttl: _ttl }, sessionData, cookies);\r\n },\r\n\r\n updateUserSession(user, cookies: Pick<Cookies, \"get\" | \"set\">) {\r\n const sessionData: Partial<UserType> = {};\r\n for (const key of _payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n return internalUpdateUserSession({ _redis, _payload, _ttl }, sessionData, cookies);\r\n },\r\n\r\n async removeUserFromSession(cookies) {\r\n await internalRemoveUserFromSession({ _redis, _payload, _ttl: _ttl }, cookies);\r\n },\r\n\r\n // -------------------\r\n // Password helpers\r\n // -------------------\r\n hashPassword(password: string, salt: string): Promise<string> {\r\n return new Promise((resolve, reject) => {\r\n crypto.scrypt(\r\n password.normalize(),\r\n salt,\r\n 64,\r\n (err: Error | null, derivedKey: Buffer) => {\r\n if (err) return reject(err);\r\n resolve(derivedKey.toString(\"hex\").normalize());\r\n }\r\n );\r\n });\r\n },\r\n\r\n generateSalt(): string {\r\n return crypto.randomBytes(16).toString(\"hex\").normalize();\r\n },\r\n\r\n async comparePassword({\r\n password,\r\n salt,\r\n hashedPassword,\r\n }: {\r\n password: string;\r\n salt: string;\r\n hashedPassword: string;\r\n }): Promise<boolean> {\r\n const inputHashed = await auth.hashPassword(password, salt);\r\n return crypto.timingSafeEqual(\r\n Buffer.from(inputHashed, \"hex\"),\r\n Buffer.from(hashedPassword, \"hex\")\r\n );\r\n },\r\n };\r\n\r\n return auth;\r\n}\r\n","export const COOKIE_SESSION_KEY = \"session-id\"","import { COOKIE_SESSION_KEY } from \"./keys.js\";\r\n\r\nexport type Cookies = {\r\n set: (\r\n key: string,\r\n value: string,\r\n options?: {\r\n secure?: boolean;\r\n httpOnly?: boolean;\r\n sameSite?: \"strict\" | \"lax\";\r\n path: string;\r\n maxAge: number\r\n }\r\n ) => void;\r\n get: (key: string) => { name: string; value: string } | undefined;\r\n delete: (key: string) => void;\r\n};\r\n\r\nexport function getSessionId(\r\n cookies: Pick<Cookies, \"get\">\r\n): string | null {\r\n return cookies.get(COOKIE_SESSION_KEY)?.value ?? null;\r\n}\r\n\r\nexport function deleteSessionCookie(\r\n cookies: Pick<Cookies, \"delete\">\r\n) {\r\n cookies.delete(COOKIE_SESSION_KEY);\r\n}\r\n","export function stringifyBigInt<T>(obj: T): string {\r\n return JSON.stringify(obj, (_, value) =>\r\n typeof value === \"bigint\" ? value.toString() : value\r\n );\r\n}\r\n\r\nexport function generateSessionId(): string {\r\n const array = new Uint8Array(512);\r\n crypto.getRandomValues(array);\r\n\r\n return Array.from(array)\r\n .map((b) => b.toString(16).padStart(2, \"0\"))\r\n .join(\"\");\r\n}\r\n","import { Redis } from \"@upstash/redis\";\r\nimport {\r\n Cookies,\r\n deleteSessionCookie,\r\n getSessionId,\r\n} from \"./cookie.js\";\r\nimport { generateSessionId, stringifyBigInt } from \"./utils.js\";\r\nimport { COOKIE_SESSION_KEY } from \"./keys.js\";\r\n\r\nexport async function getUserFromSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n cookies: Pick<Cookies, \"get\">\r\n): Promise<Partial<UserType> | null> {\r\n const sessionId = cookies.get(COOKIE_SESSION_KEY)?.value;\r\n if (!sessionId) return null;\r\n\r\n const data = await auth._redis.get<string | object>(`session:${sessionId}`);\r\n if (!data) return null;\r\n\r\n // Only parse if it's still a string\r\n const sessionData: Partial<UserType> = typeof data === \"string\"\r\n ? JSON.parse(data)\r\n : (data as any);\r\n\r\n // Optional: pick only _payload to ensure type safety\r\n const result: Partial<UserType> = {};\r\n for (const key of auth._payload) {\r\n if (key in sessionData) result[key] = sessionData[key];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nexport async function createUserSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n user: Partial<UserType>,\r\n cookies: Pick<Cookies, \"set\">\r\n) {\r\n const sessionId = generateSessionId();\r\n\r\n // Pick only session fields\r\n const sessionData: Partial<UserType> = {};\r\n for (const key of auth._payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n\r\n await auth._redis.set(\r\n `session:${sessionId}`,\r\n stringifyBigInt(sessionData),\r\n { ex: auth._ttl }\r\n );\r\n\r\n cookies.set(COOKIE_SESSION_KEY, sessionId, {\r\n secure: process.env.NODE_ENV === \"production\",\r\n httpOnly: true,\r\n sameSite: \"lax\",\r\n path: \"/\",\r\n maxAge: auth._ttl,\r\n });\r\n}\r\n\r\n\r\nexport async function updateUserSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n user: Partial<UserType>, // ✅ allow partial here\r\n cookies: Pick<Cookies, \"get\" | \"set\">\r\n): Promise<void> {\r\n const sessionId = getSessionId(cookies);\r\n if (!sessionId) return; // ✅ just return void\r\n\r\n // Pick only session fields\r\n const sessionData: Partial<UserType> = {} as any;\r\n for (const key of auth._payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n\r\n await auth._redis.set(\r\n `session:${sessionId}`,\r\n stringifyBigInt(sessionData),\r\n { ex: auth._ttl }\r\n );\r\n}\r\n\r\nexport async function removeUserFromSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n cookies: Pick<Cookies, \"get\" | \"delete\">\r\n) {\r\n const sessionId = getSessionId(cookies);\r\n if (!sessionId) return null;\r\n\r\n await auth._redis.del(`session:${sessionId}`);\r\n deleteSessionCookie(cookies);\r\n}\r\n","import { Redis } from \"@upstash/redis\";\r\n\r\nexport type CreateRedisClientOptions = {\r\n url: string;\r\n token: string;\r\n};\r\n\r\nexport const createRedisClient = ({ url, token }: CreateRedisClientOptions) => {\r\n if (!url || !token) {\r\n throw new Error(\"Both REDIS URL and TOKEN are required to create Redis client\");\r\n }\r\n\r\n const redisClient = new Redis({\r\n url,\r\n token,\r\n });\r\n\r\n return redisClient;\r\n};\r\n"],"mappings":"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","names":["crypto","crypto"]}
1
+ {"version":3,"sources":["../src/create-auth.ts","../src/internal/redis-client.ts","../src/internal/keys.ts","../src/internal/cookie.ts","../src/internal/utils.ts","../src/internal/session.ts"],"sourcesContent":["import crypto from \"crypto\";\r\nimport { Cookies } from \"./internal/cookie.js\";\r\nimport { createRedisClient } from \"./internal/redis-client.js\";\r\nimport {\r\n getUserFromSession,\r\n createUserSession as internalCreateUserSession,\r\n removeUserFromSession as internalRemoveUserFromSession,\r\n updateUserSession as internalUpdateUserSession,\r\n} from \"./internal/session.js\";\r\nimport { AuthInstance, AuthPublic } from \"./internal/types.js\";\r\n\r\ntype RedisConfig = {\r\n url: string;\r\n token: string;\r\n}\r\n\r\ntype CreateAuthOptions<UserType extends Record<string, unknown>> = {\r\n redis: RedisConfig;\r\n ttl: number;\r\n payload: (keyof UserType)[];\r\n}\r\n\r\n/**\r\n * Create an authentication instance\r\n */\r\nexport function createAuth<UserType extends Record<string, unknown>>(\r\n options: CreateAuthOptions<UserType>\r\n): AuthPublic<UserType> {\r\n if (!options.payload.includes(\"id\" as keyof UserType)) {\r\n throw new Error(\"payload must include `id`\");\r\n }\r\n\r\n const _redis = createRedisClient(options.redis);\r\n const _payload = options.payload;\r\n const _ttl = options.ttl;\r\n\r\n const auth: AuthInstance<UserType> = {\r\n _redis,\r\n _payload,\r\n _ttl,\r\n // -------------------\r\n // Session operations\r\n // -------------------\r\n getCurrentUser(cookies) {\r\n return getUserFromSession({ _redis, _payload, _ttl: _ttl }, cookies);\r\n },\r\n\r\n createUserSession(user, cookies) {\r\n const sessionData: Partial<UserType> = {} as any;\r\n for (const key of _payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n return internalCreateUserSession({ _redis, _payload, _ttl: _ttl }, sessionData, cookies);\r\n },\r\n\r\n updateUserSession(user, cookies: Pick<Cookies, \"get\" | \"set\">) {\r\n const sessionData: Partial<UserType> = {};\r\n for (const key of _payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n return internalUpdateUserSession({ _redis, _payload, _ttl }, sessionData, cookies);\r\n },\r\n\r\n async removeUserFromSession(cookies) {\r\n await internalRemoveUserFromSession({ _redis, _payload, _ttl: _ttl }, cookies);\r\n },\r\n\r\n // -------------------\r\n // Password helpers\r\n // -------------------\r\n hashPassword(password: string, salt: string): Promise<string> {\r\n return new Promise((resolve, reject) => {\r\n crypto.scrypt(\r\n password.normalize(),\r\n salt,\r\n 64,\r\n (err: Error | null, derivedKey: Buffer) => {\r\n if (err) return reject(err);\r\n resolve(derivedKey.toString(\"hex\").normalize());\r\n }\r\n );\r\n });\r\n },\r\n\r\n generateSalt(): string {\r\n return crypto.randomBytes(16).toString(\"hex\").normalize();\r\n },\r\n\r\n async comparePassword({\r\n password,\r\n salt,\r\n hashedPassword,\r\n }: {\r\n password: string;\r\n salt: string;\r\n hashedPassword: string;\r\n }): Promise<boolean> {\r\n const inputHashed = await auth.hashPassword(password, salt);\r\n return crypto.timingSafeEqual(\r\n Buffer.from(inputHashed, \"hex\"),\r\n Buffer.from(hashedPassword, \"hex\")\r\n );\r\n },\r\n };\r\n\r\n return auth;\r\n}\r\n","import { Redis } from \"@upstash/redis\";\r\n\r\nexport type CreateRedisClientOptions = {\r\n url: string;\r\n token: string;\r\n};\r\n\r\nexport const createRedisClient = ({ url, token }: CreateRedisClientOptions) => {\r\n if (!url || !token) {\r\n throw new Error(\"Both REDIS URL and TOKEN are required to create Redis client\");\r\n }\r\n\r\n const redisClient = new Redis({\r\n url,\r\n token,\r\n });\r\n\r\n return redisClient;\r\n};\r\n","export const COOKIE_SESSION_KEY = \"session-id\"","import { COOKIE_SESSION_KEY } from \"./keys.js\";\r\n\r\nexport type Cookies = {\r\n set: (\r\n key: string,\r\n value: string,\r\n options?: {\r\n secure?: boolean;\r\n httpOnly?: boolean;\r\n sameSite?: \"strict\" | \"lax\";\r\n path: string;\r\n maxAge: number\r\n }\r\n ) => void;\r\n get: (key: string) => { name: string; value: string } | undefined;\r\n delete: (key: string) => void;\r\n};\r\n\r\nexport function getSessionId(\r\n cookies: Pick<Cookies, \"get\">\r\n): string | null {\r\n return cookies.get(COOKIE_SESSION_KEY)?.value ?? null;\r\n}\r\n\r\nexport function deleteSessionCookie(\r\n cookies: Pick<Cookies, \"delete\">\r\n) {\r\n cookies.delete(COOKIE_SESSION_KEY);\r\n}\r\n","export function stringifyBigInt<T>(obj: T): string {\r\n return JSON.stringify(obj, (_, value) =>\r\n typeof value === \"bigint\" ? value.toString() : value\r\n );\r\n}\r\n\r\nexport function generateSessionId(): string {\r\n const array = new Uint8Array(512);\r\n crypto.getRandomValues(array);\r\n\r\n return Array.from(array)\r\n .map((b) => b.toString(16).padStart(2, \"0\"))\r\n .join(\"\");\r\n}\r\n","import { Redis } from \"@upstash/redis\";\r\nimport {\r\n Cookies,\r\n deleteSessionCookie,\r\n getSessionId,\r\n} from \"./cookie.js\";\r\nimport { generateSessionId, stringifyBigInt } from \"./utils.js\";\r\nimport { COOKIE_SESSION_KEY } from \"./keys.js\";\r\n\r\nexport async function getUserFromSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n cookies: Pick<Cookies, \"get\">\r\n): Promise<Partial<UserType> | null> {\r\n const sessionId = cookies.get(COOKIE_SESSION_KEY)?.value;\r\n if (!sessionId) return null;\r\n\r\n const data = await auth._redis.get<string | object>(`session:${sessionId}`);\r\n if (!data) return null;\r\n\r\n // Only parse if it's still a string\r\n const sessionData: Partial<UserType> = typeof data === \"string\"\r\n ? JSON.parse(data)\r\n : (data as any);\r\n\r\n // Optional: pick only _payload to ensure type safety\r\n const result: Partial<UserType> = {};\r\n for (const key of auth._payload) {\r\n if (key in sessionData) result[key] = sessionData[key];\r\n }\r\n\r\n return result;\r\n}\r\n\r\nexport async function createUserSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n user: Partial<UserType>,\r\n cookies: Pick<Cookies, \"set\">\r\n) {\r\n const sessionId = generateSessionId();\r\n\r\n // Pick only session fields\r\n const sessionData: Partial<UserType> = {};\r\n for (const key of auth._payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n\r\n await auth._redis.set(\r\n `session:${sessionId}`,\r\n stringifyBigInt(sessionData),\r\n { ex: auth._ttl }\r\n );\r\n\r\n cookies.set(COOKIE_SESSION_KEY, sessionId, {\r\n secure: process.env.NODE_ENV === \"production\",\r\n httpOnly: true,\r\n sameSite: \"lax\",\r\n path: \"/\",\r\n maxAge: auth._ttl,\r\n });\r\n}\r\n\r\n\r\nexport async function updateUserSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n user: Partial<UserType>, // ✅ allow partial here\r\n cookies: Pick<Cookies, \"get\" | \"set\">\r\n): Promise<void> {\r\n const sessionId = getSessionId(cookies);\r\n if (!sessionId) return; // ✅ just return void\r\n\r\n // Pick only session fields\r\n const sessionData: Partial<UserType> = {} as any;\r\n for (const key of auth._payload) {\r\n if (key in user) sessionData[key] = user[key];\r\n }\r\n\r\n await auth._redis.set(\r\n `session:${sessionId}`,\r\n stringifyBigInt(sessionData),\r\n { ex: auth._ttl }\r\n );\r\n}\r\n\r\nexport async function removeUserFromSession<UserType extends Record<string, unknown>>(\r\n auth: { _redis: Redis; _payload: (keyof UserType)[]; _ttl: number },\r\n cookies: Pick<Cookies, \"get\" | \"delete\">\r\n) {\r\n const sessionId = getSessionId(cookies);\r\n if (!sessionId) return null;\r\n\r\n await auth._redis.del(`session:${sessionId}`);\r\n deleteSessionCookie(cookies);\r\n}\r\n"],"mappings":"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","names":["crypto","crypto"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swift-auth",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "type": "module",
5
5
  "description": "Custom authentication system for apps",
6
6
  "main": "dist/index.cjs.js",