kroxt 1.1.2 → 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/README.md CHANGED
@@ -6,6 +6,7 @@ A framework-agnostic, modular authentication engine for modern TypeScript applic
6
6
 
7
7
  - 🔐 **Secure Hashing**: Powered by `argon2` for industry-standard password security.
8
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.
9
10
  - 🌍 **OAuth Ready**: Built-in support for GitHub and Google OAuth via `arctic`.
10
11
  - 🧩 **Database Agnostic**: Use Mongoose, Prisma, Drizzle, or any store via the `AuthAdapter` pattern.
11
12
  - 🌶️ **Password Peppering**: Server-side pepper support for enhanced hash protection.
@@ -25,35 +26,60 @@ npm install kroxt
25
26
 
26
27
  This guide walks you through setting up Kroxt from scratch in your application.
27
28
 
28
- ### Step 1: The Adapter Pattern
29
+ ### Step 1: Define your User
29
30
 
30
- Kroxt doesn't care which database you use. You just need to implement the `AuthAdapter` interface.
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.
31
32
 
32
- In this example, we use a simple user structure: `name`, `email`, and `password`.
33
- > [!NOTE]
34
- > Kroxt's adapter can accept **any** additional fields your application requires (e.g., `role`, `avatar`, `preferences`) with no limits.
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:
35
49
 
36
50
  ```typescript
37
- import type { AuthAdapter, User } from "kroxt/adapter";
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";
38
54
 
39
- export const myAdapter: AuthAdapter = {
55
+ export const myAdapter: AuthAdapter<MyUser> = {
40
56
  createUser: async (data) => {
41
- // Save to your DB: { name, email, passwordHash, ...anyOtherFields }
42
- // return the created user including its unique id
57
+ const user = await User.create(data);
58
+ const obj = user.toObject();
59
+ return { ...obj, id: obj._id.toString() };
43
60
  },
44
61
  findUserByEmail: async (email) => {
45
- // Find user by email in your DB
62
+ const user = await User.findOne({ email });
63
+ if (!user) return null;
64
+ const obj = user.toObject();
65
+ return { ...obj, id: obj._id.toString() };
46
66
  },
47
67
  findUserById: async (id) => {
48
- // Find user by ID in your DB
68
+ const user = await User.findById(id);
69
+ if (!user) return null;
70
+ const obj = user.toObject();
71
+ return { ...obj, id: obj._id.toString() };
49
72
  },
50
- linkOAuthAccount: async (user, provider, providerId) => {
51
- // Link an OAuth provider to an existing user
73
+ linkOAuthAccount: async (userId, provider, providerId) => {
74
+ await User.findByIdAndUpdate(userId, {
75
+ oauthProvider: provider,
76
+ oauthId: providerId
77
+ });
52
78
  }
53
79
  };
54
80
  ```
55
81
 
56
- ### Step 2: Initialize the Auth Engine
82
+ ### Step 3: Initialize the Auth Engine
57
83
 
58
84
  Configure Kroxt with your adapter and security settings.
59
85
 
@@ -68,11 +94,26 @@ export const auth = createAuth({
68
94
  session: {
69
95
  expires: "15m", // Access token duration
70
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
+ }
71
112
  }
72
113
  });
73
114
  ```
74
115
 
75
- ### Step 3: Implement Controllers & Routes
116
+ ### Step 4: Implement Controllers & Routes
76
117
 
77
118
  Use the engine in your application logic. Examples below use an Express-like structure.
78
119
 
@@ -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,6 @@
1
1
  {
2
2
  "name": "kroxt",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "license": "MIT",
5
5
  "description": "A framework-agnostic modular auth engine",
6
6
  "type": "module",