kroxt 1.3.10 → 1.3.11

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 (41) hide show
  1. package/README.md +270 -231
  2. package/dist/auth/adapters/drizzle.cjs +5 -0
  3. package/dist/auth/adapters/drizzle.cjs.map +2 -2
  4. package/dist/auth/adapters/drizzle.d.ts.map +1 -1
  5. package/dist/auth/adapters/drizzle.js +5 -0
  6. package/dist/auth/adapters/drizzle.js.map +2 -2
  7. package/dist/auth/adapters/index.cjs.map +1 -1
  8. package/dist/auth/adapters/index.d.ts +2 -0
  9. package/dist/auth/adapters/index.d.ts.map +1 -1
  10. package/dist/auth/adapters/memory.cjs +9 -0
  11. package/dist/auth/adapters/memory.cjs.map +2 -2
  12. package/dist/auth/adapters/memory.d.ts.map +1 -1
  13. package/dist/auth/adapters/memory.js +9 -0
  14. package/dist/auth/adapters/memory.js.map +2 -2
  15. package/dist/auth/adapters/mongoose.cjs +5 -0
  16. package/dist/auth/adapters/mongoose.cjs.map +2 -2
  17. package/dist/auth/adapters/mongoose.d.ts.map +1 -1
  18. package/dist/auth/adapters/mongoose.js +5 -0
  19. package/dist/auth/adapters/mongoose.js.map +2 -2
  20. package/dist/auth/adapters/prisma.cjs +8 -0
  21. package/dist/auth/adapters/prisma.cjs.map +2 -2
  22. package/dist/auth/adapters/prisma.d.ts.map +1 -1
  23. package/dist/auth/adapters/prisma.js +8 -0
  24. package/dist/auth/adapters/prisma.js.map +2 -2
  25. package/dist/auth/core/index.cjs +32 -5
  26. package/dist/auth/core/index.cjs.map +2 -2
  27. package/dist/auth/core/index.d.ts +3 -0
  28. package/dist/auth/core/index.d.ts.map +1 -1
  29. package/dist/auth/core/index.js +32 -5
  30. package/dist/auth/core/index.js.map +2 -2
  31. package/dist/cli/index.cjs +49 -18
  32. package/dist/cli/index.cjs.map +3 -3
  33. package/dist/cli/index.js +49 -18
  34. package/dist/cli/index.js.map +2 -2
  35. package/dist/cli/templates.cjs +5 -1
  36. package/dist/cli/templates.cjs.map +2 -2
  37. package/dist/cli/templates.d.ts +1 -1
  38. package/dist/cli/templates.d.ts.map +1 -1
  39. package/dist/cli/templates.js +5 -1
  40. package/dist/cli/templates.js.map +2 -2
  41. package/package.json +107 -107
