@cauth/express 0.0.4 → 0.1.2
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 +77 -100
- package/dist/index.d.ts +76 -27
- package/dist/index.js +10 -11
- package/package.json +1 -1
- package/dist/express-BgF1jv36.d.ts +0 -12
package/README.md
CHANGED
|
@@ -1,140 +1,117 @@
|
|
|
1
1
|
# @cauth/express
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@cauth/express)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**CAuth Express** provides seamless integration between the CAuth core authentication system and Express.js applications. It includes type-safe route handlers, middleware, and request augmentation.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- **Authentication Middleware**: Ready-to-use authentication guard
|
|
10
|
-
- **Request Augmentation**: Typed user data in request object
|
|
11
|
-
- **Error Handling**: Express-compatible error handling
|
|
7
|
+
> [!IMPORTANT]
|
|
8
|
+
> For more information and full documentation, visit **[cauth.dev](https://cauth.dev)**.
|
|
12
9
|
|
|
13
|
-
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## ✨ Features
|
|
13
|
+
|
|
14
|
+
- **🚀 Express Optimized**: Plug-and-play middleware and route handlers.
|
|
15
|
+
- **🛡️ Type-Safe Guard**: Protect routes with RBAC that knows your roles.
|
|
16
|
+
- **📦 Request Augmentation**: Automatically injects `req.cauth` with user session data.
|
|
17
|
+
- **🧩 Flexible**: Use pre-built routes or call core `FN` functions manually.
|
|
18
|
+
- **🛡️ Standardized Errors**: Automatically maps core errors to appropriate HTTP status codes.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 🚀 Installation
|
|
14
23
|
|
|
15
24
|
```bash
|
|
16
25
|
npm install @cauth/express @cauth/core
|
|
17
26
|
# or
|
|
18
27
|
yarn add @cauth/express @cauth/core
|
|
19
|
-
# or
|
|
20
|
-
pnpm add @cauth/express @cauth/core
|
|
21
28
|
```
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 🏁 Quick Start
|
|
33
|
+
|
|
34
|
+
1. Initialize your CAuth client (see `@cauth/core` for full config).
|
|
35
|
+
2. Attach the generated routes and middleware to your Express app.
|
|
24
36
|
|
|
25
37
|
```typescript
|
|
26
38
|
import express from 'express';
|
|
27
|
-
import
|
|
28
|
-
import { ExpressContractor, Guard } from '@cauth/express';
|
|
29
|
-
import { PrismaContractor } from '@cauth/prisma';
|
|
39
|
+
import auth from './auth'; // Your initialized CAuth instance
|
|
30
40
|
|
|
31
41
|
const app = express();
|
|
32
42
|
app.use(express.json());
|
|
33
43
|
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
jwtConfig: {
|
|
40
|
-
accessTokenSecret: process.env.ACCESS_TOKEN_SECRET!,
|
|
41
|
-
refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET!,
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
// authentication routes
|
|
46
|
-
app.post('/register', CAuthClient.Routes.Register())
|
|
47
|
-
|
|
48
|
-
app.post('/login', CAuthClient.Routes.Login())
|
|
49
|
-
|
|
50
|
-
// Using the Guard to extract the id from the user's request
|
|
51
|
-
app.post('/change-password', CAuthClient.Guard(), (req: Request, res: Response) => CAuth.Guard(), CAuthClient.Routes.ChangePassword(req.cauth?.id!)(req, res))
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
app.post('/refresh', CAuthClient.Routes.Refresh())
|
|
55
|
-
|
|
56
|
-
app.post('/logout', CAuthClient.Routes.Logout())
|
|
57
|
-
|
|
58
|
-
app.post('/login-with-code', async (req: Request, res: Response) => {
|
|
59
|
-
const result = await CAuthClient.FN.LoginWithOTP({ phoneNumber: req.body.phone, code: req.body.code })
|
|
44
|
+
// 1. Mount pre-built authentication routes
|
|
45
|
+
app.post('/auth/register', auth.Routes.Register());
|
|
46
|
+
app.post('/auth/login', auth.Routes.Login());
|
|
47
|
+
app.post('/auth/refresh', auth.Routes.Refresh());
|
|
48
|
+
app.post('/auth/logout', auth.Routes.Logout());
|
|
60
49
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// Protected route example
|
|
66
|
-
app.get('/protected', CAuthClient.Guard(), (req, res) => {
|
|
67
|
-
// User data is available in req.user
|
|
68
|
-
res.json({ message: 'Protected data', user: req.user });
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// Role-based protection
|
|
72
|
-
app.get('/admin', Guard(['ADMIN']), (req, res) => {
|
|
73
|
-
res.json({ message: 'Admin only', user: req.user });
|
|
50
|
+
// 2. Protect routes with the Guard middleware
|
|
51
|
+
app.get('/me', auth.Guard(), (req, res) => {
|
|
52
|
+
// Access typed user data from req.cauth
|
|
53
|
+
res.json({ user: req.cauth });
|
|
74
54
|
});
|
|
75
55
|
|
|
76
|
-
|
|
77
|
-
|
|
56
|
+
// 3. Role-based protection
|
|
57
|
+
app.get('/admin', auth.Guard(['ADMIN']), (req, res) => {
|
|
58
|
+
res.json({ message: 'Welcome, Admin!' });
|
|
78
59
|
});
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## API Reference
|
|
82
|
-
|
|
83
60
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
61
|
+
// 4. Manual usage in custom routes
|
|
62
|
+
app.post('/auth/reset-password', async (req, res) => {
|
|
63
|
+
const result = await auth.FN.RequestOTPCode({
|
|
64
|
+
email: req.body.email,
|
|
65
|
+
otpPurpose: 'RESET_PASSWORD',
|
|
66
|
+
onCode: (code) => {
|
|
67
|
+
// Logic to send code via email
|
|
68
|
+
console.log(`OTP Code: ${code}`);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
res.status(result.success ? 200 : 400).send(result);
|
|
93
73
|
});
|
|
94
74
|
|
|
95
|
-
|
|
96
|
-
app.get('/admin', Guard(['ADMIN']), (req, res) => {
|
|
97
|
-
res.json({ message: 'Admin access granted' });
|
|
98
|
-
});
|
|
75
|
+
app.listen(3000);
|
|
99
76
|
```
|
|
100
77
|
|
|
101
|
-
|
|
78
|
+
---
|
|
102
79
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
```typescript
|
|
106
|
-
interface AuthenticatedRequest extends Request {
|
|
107
|
-
cauth: {
|
|
108
|
-
id: string;
|
|
109
|
-
role: string;
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
```
|
|
80
|
+
## 📖 API Reference
|
|
113
81
|
|
|
114
|
-
###
|
|
82
|
+
### `auth.Guard(roles?: string[])`
|
|
83
|
+
A middleware that verifies the Access Token in the `Authorization` header (`Bearer <token>`).
|
|
115
84
|
|
|
116
|
-
|
|
85
|
+
- If no roles are provided, it only checks for a valid session.
|
|
86
|
+
- If roles are provided, it checks if the user has one of the specified roles.
|
|
87
|
+
- Injects `req.cauth` with `{ id: string, role: string }`.
|
|
117
88
|
|
|
118
|
-
|
|
119
|
-
-
|
|
120
|
-
- 403: Forbidden (insufficient permissions)
|
|
121
|
-
- 404: Account not found
|
|
122
|
-
- 409: Duplicate account
|
|
123
|
-
- 422: Invalid OTP code
|
|
89
|
+
### `auth.Routes`
|
|
90
|
+
A collection of pre-configured Express route handlers:
|
|
124
91
|
|
|
125
|
-
|
|
92
|
+
- **Register**: `POST` handler for user creation.
|
|
93
|
+
- **Login**: `POST` handler for credentials-based auth.
|
|
94
|
+
- **Logout**: `POST` handler that revokes refresh tokens.
|
|
95
|
+
- **Refresh**: `POST` handler for rotating access tokens.
|
|
96
|
+
- **ChangePassword**: `POST` handler for updating passwords (requires `userId`).
|
|
126
97
|
|
|
127
|
-
|
|
98
|
+
---
|
|
128
99
|
|
|
129
|
-
|
|
130
|
-
- TypeScript >= 5.9
|
|
131
|
-
- Express.js >= 4.18
|
|
100
|
+
## 🔒 Error Mapping
|
|
132
101
|
|
|
102
|
+
CAuth Express automatically maps core errors to HTTP status codes:
|
|
133
103
|
|
|
134
|
-
|
|
104
|
+
| Core Error | HTTP Status |
|
|
105
|
+
| :--- | :--- |
|
|
106
|
+
| `CredentialMismatchError` | 401 Unauthorized |
|
|
107
|
+
| `InvalidDataError` | 400 Bad Request |
|
|
108
|
+
| `AccountNotFoundError` | 404 Not Found |
|
|
109
|
+
| `InvalidRoleError` | 403 Forbidden |
|
|
110
|
+
| `DuplicateAccountError` | 409 Conflict |
|
|
111
|
+
| `InvalidOTPCode` | 422 Unprocessable Entity |
|
|
135
112
|
|
|
136
|
-
|
|
113
|
+
---
|
|
137
114
|
|
|
138
|
-
##
|
|
115
|
+
## 📄 License
|
|
139
116
|
|
|
140
|
-
|
|
117
|
+
MIT © [Jonace Mpelule](https://github.com/jonace-mpelule)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import "./express-BgF1jv36.js";
|
|
2
1
|
import { NextFunction, Request, Response } from "express";
|
|
3
2
|
import z$1, { z } from "zod";
|
|
4
3
|
import ms from "ms";
|
|
@@ -24,23 +23,33 @@ declare const AuthModelSchema: z$1.ZodObject<{
|
|
|
24
23
|
passwordHash: z$1.ZodOptional<z$1.ZodString>;
|
|
25
24
|
role: z$1.ZodString;
|
|
26
25
|
lastLogin: z$1.ZodDate;
|
|
27
|
-
refreshTokens: z$1.
|
|
26
|
+
refreshTokens: z$1.ZodArray<z$1.ZodObject<{
|
|
27
|
+
token: z$1.ZodString;
|
|
28
|
+
exp: z$1.ZodCoercedNumber<unknown>;
|
|
29
|
+
}, z$1.core.$strip>>;
|
|
28
30
|
createdAt: z$1.ZodDate;
|
|
29
31
|
updatedAt: z$1.ZodDate;
|
|
30
|
-
}, z$1.
|
|
32
|
+
}, z$1.core.$strip>;
|
|
31
33
|
type AuthModel = z$1.infer<typeof AuthModelSchema>;
|
|
32
34
|
//#endregion
|
|
35
|
+
//#region ../core/src/errors/errors.d.ts
|
|
36
|
+
type CAuthErrorShape = {
|
|
37
|
+
type: string;
|
|
38
|
+
message: string;
|
|
39
|
+
code: string | number;
|
|
40
|
+
name: string;
|
|
41
|
+
};
|
|
42
|
+
//#endregion
|
|
33
43
|
//#region ../core/src/types/result.t.d.ts
|
|
34
44
|
type FNError = {
|
|
35
|
-
|
|
36
|
-
error: Error;
|
|
45
|
+
error: CAuthErrorShape;
|
|
37
46
|
};
|
|
38
47
|
/**
|
|
39
48
|
* @description Core Result type.
|
|
40
49
|
* @template T - The type of the value.
|
|
41
50
|
* @template E - The type of the errors, which must extend { type: string; error: Error }.
|
|
42
51
|
*/
|
|
43
|
-
type Result
|
|
52
|
+
type Result<T, E extends FNError = FNError> = {
|
|
44
53
|
success: true;
|
|
45
54
|
value: T;
|
|
46
55
|
} | {
|
|
@@ -85,6 +94,7 @@ interface DatabaseContract {
|
|
|
85
94
|
id: string;
|
|
86
95
|
refreshToken: string;
|
|
87
96
|
select?: any;
|
|
97
|
+
config: CAuthOptions;
|
|
88
98
|
}): Promise<T>;
|
|
89
99
|
removeAndAddRefreshToken({
|
|
90
100
|
...args
|
|
@@ -134,30 +144,62 @@ declare const CAuthOptionsSchema: z$1.ZodObject<{
|
|
|
134
144
|
accessTokenSecret: z$1.ZodString;
|
|
135
145
|
accessTokenLifeSpan: z$1.ZodOptional<z$1.ZodCustom<ms.StringValue, ms.StringValue>>;
|
|
136
146
|
refreshTokenLifeSpan: z$1.ZodOptional<z$1.ZodCustom<ms.StringValue, ms.StringValue>>;
|
|
137
|
-
}, z$1.
|
|
147
|
+
}, z$1.core.$strip>;
|
|
138
148
|
otpConfig: z$1.ZodObject<{
|
|
139
149
|
expiresIn: z$1.ZodOptional<z$1.ZodNumber>;
|
|
140
150
|
length: z$1.ZodOptional<z$1.ZodNumber>;
|
|
141
|
-
}, z$1.
|
|
142
|
-
}, z$1.
|
|
151
|
+
}, z$1.core.$strip>;
|
|
152
|
+
}, z$1.core.$strip>;
|
|
143
153
|
type CAuthOptions = z$1.infer<typeof CAuthOptionsSchema>;
|
|
144
154
|
//#endregion
|
|
145
155
|
//#region ../core/src/types/dto-schemas.t.d.ts
|
|
146
156
|
declare const LoginSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
147
157
|
email: z.ZodEmail;
|
|
148
158
|
phoneNumber: z.ZodOptional<z.ZodNever>;
|
|
149
|
-
password: z.ZodString
|
|
159
|
+
password: z.ZodOptional<z.ZodString>;
|
|
150
160
|
}, z.core.$strip>, z.ZodObject<{
|
|
151
161
|
phoneNumber: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
152
162
|
email: z.ZodOptional<z.ZodNever>;
|
|
153
|
-
password: z.ZodString
|
|
163
|
+
password: z.ZodOptional<z.ZodString>;
|
|
154
164
|
}, z.core.$strip>]>;
|
|
155
165
|
type LoginSchemaType = z.infer<typeof LoginSchema>;
|
|
166
|
+
declare const OTPCodeUnion: z.ZodUnion<readonly [z.ZodObject<{
|
|
167
|
+
email: z.ZodEmail;
|
|
168
|
+
phoneNumber: z.ZodOptional<z.ZodNever>;
|
|
169
|
+
code: z.ZodString;
|
|
170
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
171
|
+
phoneNumber: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
172
|
+
email: z.ZodOptional<z.ZodNever>;
|
|
173
|
+
code: z.ZodString;
|
|
174
|
+
}, z.core.$strip>]>;
|
|
175
|
+
type OTPLogin = z.infer<typeof OTPCodeUnion>;
|
|
176
|
+
declare const RequestOTPCodeSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
177
|
+
otpPurpose: z.ZodEnum<{
|
|
178
|
+
LOGIN: "LOGIN";
|
|
179
|
+
RESET_PASSWORD: "RESET_PASSWORD";
|
|
180
|
+
ACTION: "ACTION";
|
|
181
|
+
}>;
|
|
182
|
+
usePassword: z.ZodDefault<z.ZodBoolean>;
|
|
183
|
+
password: z.ZodOptional<z.ZodString>;
|
|
184
|
+
phoneNumber: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
185
|
+
email: z.ZodOptional<z.ZodNever>;
|
|
186
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
187
|
+
otpPurpose: z.ZodEnum<{
|
|
188
|
+
LOGIN: "LOGIN";
|
|
189
|
+
RESET_PASSWORD: "RESET_PASSWORD";
|
|
190
|
+
ACTION: "ACTION";
|
|
191
|
+
}>;
|
|
192
|
+
usePassword: z.ZodDefault<z.ZodBoolean>;
|
|
193
|
+
password: z.ZodOptional<z.ZodString>;
|
|
194
|
+
phoneNumber: z.ZodOptional<z.ZodNever>;
|
|
195
|
+
email: z.ZodString;
|
|
196
|
+
}, z.core.$strip>]>;
|
|
197
|
+
type RequestOTP = z.infer<typeof RequestOTPCodeSchema>;
|
|
156
198
|
declare const RegisterSchema: z.ZodObject<{
|
|
157
199
|
phoneNumber: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
158
200
|
email: z.ZodOptional<z.ZodEmail>;
|
|
159
201
|
role: z.ZodString;
|
|
160
|
-
password: z.ZodString
|
|
202
|
+
password: z.ZodOptional<z.ZodString>;
|
|
161
203
|
}, z.core.$strip>;
|
|
162
204
|
type RegisterSchemaType = z.infer<typeof RegisterSchema>;
|
|
163
205
|
declare const RefreshTokenSchema: z.ZodObject<{
|
|
@@ -199,31 +241,38 @@ declare class _CAuth<T extends string[], TContractor extends RoutesContract<any>
|
|
|
199
241
|
ChangePassword: (userId: string) => ReturnType<TContractor["ChangePassword"]>;
|
|
200
242
|
};
|
|
201
243
|
FN: {
|
|
202
|
-
Login: (
|
|
244
|
+
Login: ({
|
|
245
|
+
...args
|
|
246
|
+
}: LoginSchemaType) => Promise<Result<{
|
|
203
247
|
account: Account;
|
|
204
248
|
tokens: Tokens;
|
|
205
249
|
}>>;
|
|
206
|
-
Register: (
|
|
250
|
+
Register: ({
|
|
251
|
+
...args
|
|
252
|
+
}: RegisterSchemaType) => Promise<Result<{
|
|
207
253
|
account: Account;
|
|
208
254
|
tokens: Tokens;
|
|
209
255
|
}>>;
|
|
210
|
-
Logout: (
|
|
211
|
-
|
|
256
|
+
Logout: ({
|
|
257
|
+
...args
|
|
258
|
+
}: LogoutSchemaType) => Promise<Result<unknown>>;
|
|
259
|
+
Refresh: ({
|
|
260
|
+
...args
|
|
261
|
+
}: RefreshTokenSchemaType) => Promise<Result<{
|
|
212
262
|
account: Account;
|
|
213
263
|
tokens: Tokens;
|
|
214
264
|
}>>;
|
|
215
|
-
ChangePassword: (
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
265
|
+
ChangePassword: ({
|
|
266
|
+
...args
|
|
267
|
+
}: ChangePasswordSchemaType) => Promise<Result<unknown>>;
|
|
268
|
+
RequestOTPCode: ({
|
|
269
|
+
...args
|
|
270
|
+
}: RequestOTP & {
|
|
271
|
+
onCode: (code: string) => any;
|
|
220
272
|
}) => Promise<Result<{
|
|
221
273
|
id: string;
|
|
222
|
-
code: string;
|
|
223
274
|
}>>;
|
|
224
|
-
LoginWithOTP: (args:
|
|
225
|
-
code: string;
|
|
226
|
-
}) => Promise<Result<{
|
|
275
|
+
LoginWithOTP: (args: OTPLogin) => Promise<Result<{
|
|
227
276
|
account: Account;
|
|
228
277
|
tokens: Tokens;
|
|
229
278
|
}>>;
|
|
@@ -231,9 +280,9 @@ declare class _CAuth<T extends string[], TContractor extends RoutesContract<any>
|
|
|
231
280
|
id: string;
|
|
232
281
|
code: string;
|
|
233
282
|
otpPurpose: OtpPurpose;
|
|
234
|
-
}) => Promise<{
|
|
283
|
+
}) => Promise<Result<{
|
|
235
284
|
isValid: boolean;
|
|
236
|
-
}
|
|
285
|
+
}>>;
|
|
237
286
|
};
|
|
238
287
|
Tokens: {
|
|
239
288
|
GenerateRefreshToken: (payload: any) => Promise<string>;
|