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 +56 -15
- package/dist-lib/core.d.ts +9 -0
- package/dist-lib/core.js +6 -1
- package/package.json +1 -1
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:
|
|
29
|
+
### Step 1: Define your User
|
|
29
30
|
|
|
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.
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
|
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
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
51
|
-
|
|
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
|
|
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
|
|
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
|
|
package/dist-lib/core.d.ts
CHANGED
|
@@ -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
|
-
|
|
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)
|