package/README.md CHANGED
@@ -1,231 +1,270 @@
1
- # Kroxt
2
-
3
- **The Most Simplified auth library**
4
-
5
- Kroxt is a premium, framework-agnostic, and security-hardened authentication library for modern TypeScript environments. Designed for 100% schema control and "Zero-Config" onboarding.
6
-
7
- [![npm version](https://img.shields.io/npm/v/kroxt.svg)](https://www.npmjs.com/package/kroxt)
8
- [![License: MIT](https://img.shields.io/badge/License-MIT-black.svg)](https://opensource.org/licenses/MIT)
9
-
10
- ---
11
-
12
- ## ⚡ 30-Second Onboarding
13
-
14
- The recommended way to start is the **Kroxt CLI**. It detects your framework (Next.js, Express, Fastify) and scaffolds a professional auth structure automatically.
15
-
16
- ```bash
17
- npx kroxt init
18
- ```
19
-
20
- ---
21
-
22
- ## 🏗️ Core Architecture
23
-
24
- Kroxt is "Headless." It provides the **Brain** (Logic, Hashing, JWTs, Security) while you provide the **Face** (UI/Routes).
25
-
26
- ### 1. The Configuration Matrix
27
- Every feature in Kroxt is modular. Toggle security layers with a single boolean.
28
-
29
- ```typescript
30
- import { createAuth } from "kroxt";
31
- import { createMongoAdapter } from "kroxt/adapters/mongoose";
32
-
33
- export const auth = createAuth({
34
- adapter: createMongoAdapter(UserModel),
35
- secret: process.env.JWT_SECRET,
36
-
37
- // Security Layer 1: Sessions
38
- session: {
39
- expires: "15m", // Access Token duration
40
- refreshExpires: "7d", // Refresh Token duration
41
- enforceStrictRevocation: true, // DB-lookup on EVERY request (Admin-mode)
42
- },
43
-
44
- // Security Layer 2: Defense
45
- rateLimit: {
46
- max: 100, // Requests per window
47
- windowMs: 60 * 1000 // 1 Minute window
48
- },
49
-
50
- // Security Layer 3: Brute Force
51
- ipBlocking: {
52
- maxStrikes: 5, // Ban after 5 failures
53
- blockDurationMs: 15 * 60 * 1000 // 15 Min ban
54
- },
55
-
56
- // Security Layer 4: Crypto
57
- passwordPolicy: {
58
- minLength: 8,
59
- requireUppercase: true,
60
- requireSpecialCharacter: true,
61
- usePepper: true // Requires JWT_PEPPER env variable
62
- }
63
- });
64
- ```
65
-
66
- ---
67
-
68
- ## 🔌 Universal Adapters
69
-
70
- Bring your own schema. Kroxt adapts to you.
71
-
72
- ### Mongoose (MongoDB)
73
- ```typescript
74
- import { createMongoAdapter } from "kroxt/adapters/mongoose";
75
- const adapter = createMongoAdapter(User);
76
- ```
77
-
78
- ### Prisma (SQL)
79
- ```typescript
80
- import { createPrismaAdapter } from "kroxt/adapters/prisma";
81
- const adapter = createPrismaAdapter(prisma.user);
82
- ```
83
-
84
- ### Drizzle (SQL)
85
- ```typescript
86
- import { createDrizzleAdapter } from "kroxt/adapters/drizzle";
87
- import { eq } from "drizzle-orm";
88
- const adapter = createDrizzleAdapter(db, users, eq);
89
- ```
90
-
91
- ### Memory (Testing)
92
- ```typescript
93
- import { createMemoryAdapter } from "kroxt";
94
- const adapter = createMemoryAdapter();
95
- ```
96
-
97
- ---
98
-
99
- ## 🧠 API Reference
100
-
101
- ### `auth.signup(userData, password)`
102
- Registers a new user. User data is strictly typed to your schema.
103
- ```typescript
104
- const { user, accessToken, refreshToken } = await auth.signup({
105
- email: "dev@kroxt.io",
106
- role: "admin"
107
- }, "secure_password");
108
- ```
109
-
110
- ### `auth.loginWithPassword(email, password, clientIp?)`
111
- Authenticates a user and generates tokens. Pass `clientIp` to enable IP-Blocking.
112
- ```typescript
113
- const result = await auth.loginWithPassword(email, password, req.ip);
114
- ```
115
-
116
- ### `auth.refreshSession(refreshToken, clientIp?)`
117
- Rotates the session. If `enforceStrictRevocation` is on, it validates the token against the user's current password hash.
118
- ```typescript
119
- const { user, accessToken, refreshToken: newRefresh } = await auth.refreshSession(token);
120
- ```
121
-
122
- ### `auth.logout(refreshToken)`
123
- Invalidates a session.
124
- ```typescript
125
- await auth.logout(refreshToken);
126
- ```
127
-
128
- ### `auth.changePassword(userId, newPassword)`
129
- Updates password and **instantly invalidates all other active sessions** globally via Hash-Linked revocation.
130
- ```typescript
131
- await auth.changePassword(user.id, "new_secure_pass");
132
- ```
133
-
134
- ---
135
-
136
- ## 🧩 Middleware Implementation
137
-
138
- ### Express / Fastify
139
- ```typescript
140
- const protect = async (req, res, next) => {
141
- const token = req.headers.authorization?.split(" ")[1];
142
- try {
143
- const session = await auth.verifyAccessToken(token);
144
- req.user = session.user;
145
- next();
146
- } catch (err) {
147
- res.status(401).json({ error: "Unauthorized" });
148
- }
149
- };
150
- ```
151
-
152
- ---
153
-
154
- ## 🚀 Advanced Deployment
155
-
156
- ### Custom JWT Payloads
157
- Inject metadata into your tokens safely.
158
- ```typescript
159
- jwt: {
160
- payload: (user, type) => {
161
- return type === "access" ? { role: user.role } : {};
162
- }
163
- }
164
- ```
165
-
166
- ### Password Peppering
167
- Kroxt supports server-side peppering to protect against rainbow table attacks even if your database is leaked.
168
- 1. Set `usePepper: true` in config.
169
- 2. Add `JWT_PEPPER` to your `.env`.
170
-
171
- ---
172
-
173
- ## 🧠 API Reference (Exhaustive)
174
-
175
- ### `auth.signup()`
176
- | Argument | Type | Description |
177
- | --- | --- | --- |
178
- | `userData` | `Omit<User, "id">` | Your user object without the ID (ID is auto-generated) |
179
- | `password` | `string` (Optional) | Plain text password. Will be hashed using Argon2 |
180
-
181
- ### `auth.loginWithPassword()`
182
- | Argument | Type | Description |
183
- | --- | --- | --- |
184
- | `email` | `string` | User email |
185
- | `password` | `string` | User password |
186
- | `clientIp` | `string` (Optional) | Required for IP-Blocking defense |
187
-
188
- ### `auth.changePassword()`
189
- | Argument | Type | Description |
190
- | --- | --- | --- |
191
- | `userId` | `string` | The ID of the user to update |
192
- | `newPassword` | `string` | The new plain text password |
193
-
194
- > [!TIP]
195
- > **Hash-Linked Revocation**: When you call `changePassword`, all existing refresh tokens for that user are immediately invalidated because they contain a fragment of the old password hash.
196
-
197
- ---
198
-
199
- ## 🚦 Error Handling
200
-
201
- Kroxt throws descriptive errors that you can catch in your controller.
202
-
203
- ```typescript
204
- try {
205
- await auth.loginWithPassword(email, password, req.ip);
206
- } catch (err) {
207
- if (err.message === "IP is temporarily blocked.") {
208
- return res.status(403).send("Banned.");
209
- }
210
- if (err.message === "Too many requests, please try again later.") {
211
- return res.status(429).send("Slow down.");
212
- }
213
- return res.status(401).send("Invalid Credentials");
214
- }
215
- ```
216
-
217
- ---
218
-
219
- ## 🔒 Security Best Practices
220
-
221
- 1. **Environmental Pepper**: Always use `JWT_PEPPER`. This adds a server-side secret to every password hash. If your database is stolen, your user passwords are still protected by this environment variable.
222
- 2. **Strict Revocation**: Set `enforceStrictRevocation: true` for high-security areas (like admin panels). This forces a database lookup on every single request to ensure the user hasn't been banned or changed their password in the last few seconds.
223
- 3. **Dual Tokens**: Always use the provided `accessToken` for short-term API access and the `refreshToken` (stored in an `HttpOnly` cookie) for session persistence.
224
-
225
- ---
226
-
227
- ## 🔗 Ecosystem
228
- - [Kroxt Examples Repository](https://github.com/adepoju-oluwatobi/kroxt-examples)
229
-
230
- ## 📄 License
231
- MIT © [Adepoju Oluwatobi](https://github.com/adepoju-oluwatobi)
1
+ # Kroxt
2
+
3
+ **The Most Simplified auth library**
4
+
5
+ Kroxt is a premium, framework-agnostic, and security-hardened authentication engine for modern TypeScript environments. Engineered for **Next.js (App Router)**, Express, and Fastify—with 100% schema control and "Zero-Config" onboarding.
6
+
7
+ [![npm version](https://img.shields.io/npm/v/kroxt.svg)](https://www.npmjs.com/package/kroxt)
8
+ [![Next.js Ready](https://img.shields.io/badge/Next.js-First--Class-black?logo=next.js)](https://nextjs.org)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
10
+
11
+ The recommended way to start is the **Kroxt CLI**. It detects your framework (Next.js, Express, Fastify) and scaffolds a professional auth structure automatically.
12
+
13
+ ```bash
14
+ npx kroxt init
15
+ ```
16
+
17
+ ---
18
+
19
+ ## 🚀 Next.js First-Class Support
20
+
21
+ Kroxt is optimized for the Next.js App Router. Use standard **Route Handlers** for high performance and zero overhead.
22
+
23
+ ### 1. Configuration (`lib/kroxt/auth.ts`)
24
+ ```typescript
25
+ import { createAuth } from "kroxt";
26
+ import { createMongoAdapter } from "kroxt/adapters/mongoose";
27
+ import { User } from "./models/user.model";
28
+
29
+ export const auth = createAuth({
30
+ adapter: createMongoAdapter(User),
31
+ secret: process.env.JWT_SECRET,
32
+ session: { enforceStrictRevocation: true } // Real-time security
33
+ });
34
+ ```
35
+
36
+ ### 2. Login Route (`app/api/auth/login/route.ts`)
37
+ ```typescript
38
+ import { auth } from "@/lib/kroxt/auth";
39
+ import { NextRequest, NextResponse } from "next/server";
40
+
41
+ export async function POST(req: NextRequest) {
42
+ try {
43
+ const { email, password } = await req.json();
44
+ const result = await auth.loginWithPassword(email, password, req.ip);
45
+
46
+ return NextResponse.json(result);
47
+ } catch (error: any) {
48
+ return NextResponse.json({ error: error.message }, { status: 401 });
49
+ }
50
+ }
51
+ ```
52
+
53
+ ## 🏗️ Core Architecture
54
+
55
+ Kroxt is "Headless." It provides the **Brain** (Logic, Hashing, JWTs, Security) while you provide the **Face** (UI/Routes).
56
+
57
+ ### 1. The Configuration Matrix
58
+ Every feature in Kroxt is modular. Toggle security layers with a single boolean.
59
+
60
+ ```typescript
61
+ import { createAuth } from "kroxt";
62
+ import { createMongoAdapter } from "kroxt/adapters/mongoose";
63
+
64
+ export const auth = createAuth({
65
+ adapter: createMongoAdapter(UserModel),
66
+ secret: process.env.JWT_SECRET,
67
+
68
+ // Security Layer 1: Sessions
69
+ session: {
70
+ expires: "15m", // Access Token duration
71
+ refreshExpires: "7d", // Refresh Token duration
72
+ enforceStrictRevocation: true, // DB-lookup on EVERY request (Admin-mode)
73
+ },
74
+
75
+ // Security Layer 2: Defense
76
+ rateLimit: {
77
+ max: 100, // Requests per window
78
+ windowMs: 60 * 1000 // 1 Minute window
79
+ },
80
+
81
+ // Security Layer 3: Brute Force
82
+ ipBlocking: {
83
+ maxStrikes: 5, // Ban after 5 failures
84
+ blockDurationMs: 15 * 60 * 1000 // 15 Min ban
85
+ },
86
+
87
+ // Security Layer 4: Crypto
88
+ passwordPolicy: {
89
+ minLength: 8,
90
+ requireUppercase: true,
91
+ requireSpecialCharacter: true,
92
+ usePepper: true // Requires JWT_PEPPER env variable
93
+ }
94
+ });
95
+ ```
96
+
97
+ ---
98
+
99
+ ## 🔌 Universal Adapters
100
+
101
+ Bring your own schema. Kroxt adapts to you.
102
+
103
+ ### Mongoose (MongoDB)
104
+ ```typescript
105
+ import { createMongoAdapter } from "kroxt/adapters/mongoose";
106
+ const adapter = createMongoAdapter(User);
107
+ ```
108
+
109
+ ### Prisma (SQL)
110
+ ```typescript
111
+ import { createPrismaAdapter } from "kroxt/adapters/prisma";
112
+ const adapter = createPrismaAdapter(prisma.user);
113
+ ```
114
+
115
+ ### Drizzle (SQL)
116
+ ```typescript
117
+ import { createDrizzleAdapter } from "kroxt/adapters/drizzle";
118
+ import { eq } from "drizzle-orm";
119
+ const adapter = createDrizzleAdapter(db, users, eq);
120
+ ```
121
+
122
+ ### Memory (Testing)
123
+ ```typescript
124
+ import { createMemoryAdapter } from "kroxt";
125
+ const adapter = createMemoryAdapter();
126
+ ```
127
+
128
+ ---
129
+
130
+ ## 🧠 API Reference
131
+
132
+ ### `auth.signup(userData, password)`
133
+ Registers a new user. User data is strictly typed to your schema.
134
+ ```typescript
135
+ const { user, accessToken, refreshToken } = await auth.signup({
136
+ email: "dev@kroxt.io",
137
+ role: "admin"
138
+ }, "secure_password");
139
+ ```
140
+
141
+ ### `auth.loginWithPassword(email, password, clientIp?)`
142
+ Authenticates a user and generates tokens. Pass `clientIp` to enable IP-Blocking.
143
+ ```typescript
144
+ const result = await auth.loginWithPassword(email, password, req.ip);
145
+ ```
146
+
147
+ ### `auth.refreshSession(refreshToken, clientIp?)`
148
+ Rotates the session. If `enforceStrictRevocation` is on, it validates the token against the user's current password hash.
149
+ ```typescript
150
+ const { user, accessToken, refreshToken: newRefresh } = await auth.refreshSession(token);
151
+ ```
152
+
153
+ ### `auth.logout(userId)`
154
+ Terminates all active sessions for a user globally. This increments the `sessionVersion` in the database, invalidating all current tokens instantly.
155
+ ```typescript
156
+ await auth.logout(payload.sub);
157
+ ```
158
+
159
+ ### `auth.changePassword(userId, newPassword)`
160
+ Updates password and **instantly invalidates all other active sessions** globally via Hash-Linked revocation.
161
+ ```typescript
162
+ await auth.changePassword(payload.sub, "new_secure_pass");
163
+ ```
164
+
165
+ ---
166
+
167
+ ## 🧩 Middleware Implementation
168
+
169
+ ### Express / Fastify
170
+ ```typescript
171
+ const protect = async (req, res, next) => {
172
+ const token = req.headers.authorization?.split(" ")[1];
173
+ try {
174
+ const session = await auth.verifyAccessToken(token);
175
+ req.user = session.user;
176
+ next();
177
+ } catch (err) {
178
+ res.status(401).json({ error: "Unauthorized" });
179
+ }
180
+ };
181
+ ```
182
+
183
+ ---
184
+
185
+ ## 🚀 Advanced Deployment
186
+
187
+ ### Custom JWT Payloads
188
+ Inject metadata into your tokens safely.
189
+ ```typescript
190
+ jwt: {
191
+ payload: (user, type) => {
192
+ return type === "access" ? { role: user.role } : {};
193
+ }
194
+ }
195
+ ```
196
+
197
+ ### Password Peppering
198
+ Kroxt supports server-side peppering to protect against rainbow table attacks even if your database is leaked.
199
+ 1. Set `usePepper: true` in config.
200
+ 2. Add `JWT_PEPPER` to your `.env`.
201
+
202
+ ---
203
+
204
+ ## 🧠 API Reference (Exhaustive)
205
+
206
+ ### `auth.signup()`
207
+ | Argument | Type | Description |
208
+ | --- | --- | --- |
209
+ | `userData` | `Omit<User, "id">` | Your user object without the ID (ID is auto-generated) |
210
+ | `password` | `string` (Optional) | Plain text password. Will be hashed using Argon2 |
211
+
212
+ ### `auth.loginWithPassword()`
213
+ | Argument | Type | Description |
214
+ | --- | --- | --- |
215
+ | `email` | `string` | User email |
216
+ | `password` | `string` | User password |
217
+ | `clientIp` | `string` (Optional) | Required for IP-Blocking defense |
218
+
219
+ ### `auth.logout()`
220
+ | Argument | Type | Description |
221
+ | --- | --- | --- |
222
+ | `userId` | `string` | The ID of the user whose sessions will be revoked |
223
+
224
+ > [!IMPORTANT]
225
+ > **Global Session Revocation**: Kroxt uses a `sessionVersion` counter. When `logout` is called, this counter increments in the database. Any token presented with an older version will be rejected during verification if `enforceStrictRevocation` is enabled or during session refreshes.
226
+
227
+ ### `auth.changePassword()`
228
+ | Argument | Type | Description |
229
+ | --- | --- | --- |
230
+ | `userId` | `string` | The ID of the user to update |
231
+ | `newPassword` | `string` | The new plain text password |
232
+
233
+ > [!TIP]
234
+ > **Hash-Linked Revocation**: When you call `changePassword`, all existing refresh tokens for that user are immediately invalidated because they contain a fragment of the old password hash (`pw_frag`).
235
+
236
+ ---
237
+
238
+ ## 🚦 Error Handling
239
+
240
+ Kroxt throws descriptive errors that you can catch in your controller.
241
+
242
+ ```typescript
243
+ try {
244
+ await auth.loginWithPassword(email, password, req.ip);
245
+ } catch (err) {
246
+ if (err.message === "IP is temporarily blocked.") {
247
+ return res.status(403).send("Banned.");
248
+ }
249
+ if (err.message === "Too many requests, please try again later.") {
250
+ return res.status(429).send("Slow down.");
251
+ }
252
+ return res.status(401).send("Invalid Credentials");
253
+ }
254
+ ```
255
+
256
+ ---
257
+
258
+ ## 🔒 Security Best Practices
259
+
260
+ 1. **Environmental Pepper**: Always use `JWT_PEPPER`. This adds a server-side secret to every password hash. If your database is stolen, your user passwords are still protected by this environment variable.
261
+ 2. **Strict Revocation**: Set `enforceStrictRevocation: true` for high-security areas (like admin panels). This forces a database lookup on every single request to ensure the user hasn't been banned or changed their password in the last few seconds.
262
+ 3. **Dual Tokens**: Always use the provided `accessToken` for short-term API access and the `refreshToken` (stored in an `HttpOnly` cookie) for session persistence.
263
+
264
+ ---
265
+
266
+ ## 🔗 Ecosystem
267
+ - [Kroxt Examples Repository](https://github.com/adepoju-oluwatobi/kroxt-examples)
268
+
269
+ ## 📄 License
270
+ MIT © [Adepoju Oluwatobi](https://github.com/adepoju-oluwatobi)
@@ -45,6 +45,11 @@ function createDrizzleAdapter(db, table, eq, rateLimitTable) {
45
45
  oauthProvider: provider,
46
46
  oauthId: providerId
47
47
  }).where(eq(table.id, userId));
48
+ },
49
+ async invalidateSession(userId) {
50
+ await db.update(table).set({
51
+ sessionVersion: (table.sessionVersion || 0) + 1
52
+ }).where(eq(table.id, userId));
48
53
  }
49
54
  };
50
55
  if (rateLimitTable) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/auth/adapters/drizzle.ts"],
4
- "sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates a Drizzle ORM adapter.\r\n * \r\n * Works with any Drizzle-supported database (PostgreSQL, MySQL, SQLite)\r\n * by using the standard drizzle-orm `db` instance and table definition.\r\n * \r\n * @param db - The Drizzle database instance.\r\n * @param table - The Drizzle table representing users.\r\n * @param eq - The Drizzle `eq` operator (imported from `drizzle-orm`).\r\n * @param rateLimitTable - Optional Drizzle table for rate limiting tracking.\r\n * @returns An AuthAdapter compliant object.\r\n */\r\nexport function createDrizzleAdapter<TUser extends User = User>(\r\n db: any,\r\n table: any,\r\n eq: any,\r\n rateLimitTable?: any\r\n): AuthAdapter<TUser> {\r\n const adapter: AuthAdapter<TUser> = {\r\n async createUser(data: any) {\r\n const dataToSave = { id: data.id || globalThis.crypto.randomUUID(), ...data };\r\n const results = await db.insert(table).values(dataToSave).returning();\r\n return results[0] as TUser;\r\n },\r\n\r\n async findUserByEmail(email: string) {\r\n const results = await db.select().from(table).where(eq(table.email, email)).limit(1);\r\n return (results[0] || null) as TUser | null;\r\n },\r\n\r\n async findUserById(id: string) {\r\n const results = await db.select().from(table).where(eq(table.id, id)).limit(1);\r\n return (results[0] || null) as TUser | null;\r\n },\r\n\r\n async updateUser(id: string, data: Partial<TUser>) {\r\n const results = await db.update(table).set(data).where(eq(table.id, id)).returning();\r\n return (results[0] || null) as TUser | null;\r\n },\r\n\r\n async linkOAuthAccount(userId: string, provider: string, providerId: string) {\r\n await db.update(table)\r\n .set({\r\n oauthProvider: provider,\r\n oauthId: providerId,\r\n })\r\n .where(eq(table.id, userId));\r\n },\r\n };\r\n\r\n if (rateLimitTable) {\r\n adapter.incrementRateLimit = async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n const resetTime = now + windowMs;\r\n \r\n let records = await db.select().from(rateLimitTable).where(eq(rateLimitTable.key, key)).limit(1);\r\n let record = records[0];\r\n \r\n if (!record || now > record.resetTime) {\r\n if (record) {\r\n await db.update(rateLimitTable).set({ count: 1, resetTime }).where(eq(rateLimitTable.key, key));\r\n } else {\r\n await db.insert(rateLimitTable).values({ key, count: 1, resetTime });\r\n }\r\n return { count: 1, resetTime };\r\n } else {\r\n await db.update(rateLimitTable).set({ count: record.count + 1 }).where(eq(rateLimitTable.key, key));\r\n return { count: record.count + 1, resetTime: record.resetTime };\r\n }\r\n };\r\n\r\n adapter.getRateLimit = async (key: string) => {\r\n const now = Date.now();\r\n let records = await db.select().from(rateLimitTable).where(eq(rateLimitTable.key, key)).limit(1);\r\n let record = records[0];\r\n if (!record || now > record.resetTime) return null;\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n }\r\n\r\n return adapter;\r\n}\r\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAcO,SAAS,qBACd,IACA,OACA,IACA,gBACoB;AACpB,QAAM,UAA8B;AAAA,IAClC,MAAM,WAAW,MAAW;AAC1B,YAAM,aAAa,EAAE,IAAI,KAAK,MAAM,WAAW,OAAO,WAAW,GAAG,GAAG,KAAK;AAC5E,YAAM,UAAU,MAAM,GAAG,OAAO,KAAK,EAAE,OAAO,UAAU,EAAE,UAAU;AACpE,aAAO,QAAQ,CAAC;AAAA,IAClB;AAAA,IAEA,MAAM,gBAAgB,OAAe;AACnC,YAAM,UAAU,MAAM,GAAG,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,GAAG,MAAM,OAAO,KAAK,CAAC,EAAE,MAAM,CAAC;AACnF,aAAQ,QAAQ,CAAC,KAAK;AAAA,IACxB;AAAA,IAEA,MAAM,aAAa,IAAY;AAC7B,YAAM,UAAU,MAAM,GAAG,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC;AAC7E,aAAQ,QAAQ,CAAC,KAAK;AAAA,IACxB;AAAA,IAEA,MAAM,WAAW,IAAY,MAAsB;AACjD,YAAM,UAAU,MAAM,GAAG,OAAO,KAAK,EAAE,IAAI,IAAI,EAAE,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC,EAAE,UAAU;AACnF,aAAQ,QAAQ,CAAC,KAAK;AAAA,IACxB;AAAA,IAEA,MAAM,iBAAiB,QAAgB,UAAkB,YAAoB;AAC3E,YAAM,GAAG,OAAO,KAAK,EAClB,IAAI;AAAA,QACH,eAAe;AAAA,QACf,SAAS;AAAA,MACX,CAAC,EACA,MAAM,GAAG,MAAM,IAAI,MAAM,CAAC;AAAA,IAC/B;AAAA,EACF;AAEA,MAAI,gBAAgB;AAClB,YAAQ,qBAAqB,OAAO,KAAa,aAAqB;AACpE,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,YAAY,MAAM;AAExB,UAAI,UAAU,MAAM,GAAG,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,GAAG,eAAe,KAAK,GAAG,CAAC,EAAE,MAAM,CAAC;AAC/F,UAAI,SAAS,QAAQ,CAAC;AAEtB,UAAI,CAAC,UAAU,MAAM,OAAO,WAAW;AACnC,YAAI,QAAQ;AACR,gBAAM,GAAG,OAAO,cAAc,EAAE,IAAI,EAAE,OAAO,GAAG,UAAU,CAAC,EAAE,MAAM,GAAG,eAAe,KAAK,GAAG,CAAC;AAAA,QAClG,OAAO;AACH,gBAAM,GAAG,OAAO,cAAc,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,UAAU,CAAC;AAAA,QACvE;AACA,eAAO,EAAE,OAAO,GAAG,UAAU;AAAA,MACjC,OAAO;AACH,cAAM,GAAG,OAAO,cAAc,EAAE,IAAI,EAAE,OAAO,OAAO,QAAQ,EAAE,CAAC,EAAE,MAAM,GAAG,eAAe,KAAK,GAAG,CAAC;AAClG,eAAO,EAAE,OAAO,OAAO,QAAQ,GAAG,WAAW,OAAO,UAAU;AAAA,MAClE;AAAA,IACF;AAEA,YAAQ,eAAe,OAAO,QAAgB;AAC5C,YAAM,MAAM,KAAK,IAAI;AACrB,UAAI,UAAU,MAAM,GAAG,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,GAAG,eAAe,KAAK,GAAG,CAAC,EAAE,MAAM,CAAC;AAC/F,UAAI,SAAS,QAAQ,CAAC;AACtB,UAAI,CAAC,UAAU,MAAM,OAAO,UAAW,QAAO;AAC9C,aAAO,EAAE,OAAO,OAAO,OAAO,WAAW,OAAO,UAAU;AAAA,IAC5D;AAAA,EACF;AAEA,SAAO;AACT;",
4
+ "sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates a Drizzle ORM adapter.\r\n * \r\n * Works with any Drizzle-supported database (PostgreSQL, MySQL, SQLite)\r\n * by using the standard drizzle-orm `db` instance and table definition.\r\n * \r\n * @param db - The Drizzle database instance.\r\n * @param table - The Drizzle table representing users.\r\n * @param eq - The Drizzle `eq` operator (imported from `drizzle-orm`).\r\n * @param rateLimitTable - Optional Drizzle table for rate limiting tracking.\r\n * @returns An AuthAdapter compliant object.\r\n */\r\nexport function createDrizzleAdapter<TUser extends User = User>(\r\n db: any,\r\n table: any,\r\n eq: any,\r\n rateLimitTable?: any\r\n): AuthAdapter<TUser> {\r\n const adapter: AuthAdapter<TUser> = {\r\n async createUser(data: any) {\r\n const dataToSave = { id: data.id || globalThis.crypto.randomUUID(), ...data };\r\n const results = await db.insert(table).values(dataToSave).returning();\r\n return results[0] as TUser;\r\n },\r\n\r\n async findUserByEmail(email: string) {\r\n const results = await db.select().from(table).where(eq(table.email, email)).limit(1);\r\n return (results[0] || null) as TUser | null;\r\n },\r\n\r\n async findUserById(id: string) {\r\n const results = await db.select().from(table).where(eq(table.id, id)).limit(1);\r\n return (results[0] || null) as TUser | null;\r\n },\r\n\r\n async updateUser(id: string, data: Partial<TUser>) {\r\n const results = await db.update(table).set(data).where(eq(table.id, id)).returning();\r\n return (results[0] || null) as TUser | null;\r\n },\r\n\r\n async linkOAuthAccount(userId: string, provider: string, providerId: string) {\r\n await db.update(table)\r\n .set({\r\n oauthProvider: provider,\r\n oauthId: providerId,\r\n })\r\n .where(eq(table.id, userId));\r\n },\r\n\r\n async invalidateSession(userId: string) {\r\n await db.update(table)\r\n .set({\r\n sessionVersion: (table.sessionVersion || 0) + 1,\r\n })\r\n .where(eq(table.id, userId));\r\n },\r\n };\r\n\r\n if (rateLimitTable) {\r\n adapter.incrementRateLimit = async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n const resetTime = now + windowMs;\r\n \r\n let records = await db.select().from(rateLimitTable).where(eq(rateLimitTable.key, key)).limit(1);\r\n let record = records[0];\r\n \r\n if (!record || now > record.resetTime) {\r\n if (record) {\r\n await db.update(rateLimitTable).set({ count: 1, resetTime }).where(eq(rateLimitTable.key, key));\r\n } else {\r\n await db.insert(rateLimitTable).values({ key, count: 1, resetTime });\r\n }\r\n return { count: 1, resetTime };\r\n } else {\r\n await db.update(rateLimitTable).set({ count: record.count + 1 }).where(eq(rateLimitTable.key, key));\r\n return { count: record.count + 1, resetTime: record.resetTime };\r\n }\r\n };\r\n\r\n adapter.getRateLimit = async (key: string) => {\r\n const now = Date.now();\r\n let records = await db.select().from(rateLimitTable).where(eq(rateLimitTable.key, key)).limit(1);\r\n let record = records[0];\r\n if (!record || now > record.resetTime) return null;\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n }\r\n\r\n return adapter;\r\n}\r\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../../../src/auth/adapters/drizzle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,SAAS,IAAI,GAAG,IAAI,EAC5D,EAAE,EAAE,GAAG,EACP,KAAK,EAAE,GAAG,EACV,EAAE,EAAE,GAAG,EACP,cAAc,CAAC,EAAE,GAAG,GACnB,WAAW,CAAC,KAAK,CAAC,CAgEpB"}
1
+ {"version":3,"file":"drizzle.d.ts","sourceRoot":"","sources":["../../../src/auth/adapters/drizzle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,SAAS,IAAI,GAAG,IAAI,EAC5D,EAAE,EAAE,GAAG,EACP,KAAK,EAAE,GAAG,EACV,EAAE,EAAE,GAAG,EACP,cAAc,CAAC,EAAE,GAAG,GACnB,WAAW,CAAC,KAAK,CAAC,CAwEpB"}
@@ -22,6 +22,11 @@ function createDrizzleAdapter(db, table, eq, rateLimitTable) {
22
22
  oauthProvider: provider,
23
23
  oauthId: providerId
24
24
  }).where(eq(table.id, userId));
25
+ },
26
+ async invalidateSession(userId) {
27
+ await db.update(table).set({
28
+ sessionVersion: (table.sessionVersion || 0) + 1
29
+ }).where(eq(table.id, userId));
25
30
  }
26
31
  };
27
32
  if (rateLimitTable) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/auth/adapters/drizzle.ts"],
4
- "sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates a Drizzle ORM adapter.\r\n * \r\n * Works with any Drizzle-supported database (PostgreSQL, MySQL, SQLite)\r\n * by using the standard drizzle-orm `db` instance and table definition.\r\n * \r\n * @param db - The Drizzle database instance.\r\n * @param table - The Drizzle table representing users.\r\n * @param eq - The Drizzle `eq` operator (imported from `drizzle-orm`).\r\n * @param rateLimitTable - Optional Drizzle table for rate limiting tracking.\r\n * @returns An AuthAdapter compliant object.\r\n */\r\nexport function createDrizzleAdapter<TUser extends User = User>(\r\n db: any,\r\n table: any,\r\n eq: any,\r\n rateLimitTable?: any\r\n): AuthAdapter<TUser> {\r\n const adapter: AuthAdapter<TUser> = {\r\n async createUser(data: any) {\r\n const dataToSave = { id: data.id || globalThis.crypto.randomUUID(), ...data };\r\n const results = await db.insert(table).values(dataToSave).returning();\r\n return results[0] as TUser;\r\n },\r\n\r\n async findUserByEmail(email: string) {\r\n const results = await db.select().from(table).where(eq(table.email, email)).limit(1);\r\n return (results[0] || null) as TUser | null;\r\n },\r\n\r\n async findUserById(id: string) {\r\n const results = await db.select().from(table).where(eq(table.id, id)).limit(1);\r\n return (results[0] || null) as TUser | null;\r\n },\r\n\r\n async updateUser(id: string, data: Partial<TUser>) {\r\n const results = await db.update(table).set(data).where(eq(table.id, id)).returning();\r\n return (results[0] || null) as TUser | null;\r\n },\r\n\r\n async linkOAuthAccount(userId: string, provider: string, providerId: string) {\r\n await db.update(table)\r\n .set({\r\n oauthProvider: provider,\r\n oauthId: providerId,\r\n })\r\n .where(eq(table.id, userId));\r\n },\r\n };\r\n\r\n if (rateLimitTable) {\r\n adapter.incrementRateLimit = async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n const resetTime = now + windowMs;\r\n \r\n let records = await db.select().from(rateLimitTable).where(eq(rateLimitTable.key, key)).limit(1);\r\n let record = records[0];\r\n \r\n if (!record || now > record.resetTime) {\r\n if (record) {\r\n await db.update(rateLimitTable).set({ count: 1, resetTime }).where(eq(rateLimitTable.key, key));\r\n } else {\r\n await db.insert(rateLimitTable).values({ key, count: 1, resetTime });\r\n }\r\n return { count: 1, resetTime };\r\n } else {\r\n await db.update(rateLimitTable).set({ count: record.count + 1 }).where(eq(rateLimitTable.key, key));\r\n return { count: record.count + 1, resetTime: record.resetTime };\r\n }\r\n };\r\n\r\n adapter.getRateLimit = async (key: string) => {\r\n const now = Date.now();\r\n let records = await db.select().from(rateLimitTable).where(eq(rateLimitTable.key, key)).limit(1);\r\n let record = records[0];\r\n if (!record || now > record.resetTime) return null;\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n }\r\n\r\n return adapter;\r\n}\r\n"],
5
- "mappings": "AAcO,SAAS,qBACd,IACA,OACA,IACA,gBACoB;AACpB,QAAM,UAA8B;AAAA,IAClC,MAAM,WAAW,MAAW;AAC1B,YAAM,aAAa,EAAE,IAAI,KAAK,MAAM,WAAW,OAAO,WAAW,GAAG,GAAG,KAAK;AAC5E,YAAM,UAAU,MAAM,GAAG,OAAO,KAAK,EAAE,OAAO,UAAU,EAAE,UAAU;AACpE,aAAO,QAAQ,CAAC;AAAA,IAClB;AAAA,IAEA,MAAM,gBAAgB,OAAe;AACnC,YAAM,UAAU,MAAM,GAAG,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,GAAG,MAAM,OAAO,KAAK,CAAC,EAAE,MAAM,CAAC;AACnF,aAAQ,QAAQ,CAAC,KAAK;AAAA,IACxB;AAAA,IAEA,MAAM,aAAa,IAAY;AAC7B,YAAM,UAAU,MAAM,GAAG,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC;AAC7E,aAAQ,QAAQ,CAAC,KAAK;AAAA,IACxB;AAAA,IAEA,MAAM,WAAW,IAAY,MAAsB;AACjD,YAAM,UAAU,MAAM,GAAG,OAAO,KAAK,EAAE,IAAI,IAAI,EAAE,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC,EAAE,UAAU;AACnF,aAAQ,QAAQ,CAAC,KAAK;AAAA,IACxB;AAAA,IAEA,MAAM,iBAAiB,QAAgB,UAAkB,YAAoB;AAC3E,YAAM,GAAG,OAAO,KAAK,EAClB,IAAI;AAAA,QACH,eAAe;AAAA,QACf,SAAS;AAAA,MACX,CAAC,EACA,MAAM,GAAG,MAAM,IAAI,MAAM,CAAC;AAAA,IAC/B;AAAA,EACF;AAEA,MAAI,gBAAgB;AAClB,YAAQ,qBAAqB,OAAO,KAAa,aAAqB;AACpE,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,YAAY,MAAM;AAExB,UAAI,UAAU,MAAM,GAAG,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,GAAG,eAAe,KAAK,GAAG,CAAC,EAAE,MAAM,CAAC;AAC/F,UAAI,SAAS,QAAQ,CAAC;AAEtB,UAAI,CAAC,UAAU,MAAM,OAAO,WAAW;AACnC,YAAI,QAAQ;AACR,gBAAM,GAAG,OAAO,cAAc,EAAE,IAAI,EAAE,OAAO,GAAG,UAAU,CAAC,EAAE,MAAM,GAAG,eAAe,KAAK,GAAG,CAAC;AAAA,QAClG,OAAO;AACH,gBAAM,GAAG,OAAO,cAAc,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,UAAU,CAAC;AAAA,QACvE;AACA,eAAO,EAAE,OAAO,GAAG,UAAU;AAAA,MACjC,OAAO;AACH,cAAM,GAAG,OAAO,cAAc,EAAE,IAAI,EAAE,OAAO,OAAO,QAAQ,EAAE,CAAC,EAAE,MAAM,GAAG,eAAe,KAAK,GAAG,CAAC;AAClG,eAAO,EAAE,OAAO,OAAO,QAAQ,GAAG,WAAW,OAAO,UAAU;AAAA,MAClE;AAAA,IACF;AAEA,YAAQ,eAAe,OAAO,QAAgB;AAC5C,YAAM,MAAM,KAAK,IAAI;AACrB,UAAI,UAAU,MAAM,GAAG,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,GAAG,eAAe,KAAK,GAAG,CAAC,EAAE,MAAM,CAAC;AAC/F,UAAI,SAAS,QAAQ,CAAC;AACtB,UAAI,CAAC,UAAU,MAAM,OAAO,UAAW,QAAO;AAC9C,aAAO,EAAE,OAAO,OAAO,OAAO,WAAW,OAAO,UAAU;AAAA,IAC5D;AAAA,EACF;AAEA,SAAO;AACT;",
4
+ "sourcesContent": ["import type { AuthAdapter, User } from \"./index.js\";\r\n\r\n/**\r\n * Creates a Drizzle ORM adapter.\r\n * \r\n * Works with any Drizzle-supported database (PostgreSQL, MySQL, SQLite)\r\n * by using the standard drizzle-orm `db` instance and table definition.\r\n * \r\n * @param db - The Drizzle database instance.\r\n * @param table - The Drizzle table representing users.\r\n * @param eq - The Drizzle `eq` operator (imported from `drizzle-orm`).\r\n * @param rateLimitTable - Optional Drizzle table for rate limiting tracking.\r\n * @returns An AuthAdapter compliant object.\r\n */\r\nexport function createDrizzleAdapter<TUser extends User = User>(\r\n db: any,\r\n table: any,\r\n eq: any,\r\n rateLimitTable?: any\r\n): AuthAdapter<TUser> {\r\n const adapter: AuthAdapter<TUser> = {\r\n async createUser(data: any) {\r\n const dataToSave = { id: data.id || globalThis.crypto.randomUUID(), ...data };\r\n const results = await db.insert(table).values(dataToSave).returning();\r\n return results[0] as TUser;\r\n },\r\n\r\n async findUserByEmail(email: string) {\r\n const results = await db.select().from(table).where(eq(table.email, email)).limit(1);\r\n return (results[0] || null) as TUser | null;\r\n },\r\n\r\n async findUserById(id: string) {\r\n const results = await db.select().from(table).where(eq(table.id, id)).limit(1);\r\n return (results[0] || null) as TUser | null;\r\n },\r\n\r\n async updateUser(id: string, data: Partial<TUser>) {\r\n const results = await db.update(table).set(data).where(eq(table.id, id)).returning();\r\n return (results[0] || null) as TUser | null;\r\n },\r\n\r\n async linkOAuthAccount(userId: string, provider: string, providerId: string) {\r\n await db.update(table)\r\n .set({\r\n oauthProvider: provider,\r\n oauthId: providerId,\r\n })\r\n .where(eq(table.id, userId));\r\n },\r\n\r\n async invalidateSession(userId: string) {\r\n await db.update(table)\r\n .set({\r\n sessionVersion: (table.sessionVersion || 0) + 1,\r\n })\r\n .where(eq(table.id, userId));\r\n },\r\n };\r\n\r\n if (rateLimitTable) {\r\n adapter.incrementRateLimit = async (key: string, windowMs: number) => {\r\n const now = Date.now();\r\n const resetTime = now + windowMs;\r\n \r\n let records = await db.select().from(rateLimitTable).where(eq(rateLimitTable.key, key)).limit(1);\r\n let record = records[0];\r\n \r\n if (!record || now > record.resetTime) {\r\n if (record) {\r\n await db.update(rateLimitTable).set({ count: 1, resetTime }).where(eq(rateLimitTable.key, key));\r\n } else {\r\n await db.insert(rateLimitTable).values({ key, count: 1, resetTime });\r\n }\r\n return { count: 1, resetTime };\r\n } else {\r\n await db.update(rateLimitTable).set({ count: record.count + 1 }).where(eq(rateLimitTable.key, key));\r\n return { count: record.count + 1, resetTime: record.resetTime };\r\n }\r\n };\r\n\r\n adapter.getRateLimit = async (key: string) => {\r\n const now = Date.now();\r\n let records = await db.select().from(rateLimitTable).where(eq(rateLimitTable.key, key)).limit(1);\r\n let record = records[0];\r\n if (!record || now > record.resetTime) return null;\r\n return { count: record.count, resetTime: record.resetTime };\r\n };\r\n }\r\n\r\n return adapter;\r\n}\r\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/auth/adapters/index.ts"],
4
- "sourcesContent": ["export interface BaseUser {\r\n id: string;\r\n email: string;\r\n passwordHash?: string;\r\n role?: string;\r\n}\r\n\r\n// Allows any extended fields natively (like nin, bvn, maritalStatus, etc.)\r\nexport type User<TExtended = Record<string, any>> = BaseUser & TExtended;\r\n\r\nexport interface AuthAdapter<TUser = User> {\r\n createUser: (data: any) => Promise<TUser>;\r\n findUserByEmail: (email: string) => Promise<TUser | null>;\r\n findUserById: (id: string) => Promise<TUser | null>;\r\n updateUser?: (id: string, data: Partial<TUser>) => Promise<TUser | null>;\r\n linkOAuthAccount: (userId: string, provider: string, providerId: string) => Promise<void>;\r\n incrementRateLimit?: (key: string, windowMs: number) => Promise<{ count: number; resetTime: number }>;\r\n getRateLimit?: (key: string) => Promise<{ count: number; resetTime: number } | null>;\r\n}\r\n"],
4
+ "sourcesContent": ["export interface BaseUser {\r\n id: string;\r\n email: string;\r\n passwordHash?: string;\r\n role?: string;\r\n sessionVersion?: number;\r\n}\r\n\r\n// Allows any extended fields natively (like nin, bvn, maritalStatus, etc.)\r\nexport type User<TExtended = Record<string, any>> = BaseUser & TExtended;\r\n\r\nexport interface AuthAdapter<TUser = User> {\r\n createUser: (data: any) => Promise<TUser>;\r\n findUserByEmail: (email: string) => Promise<TUser | null>;\r\n findUserById: (id: string) => Promise<TUser | null>;\r\n updateUser?: (id: string, data: Partial<TUser>) => Promise<TUser | null>;\r\n linkOAuthAccount: (userId: string, provider: string, providerId: string) => Promise<void>;\r\n incrementRateLimit?: (key: string, windowMs: number) => Promise<{ count: number; resetTime: number }>;\r\n getRateLimit?: (key: string) => Promise<{ count: number; resetTime: number } | null>;\r\n invalidateSession?: (userId: string) => Promise<void>;\r\n}\r\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;AAAA;AAAA;",
6
6
  "names": []
7
7
  }
@@ -3,6 +3,7 @@ export interface BaseUser {
3
3
  email: string;
4
4
  passwordHash?: string;
5
5
  role?: string;
6
+ sessionVersion?: number;
6
7
  }
7
8
  export type User<TExtended = Record<string, any>> = BaseUser & TExtended;
8
9
  export interface AuthAdapter<TUser = User> {
@@ -19,5 +20,6 @@ export interface AuthAdapter<TUser = User> {
19
20
  count: number;
20
21
  resetTime: number;
21
22
  } | null>;
23
+ invalidateSession?: (userId: string) => Promise<void>;
22
24
  }
23
25
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/adapters/index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAGD,MAAM,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,QAAQ,GAAG,SAAS,CAAC;AAEzE,MAAM,WAAW,WAAW,CAAC,KAAK,GAAG,IAAI;IACvC,UAAU,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1C,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAC1D,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IACpD,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IACzE,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1F,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtG,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;CACtF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/adapters/index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAGD,MAAM,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,QAAQ,GAAG,SAAS,CAAC;AAEzE,MAAM,WAAW,WAAW,CAAC,KAAK,GAAG,IAAI;IACvC,UAAU,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1C,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAC1D,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IACpD,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IACzE,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1F,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtG,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACrF,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD"}