kroxt 1.1.1 → 1.1.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kroxt Auth
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,82 +1,210 @@
1
- # kroxt
2
-
3
- A framework-agnostic, modular authentication engine for modern TypeScript applications. Built for security, extensibility, and ease of use.
4
-
5
- ## Features
6
-
7
- - 🔐 **Secure Hashing**: Powered by `argon2` for industry-standard password security.
8
- - 🎟️ **Stateless Sessions**: Managed via `jose` with high-performance JWT signing and verification.
9
- - 🌍 **OAuth Ready**: Built-in support for GitHub and Google OAuth via `arctic`.
10
- - 🧩 **Database Agnostic**: Use Mongoose, Prisma, Drizzle, or even in-memory stores via the `AuthAdapter` pattern.
11
- - **Zod Schema Support**: Perfectly preserves and types your extended user metadata.
12
- - 🚀 **ESM First**: Native support for NodeNext module resolution.
13
-
14
- ## Installation
15
-
16
- ```bash
17
- npm install kroxt
18
- ```
19
-
20
- ## Quick Start
21
-
22
- ### 1. Initialize the Auth Engine
23
-
24
- ```typescript
25
- import { createAuth } from "kroxt";
26
- import { myDatabaseAdapter } from "./myAdapter.js";
27
-
28
- const auth = createAuth({
29
- adapter: myDatabaseAdapter,
30
- secret: process.env.AUTH_SECRET,
31
- session: {
32
- expires: "7d" // jose compatible duration
33
- }
34
- });
35
- ```
36
-
37
- ### 2. Sign Up a User
38
-
39
- ```typescript
40
- const { user, token } = await auth.signup({
41
- email: "user@example.com",
42
- firstName: "Tobi",
43
- role: "tenant",
44
- // ...any other extended fields supported by your adapter
45
- }, "strong-password-123");
46
- ```
47
-
48
- ### 3. Log In
49
-
50
- ```typescript
51
- const { user, token } = await auth.loginWithPassword("user@example.com", "password");
52
- ```
53
-
54
- ### 4. Verify a Session
55
-
56
- ```typescript
57
- const payload = await auth.verifyToken(token);
58
- // auth.verifyToken returns the signed payload { sub: string, role: string, ... }
59
- ```
60
-
61
- ## The Adapter Pattern
62
-
63
- Gatekeeper doesn't care which DB you use. You just need to implement the `AuthAdapter` interface:
64
-
65
- ```typescript
66
- import type { AuthAdapter, User } from "kroxt/adapter";
67
-
68
- export const myAdapter: AuthAdapter = {
69
- createUser: async (data) => { /* logic */ },
70
- findUserByEmail: async (email) => { /* logic */ },
71
- findUserById: async (id) => { /* logic */ },
72
- linkOAuthAccount: async (user, provider, provId) => { /* logic */ }
73
- };
74
- ```
75
-
76
- ## Reference Project
77
-
78
- Check out the `test-project` folder for a complete **Express + MongoDB** implementation using this library.
79
-
80
- ## License
81
-
82
- ISC
1
+ # kroxt
2
+
3
+ A framework-agnostic, modular authentication engine for modern TypeScript applications. Built for security, extensibility, and ease of use.
4
+
5
+ ## Features
6
+
7
+ - 🔐 **Secure Hashing**: Powered by `argon2` for industry-standard password security.
8
+ - 🎟️ **Dual-Token Sessions**: Native support for Access and Refresh tokens via `jose`.
9
+ - 🧩 **JWT Customization**: Fully extensible payload with support for custom user fields and `sub` override.
10
+ - 🌍 **OAuth Ready**: Built-in support for GitHub and Google OAuth via `arctic`.
11
+ - 🧩 **Database Agnostic**: Use Mongoose, Prisma, Drizzle, or any store via the `AuthAdapter` pattern.
12
+ - 🌶️ **Password Peppering**: Server-side pepper support for enhanced hash protection.
13
+ - 🛡️ **Timing Attack Protection**: Built-in safeguards against side-channel analysis during login.
14
+ - ✅ **Zod Schema Support**: Perfectly preserves and types your user metadata.
15
+ - 🚀 **ESM First**: Native support for NodeNext module resolution.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install kroxt
21
+ ```
22
+
23
+ ---
24
+
25
+ ## Guide: Full Authentication Flow
26
+
27
+ This guide walks you through setting up Kroxt from scratch in your application.
28
+
29
+ ### Step 1: Define your User
30
+
31
+ First, define what a User looks like in your system. Kroxt allows any additional fields (like `role`, `schoolId`, etc.) which you can later sign into your JWTs.
32
+
33
+ ```typescript
34
+ export interface MyUser {
35
+ id: string;
36
+ email: string;
37
+ passwordHash: string;
38
+ role: 'admin' | 'user';
39
+ schoolId: string; // Custom field for enterprise/multi-tenant apps
40
+ oauthProvider?: string; // Support for OAuth (e.g., 'github')
41
+ oauthId?: string; // Unique ID from the provider
42
+ name: string;
43
+ }
44
+ ```
45
+
46
+ ### Step 2: The Adapter Pattern
47
+
48
+ Kroxt doesn't care which database you use. You just need to implement the `AuthAdapter` interface using your model. Here is a complete example using Mongoose:
49
+
50
+ ```typescript
51
+ import type { AuthAdapter } from "kroxt/adapter";
52
+ import { User } from "./models/user.model.js"; // Your Mongoose model
53
+ import type { MyUser } from "./types.js";
54
+
55
+ export const myAdapter: AuthAdapter<MyUser> = {
56
+ createUser: async (data) => {
57
+ const user = await User.create(data);
58
+ const obj = user.toObject();
59
+ return { ...obj, id: obj._id.toString() };
60
+ },
61
+ findUserByEmail: async (email) => {
62
+ const user = await User.findOne({ email });
63
+ if (!user) return null;
64
+ const obj = user.toObject();
65
+ return { ...obj, id: obj._id.toString() };
66
+ },
67
+ findUserById: async (id) => {
68
+ const user = await User.findById(id);
69
+ if (!user) return null;
70
+ const obj = user.toObject();
71
+ return { ...obj, id: obj._id.toString() };
72
+ },
73
+ linkOAuthAccount: async (userId, provider, providerId) => {
74
+ await User.findByIdAndUpdate(userId, {
75
+ oauthProvider: provider,
76
+ oauthId: providerId
77
+ });
78
+ }
79
+ };
80
+ ```
81
+
82
+ ### Step 3: Initialize the Auth Engine
83
+
84
+ Configure Kroxt with your adapter and security settings.
85
+
86
+ ```typescript
87
+ import { createAuth } from "kroxt";
88
+ import { myAdapter } from "./myAdapter.js";
89
+
90
+ export const auth = createAuth({
91
+ adapter: myAdapter,
92
+ secret: process.env.AUTH_SECRET, // High-entropy secret for JWT signing
93
+ pepper: process.env.AUTH_PEPPER, // Optional: Server-side pepper for password hashing
94
+ session: {
95
+ expires: "15m", // Access token duration
96
+ refreshExpires: "7d" // Refresh token duration
97
+ },
98
+ jwt: {
99
+ /**
100
+ * Optional: Fully customize the JWT payload or add extra fields.
101
+ */
102
+ payload: (user, type) => {
103
+ // Only add extra details to 'access' tokens to keep 'refresh' tokens light.
104
+ if (type === "access") {
105
+ return {
106
+ schoolId: user.schoolId, // Add custom user detail
107
+ role: user.role, // Explicitly include role
108
+ };
109
+ }
110
+ return {}; // Refresh tokens stay minimal
111
+ }
112
+ }
113
+ });
114
+ ```
115
+
116
+ ### Step 4: Implement Controllers & Routes
117
+
118
+ Use the engine in your application logic. Examples below use an Express-like structure.
119
+
120
+ #### Registration
121
+ ```typescript
122
+ app.post("/register", async (req, res) => {
123
+ const { name, email, password, ...extraFields } = req.body;
124
+
125
+ // Kroxt handles argon2 hashing (with pepper) and token generation
126
+ const { user, accessToken, refreshToken } = await auth.signup({
127
+ name,
128
+ email,
129
+ ...extraFields
130
+ }, password);
131
+
132
+ res.json({ user, accessToken, refreshToken });
133
+ });
134
+ ```
135
+
136
+ #### Login
137
+ ```typescript
138
+ app.post("/login", async (req, res) => {
139
+ const { email, password } = req.body;
140
+
141
+ // Kroxt verifies password (timing-attack safe) and returns tokens
142
+ const { user, accessToken, refreshToken } = await auth.loginWithPassword(email, password);
143
+
144
+ res.json({ user, accessToken, refreshToken });
145
+ });
146
+ ```
147
+
148
+ #### Token Refresh
149
+ Keep users logged in by rotating access tokens using a valid refresh token.
150
+ ```typescript
151
+ app.post("/refresh", async (req, res) => {
152
+ const { refreshToken } = req.body;
153
+
154
+ // Returns a fresh access token
155
+ const { accessToken } = await auth.refresh(refreshToken);
156
+
157
+ res.json({ accessToken });
158
+ });
159
+ ```
160
+
161
+ #### Protecting Routes (Middleware)
162
+ ```typescript
163
+ app.get("/me", async (req, res) => {
164
+ const token = req.headers.authorization?.split(" ")[1];
165
+
166
+ // Verify the JWT and get the payload { sub: string, role: string, ... }
167
+ const payload = await auth.verifyToken(token, "access");
168
+
169
+ if (!payload) return res.status(401).send("Unauthorized");
170
+
171
+ const user = await myAdapter.findUserById(payload.sub);
172
+ res.json(user);
173
+ });
174
+ ```
175
+
176
+ ---
177
+
178
+ ## Security Best Practices
179
+
180
+ ### 1. Password Peppering
181
+ Always use a `pepper` in production. It's a server-side secret added to passwords before hashing. If your database is leaked, the hashes cannot be cracked without this pepper.
182
+
183
+ ### 2. CSRF Protection
184
+ Kroxt provides helpers for the double-submit cookie pattern. Use these if you are storing tokens in cookies.
185
+
186
+ ```typescript
187
+ import { generateCsrfToken, verifyCsrf } from "kroxt/security";
188
+
189
+ const token = generateCsrfToken();
190
+ const isValid = verifyCsrf(tokenInRequest, tokenInCookie);
191
+ ```
192
+
193
+ ### 3. Secure Cookies
194
+ If using cookies, always set these flags:
195
+ - `httpOnly: true` (Prevents XSS)
196
+ - `secure: true` (Requires HTTPS)
197
+ - `sameSite: 'strict'` (Prevents CSRF)
198
+
199
+ ### 4. Rate Limiting
200
+ Implement rate limiting (e.g., `express-rate-limit`) on `/login` and `/register` to block brute-force attempts.
201
+
202
+ ---
203
+
204
+ ## Reference Project
205
+
206
+ Check out the `kroxt-example` folder or the [GitHub repository](https://github.com/adepoju-oluwatobi/kroxt-example) for a complete **Express + MongoDB** implementation using this library.
207
+
208
+ ## License
209
+
210
+ MIT
@@ -9,6 +9,15 @@ export interface CreateAuthOptions {
9
9
  refreshExpires?: string | number;
10
10
  };
11
11
  providers?: Provider[];
12
+ jwt?: {
13
+ /**
14
+ * A callback to add custom fields to the JWT payload.
15
+ * It receives the user object and the token type ('access' or 'refresh').
16
+ * Return an object containing the fields to be merged into the payload.
17
+ * You can also override default fields like 'sub'.
18
+ */
19
+ payload?: (user: User<any>, type: "access" | "refresh") => Record<string, any>;
20
+ };
12
21
  }
13
22
  export declare function createAuth(options: CreateAuthOptions): {
14
23
  signup: (userData: Omit<User<any>, "id">, password?: string) => Promise<{
package/dist-lib/core.js CHANGED
@@ -10,7 +10,12 @@ export function createAuth(options) {
10
10
  * Generates a stateless JWT for a user session
11
11
  */
12
12
  async function generateToken(user, type = "access") {
13
- return new SignJWT({ sub: user.id, role: user.role, type })
13
+ let payload = { sub: user.id, role: user.role, type };
14
+ if (options.jwt?.payload) {
15
+ const customPayload = options.jwt.payload(user, type);
16
+ payload = { ...payload, ...customPayload };
17
+ }
18
+ return new SignJWT(payload)
14
19
  .setProtectedHeader({ alg: "HS256" })
15
20
  .setIssuedAt()
16
21
  .setExpirationTime(type === "access" ? expiration : refreshExpiration)
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "kroxt",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
+ "license": "MIT",
4
5
  "description": "A framework-agnostic modular auth engine",
5
6
  "type": "module",
6
7
  "main": "./dist-lib/index.js",