swift-auth 1.1.0 β†’ 1.3.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.
Files changed (2) hide show
  1. package/README.md +335 -130
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,108 +1,119 @@
1
- # Swift Auth πŸš€
1
+ <div align="center">
2
2
 
3
- A lightweight, type-safe authentication library for Next.js 15/16 and Upstash Redis. Swift Auth handles the heavy lifting of session management, password security, and cookie handling so you can focus on building your features.
3
+ # ⚑ Swift Auth
4
4
 
5
- ![npm](https://img.shields.io/npm/v/swift-auth?style=flat-square)
6
- ![license](https://img.shields.io/npm/l/swift-auth?style=flat-square)
7
- ![typescript](https://img.shields.io/badge/typescript-blue?style=flat-square&logo=typescript)
5
+ **Type-safe, zero-hassle authentication for Next.js**
8
6
 
9
- ## Table of Contents
7
+ [![npm version](https://img.shields.io/npm/v/swift-auth?style=flat-square&color=0ea5e9)](https://www.npmjs.com/package/swift-auth)
8
+ [![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE)
9
+ [![Node](https://img.shields.io/badge/node-%3E%3D18-brightgreen?style=flat-square)](https://nodejs.org)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0%2B-blue?style=flat-square)](https://www.typescriptlang.org)
11
+ [![Next.js](https://img.shields.io/badge/Next.js-15%2B-black?style=flat-square)](https://nextjs.org)
10
12
 
11
- - [Features](#features)
12
- - [Installation](#installation)
13
- - [Prerequisites](#prerequisites)
14
- - [Quick Start](#quick-start)
15
- - [Configuration](#configuration)
16
- - [Core Usage](#core-usage)
17
- - [Password Security](#password-security)
18
- - [API Reference](#api-reference)
19
- - [Why Swift Auth?](#why-swift-auth)
20
- - [Troubleshooting](#troubleshooting)
21
- - [License](#license)
13
+ _Lightweight β€’ Secure β€’ Type-Safe β€’ Production-Ready_
22
14
 
23
- ## Features
15
+ [Quick Start](#-quick-start) β€’ [API Reference](#-api-reference) β€’ [Examples](#-full-login-example) β€’ [Troubleshooting](#-troubleshooting)
24
16
 
25
- ✨ **Type-Safe**: Full TypeScript support with generic user types
26
- πŸ” **Secure**: Built-in password hashing with scrypt and timing-safe comparison
27
- πŸͺ **Cookie Handling**: Automatic HTTP-only cookie management
28
- ⚑ **Next.js 15+**: Fully compatible with the asynchronous `cookies()` API
29
- πŸ“¦ **Minimal**: Only includes what you needβ€”control exactly what's stored in Redis
30
- 🌍 **Production Ready**: Automatic secure flag handling for dev/production environments
17
+ </div>
31
18
 
32
- ## Installation
19
+ ---
33
20
 
34
- ```bash
35
- npm install swift-auth
36
- ```
21
+ ## 🌟 Why Swift Auth?
22
+
23
+ Stop wrestling with authentication boilerplate. Swift Auth handles **session management**, **password security**, and **cookie handling**β€”so you can focus on building amazing features.
24
+
25
+ Built for **Next.js 15/16** with **Upstash Redis**, optimized for type safety and security.
26
+
27
+ ---
28
+
29
+ ## ✨ Key Features
30
+
31
+ | Feature | Description |
32
+ | ------------------------ | ---------------------------------------------------- |
33
+ | πŸ” **Type-Safe** | Full TypeScript support with generic user types |
34
+ | πŸ›‘οΈ **Secure by Default** | Password hashing via scrypt + timing-safe comparison |
35
+ | πŸͺ **Smart Cookies** | Automatic HTTP-only cookies (dev & production) |
36
+ | ⚑ **Next.js 15+** | Works seamlessly with async `cookies()` API |
37
+ | πŸ“¦ **Minimal Footprint** | You control what's stored in Redis |
38
+ | πŸš€ **Zero Crypto Deps** | No external dependencies for hashing |
39
+ | βœ… **Production-Ready** | Battle-tested session management |
37
40
 
38
- Or with yarn:
41
+ ---
42
+
43
+ ## πŸ“¦ Installation
44
+
45
+ Install Swift Auth using your favorite package manager:
39
46
 
40
47
  ```bash
41
- yarn add swift-auth
48
+ npm install swift-auth
42
49
  ```
43
50
 
44
- Or with pnpm:
51
+ **Or with yarn/pnpm:**
45
52
 
46
53
  ```bash
54
+ yarn add swift-auth
55
+ # or
47
56
  pnpm add swift-auth
48
57
  ```
49
58
 
50
- ## Prerequisites
51
-
52
- - Node.js 18+
53
- - Next.js 15 or 16
54
- - [Upstash Redis](https://upstash.com) account with connection credentials
59
+ ---
55
60
 
56
- ## Quick Start
61
+ ## 🎯 Quick Start (5 minutes)
57
62
 
58
- 1. **Set up environment variables**:
63
+ ### Step 1️⃣ Prerequisites
59
64
 
60
- ```bash
61
- REDIS_URL=https://<your-redis-url>
62
- REDIS_TOKEN=<your-redis-token>
63
- ```
65
+ Make sure you have:
64
66
 
65
- 2. **Initialize auth in a shared file** (`lib/auth.ts`):
67
+ - **Node.js** 18+
68
+ - **Next.js** 15 or 16
69
+ - **PostgreSQL** (via Prisma)
70
+ - **Upstash Redis** account
66
71
 
67
- ```typescript
68
- import { createAuth } from "swift-auth";
72
+ ### Step 2️⃣ Environment Variables
69
73
 
70
- export type User = {
71
- id: string;
72
- name: string;
73
- email: string;
74
- created_at: Date;
75
- };
74
+ Create a `.env.local` file:
76
75
 
77
- export const auth = createAuth<User>({
78
- redis: {
79
- url: process.env.REDIS_URL!,
80
- token: process.env.REDIS_TOKEN!,
81
- },
82
- ttl: 60 * 60 * 24 * 7, // 7 Days
83
- sessionFields: ["id", "name", "email", "created_at"],
84
- });
76
+ ```env
77
+ DATABASE_URL=postgresql://user:password@localhost:5432/mydb
78
+ REDIS_URL=https://your-instance.upstash.io
79
+ REDIS_TOKEN=your_auth_token
85
80
  ```
86
81
 
87
- 3. **Use in your Server Action** (login example):
82
+ ### Step 3️⃣ Database Setup
88
83
 
89
- ```typescript
90
- "use server";
84
+ Configure your Prisma schema with the `user` model:
91
85
 
92
- import { auth } from "@/lib/auth";
93
- import { cookies } from "next/headers";
86
+ ```prisma
87
+ // prisma/schema.prisma
94
88
 
95
- export async function signIn(user: User) {
96
- const cookieStore = await cookies();
97
- await auth.createUserSession(user, cookieStore);
89
+ generator client {
90
+ provider = "prisma-client"
91
+ output = "../lib/generated/prisma"
92
+ }
93
+
94
+ datasource db {
95
+ provider = "postgresql"
96
+ url = env("DATABASE_URL")
97
+ }
98
+
99
+ model user {
100
+ id String @id @default(uuid())
101
+ name String
102
+ email String @unique
103
+ password String
104
+ salt String // ⚠️ MUST be STRING
105
+ created_at DateTime @default(now())
106
+ updated_at DateTime @updatedAt
98
107
  }
99
108
  ```
100
109
 
101
- ## Configuration
110
+ > ⚠️ **Critical**: Salt must be stored as a `String`, not Buffer or Bytes.
102
111
 
103
- Initialize your auth instance in a shared file (e.g., `lib/auth.ts`). This ensures your Redis client and session settings are consistent across your app.
112
+ ### Step 4️⃣ Create Auth Instance
104
113
 
105
114
  ```typescript
115
+ // lib/auth.ts
116
+
106
117
  import { createAuth } from "swift-auth";
107
118
 
108
119
  export type User = {
@@ -110,7 +121,6 @@ export type User = {
110
121
  name: string;
111
122
  email: string;
112
123
  created_at: Date;
113
- // ... any other fields
114
124
  };
115
125
 
116
126
  export const auth = createAuth<User>({
@@ -118,25 +128,28 @@ export const auth = createAuth<User>({
118
128
  url: process.env.REDIS_URL!,
119
129
  token: process.env.REDIS_TOKEN!,
120
130
  },
121
- ttl: 60 * 60 * 24 * 7, // 7 Days in seconds
122
- sessionFields: ["id", "name", "email", "created_at"], // Only these fields are stored in Redis
131
+ ttl: 60 * 60 * 24 * 7, // 7 days
132
+ sessionFields: ["id", "name", "email", "created_at"],
123
133
  });
124
134
  ```
125
135
 
126
136
  **Configuration Options:**
127
137
 
128
- - `redis.url`: Your Upstash Redis connection URL
129
- - `redis.token`: Your Upstash Redis authentication token
130
- - `ttl`: Session time-to-live in seconds (default: 7 days)
131
- - `sessionFields`: Array of user properties to persist in Redis
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 |
132
144
 
133
- ## Core Usage
145
+ ---
134
146
 
135
- ### Create a Session (Login)
147
+ ## πŸ” Core Usage
136
148
 
137
- Use this inside a Next.js Server Action. It automatically generates a secure Session ID, stores the user data in Redis, and sets an HTTP-only cookie.
149
+ ### Create Session (Login)
138
150
 
139
151
  ```typescript
152
+ // app/actions/auth.ts
140
153
  "use server";
141
154
 
142
155
  import { auth } from "@/lib/auth";
@@ -150,9 +163,8 @@ export async function signIn(user: User) {
150
163
 
151
164
  ### Get Current User
152
165
 
153
- Retrieve the session data in any Server Component or Layout.
154
-
155
166
  ```typescript
167
+ // app/dashboard/page.tsx
156
168
  import { auth } from "@/lib/auth";
157
169
  import { cookies } from "next/headers";
158
170
 
@@ -160,29 +172,28 @@ export default async function Dashboard() {
160
172
  const user = await auth.getCurrentUser(await cookies());
161
173
 
162
174
  if (!user) return <div>Please log in</div>;
163
- return <div>Welcome back, {user.name}</div>;
175
+ return <div>Welcome back, {user.name}! πŸ‘‹</div>;
164
176
  }
165
177
  ```
166
178
 
167
- ### Update User Session
168
-
169
- Modify session data without creating a new session ID:
179
+ ### Update Session
170
180
 
171
181
  ```typescript
182
+ // app/actions/auth.ts
172
183
  "use server";
173
184
 
174
185
  import { auth } from "@/lib/auth";
175
186
  import { cookies } from "next/headers";
176
187
 
177
188
  export async function updateProfile(user: User) {
178
- const cookieStore = await cookies();
179
- await auth.updateUserSession(user, cookieStore);
189
+ await auth.updateUserSession(user, await cookies());
180
190
  }
181
191
  ```
182
192
 
183
- ### End Session (Logout)
193
+ ### Logout
184
194
 
185
195
  ```typescript
196
+ // app/actions/auth.ts
186
197
  "use server";
187
198
 
188
199
  import { auth } from "@/lib/auth";
@@ -193,69 +204,263 @@ export async function signOut() {
193
204
  }
194
205
  ```
195
206
 
196
- ## Password Security
207
+ ---
208
+
209
+ ## πŸ”’ Password Security
210
+
211
+ Swift Auth provides built-in password hashing and verification with scrypt.
197
212
 
198
- Swift Auth provides built-in helpers for secure password management using Node's `scrypt` and `timingSafeEqual`.
213
+ ### Register User
199
214
 
200
215
  ```typescript
201
- // 1. Registering a user
202
216
  const salt = auth.generateSalt();
203
- const hashedPassword = await auth.hashPassword("my-password", salt);
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
+ ```
204
229
 
205
- // 2. Verifying a user during login
230
+ ### Verify Password During Login
231
+
232
+ ```typescript
206
233
  const isValid = await auth.comparePassword({
207
- password: "my-password",
208
- salt: salt,
209
- hashedPassword: hashedPassword,
234
+ password: "user-password",
235
+ salt,
236
+ hashedPassword,
210
237
  });
211
238
 
212
- if (isValid) {
213
- // Password is correct
214
- } else {
215
- // Password is incorrect
239
+ if (!isValid) {
240
+ return { success: false, message: "Invalid password" };
216
241
  }
217
242
  ```
218
243
 
219
- ## API Reference
244
+ ---
220
245
 
221
- | Method | Description |
222
- | -------------------------------------- | ------------------------------------------------------- |
223
- | `getCurrentUser(cookieStore)` | Returns the session data from Redis based on the cookie |
224
- | `createUserSession(user, cookieStore)` | Creates a new session and sets the browser cookie |
225
- | `updateUserSession(user, cookieStore)` | Updates the Redis data without changing the Session ID |
226
- | `removeUserFromSession(cookieStore)` | Deletes the Redis key and clears the browser cookie |
227
- | `hashPassword(password, salt)` | Hashes a string using scrypt |
228
- | `comparePassword(options)` | Prevents timing attacks while verifying passwords |
229
- | `generateSalt()` | Generates a cryptographic salt for password hashing |
246
+ ## πŸ“š Full Login Example
230
247
 
231
- ## Why Swift Auth?
248
+ Complete login flow with Prisma + Zod validation:
232
249
 
233
- - **Next.js 15+ Optimized**: Fully compatible with the new asynchronous `cookies()` API
234
- - **BigInt & Date Handling**: Built-in serialization for complex JavaScript objects that standard JSON fails to handle
235
- - **Automatic Secure Cookies**: Intelligently sets `secure: true` in production and `secure: false` for localhost development
236
- - **Minimized Payload**: By defining `sessionFields`, you control exactly what data lives in your Redis RAM, keeping costs low and performance high
237
- - **Type-Safe**: Full TypeScript support with generics for your user type
238
- - **Zero Dependencies**: Uses only Node.js built-ins for cryptography
250
+ ### Validation Schema
239
251
 
240
- ## Troubleshooting
252
+ ```typescript
253
+ // lib/validation.ts
254
+ import { z } from "zod";
241
255
 
242
- ### "Redis connection failed"
256
+ export const signInSchema = z
257
+ .object({
258
+ email: z.string().email("Invalid email"),
259
+ password: z.string().min(8, "Password too short"),
260
+ })
261
+ .strict();
243
262
 
244
- - Verify your `REDIS_URL` and `REDIS_TOKEN` environment variables
245
- - Check that your Upstash Redis instance is active
246
- - Ensure your Node.js version is 18 or higher
263
+ export type SignInInput = z.infer<typeof signInSchema>;
264
+ ```
247
265
 
248
- ### "Session not found"
266
+ ### Server Action
249
267
 
250
- - Confirm the user has a valid cookie set
251
- - Check that the Redis TTL hasn't expired
252
- - Verify that `sessionFields` includes the fields you're trying to access
268
+ ```typescript
269
+ // app/actions/auth.ts
270
+ "use server";
253
271
 
254
- ### Type errors with custom User type
272
+ import { auth } from "@/lib/auth";
273
+ import { prisma } from "@/lib/prisma";
274
+ import { signInSchema } from "@/lib/validation";
275
+ import { cookies } from "next/headers";
276
+
277
+ export async function signIn(formData: unknown) {
278
+ try {
279
+ // 1. Validate input
280
+ const parsed = signInSchema.safeParse(formData);
281
+ if (!parsed.success) {
282
+ return {
283
+ success: false,
284
+ message: "Validation error",
285
+ errors: parsed.error.flatten(),
286
+ };
287
+ }
288
+
289
+ const { email, password } = parsed.data;
290
+
291
+ // 2. Find user in database
292
+ const user = await prisma.user.findUnique({
293
+ where: { email },
294
+ });
295
+
296
+ if (!user) {
297
+ return { success: false, message: "Account not found" };
298
+ }
299
+
300
+ // 3. Verify password (timing-safe)
301
+ const isCorrectPassword = await auth.comparePassword({
302
+ hashedPassword: user.password,
303
+ password,
304
+ salt: user.salt, // must be string
305
+ });
306
+
307
+ if (!isCorrectPassword) {
308
+ return { success: false, message: "Invalid password" };
309
+ }
310
+
311
+ // 4. Create session
312
+ await auth.createUserSession(
313
+ {
314
+ id: user.id,
315
+ name: user.name,
316
+ email: user.email,
317
+ created_at: user.created_at,
318
+ },
319
+ await cookies()
320
+ );
321
+
322
+ return {
323
+ success: true,
324
+ message: "Logged in successfully",
325
+ userId: user.id,
326
+ };
327
+ } catch (error) {
328
+ console.error("Auth Error:", error);
329
+ return { success: false, message: "Internal server error" };
330
+ }
331
+ }
332
+ ```
333
+
334
+ ---
335
+
336
+ ## 🎯 API Reference
337
+
338
+ ### Session Management
339
+
340
+ | Method | Parameters | Returns | Description |
341
+ | ------------------------- | --------------------- | --------------- | ------------------------------------ |
342
+ | `getCurrentUser()` | `cookieStore` | `User \| null` | Get the currently authenticated user |
343
+ | `createUserSession()` | `user`, `cookieStore` | `Promise<void>` | Create a new session |
344
+ | `updateUserSession()` | `user`, `cookieStore` | `Promise<void>` | Update existing session |
345
+ | `removeUserFromSession()` | `cookieStore` | `Promise<void>` | Logout user |
346
+
347
+ ### Password Management
348
+
349
+ | Method | Parameters | Returns | Description |
350
+ | ------------------- | ------------------------------------ | ------------------ | -------------------------------------- |
351
+ | `generateSalt()` | - | `string` | Generate cryptographically secure salt |
352
+ | `hashPassword()` | `password`, `salt` | `Promise<string>` | Hash password with scrypt |
353
+ | `comparePassword()` | `{ password, salt, hashedPassword }` | `Promise<boolean>` | Timing-safe password comparison |
354
+
355
+ ---
356
+
357
+ ## πŸš€ Best Practices
358
+
359
+ βœ… **Do:**
360
+
361
+ - Store salt as a **STRING** in your database
362
+ - Use environment variables for Redis credentials
363
+ - Call `signOut()` before navigating to login page
364
+ - Update session after profile changes
365
+ - Use TypeScript for type safety
366
+
367
+ ❌ **Don't:**
368
+
369
+ - Store salt as Buffer or Bytes
370
+ - Hardcode Redis credentials
371
+ - Compare passwords with `===`
372
+ - Expose session data to client components
373
+ - Use TTL shorter than 1 hour for user experience
374
+
375
+ ---
376
+
377
+ ## πŸ› Troubleshooting
378
+
379
+ ### Redis Connection Failed
380
+
381
+ **Problem**: `Error: Connection refused`
382
+
383
+ **Solution:**
384
+
385
+ ```bash
386
+ # Verify your Upstash instance is active
387
+ # Check REDIS_URL and REDIS_TOKEN in .env.local
388
+ echo $REDIS_URL
389
+ ```
390
+
391
+ ### Session Not Found / Cookie Missing
392
+
393
+ **Problem**: User is logged out unexpectedly
394
+
395
+ **Solution:**
396
+
397
+ - Check if TTL has expired
398
+ - Verify `sessionFields` includes all required user data
399
+ - Ensure cookie store is being awaited properly
400
+
401
+ ```typescript
402
+ // βœ… Correct
403
+ const cookieStore = await cookies();
404
+
405
+ // ❌ Wrong
406
+ const cookieStore = cookies();
407
+ ```
408
+
409
+ ### Type Errors with User Type
255
410
 
256
- - Ensure your `User` type is exported from your auth module
257
- - Import the `User` type in components: `import type { User } from "@/lib/auth"`
411
+ **Problem**: TypeScript complains about User type mismatch
258
412
 
259
- ## License
413
+ **Solution**: Always export and reuse your User type:
414
+
415
+ ```typescript
416
+ // lib/auth.ts
417
+ export type User = {
418
+ /* ... */
419
+ };
420
+
421
+ // app/actions/auth.ts
422
+ import type { User } from "@/lib/auth";
423
+ ```
424
+
425
+ ### Password Comparison Always Fails
426
+
427
+ **Problem**: `comparePassword()` returns false for valid password
428
+
429
+ **Solution**: Ensure salt is retrieved as a STRING:
430
+
431
+ ```typescript
432
+ // βœ… Correct
433
+ const user = await prisma.user.findUnique({ where: { id } });
434
+ const isValid = await auth.comparePassword({
435
+ password,
436
+ salt: user.salt, // string βœ“
437
+ hashedPassword: user.password,
438
+ });
439
+
440
+ // ❌ Wrong
441
+ salt: user.salt as any; // don't cast!
442
+ ```
443
+
444
+ ---
445
+
446
+ ## πŸ“– More Resources
447
+
448
+ - [Next.js Cookies API](https://nextjs.org/docs/app/api-reference/functions/cookies)
449
+ - [Upstash Redis Docs](https://upstash.com/docs)
450
+ - [Prisma Documentation](https://www.prisma.io/docs)
451
+
452
+ ---
453
+
454
+ ## πŸ“„ License
260
455
 
261
456
  MIT Β© Taimoor Safdar
457
+
458
+ ---
459
+
460
+ <div align="center">
461
+
462
+ **Built with ❀️ for the Next.js community**
463
+
464
+ [⬆ back to top](#-swift-auth)
465
+
466
+ </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swift-auth",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Custom authentication system for apps",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.js",