@cauth/express 0.0.5 → 0.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 +77 -100
- package/dist/index.d.ts +56 -18
- package/dist/index.js +10 -11
- package/package.json +2 -4
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
|
@@ -23,16 +23,26 @@ declare const AuthModelSchema: z$1.ZodObject<{
|
|
|
23
23
|
passwordHash: z$1.ZodOptional<z$1.ZodString>;
|
|
24
24
|
role: z$1.ZodString;
|
|
25
25
|
lastLogin: z$1.ZodDate;
|
|
26
|
-
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>>;
|
|
27
30
|
createdAt: z$1.ZodDate;
|
|
28
31
|
updatedAt: z$1.ZodDate;
|
|
29
|
-
}, z$1.
|
|
32
|
+
}, z$1.core.$strip>;
|
|
30
33
|
type AuthModel = z$1.infer<typeof AuthModelSchema>;
|
|
31
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
|
|
32
43
|
//#region ../core/src/types/result.t.d.ts
|
|
33
44
|
type FNError = {
|
|
34
|
-
|
|
35
|
-
error: Error;
|
|
45
|
+
error: CAuthErrorShape;
|
|
36
46
|
};
|
|
37
47
|
/**
|
|
38
48
|
* @description Core Result type.
|
|
@@ -84,6 +94,7 @@ interface DatabaseContract {
|
|
|
84
94
|
id: string;
|
|
85
95
|
refreshToken: string;
|
|
86
96
|
select?: any;
|
|
97
|
+
config: CAuthOptions;
|
|
87
98
|
}): Promise<T>;
|
|
88
99
|
removeAndAddRefreshToken({
|
|
89
100
|
...args
|
|
@@ -133,30 +144,62 @@ declare const CAuthOptionsSchema: z$1.ZodObject<{
|
|
|
133
144
|
accessTokenSecret: z$1.ZodString;
|
|
134
145
|
accessTokenLifeSpan: z$1.ZodOptional<z$1.ZodCustom<ms.StringValue, ms.StringValue>>;
|
|
135
146
|
refreshTokenLifeSpan: z$1.ZodOptional<z$1.ZodCustom<ms.StringValue, ms.StringValue>>;
|
|
136
|
-
}, z$1.
|
|
147
|
+
}, z$1.core.$strip>;
|
|
137
148
|
otpConfig: z$1.ZodObject<{
|
|
138
149
|
expiresIn: z$1.ZodOptional<z$1.ZodNumber>;
|
|
139
150
|
length: z$1.ZodOptional<z$1.ZodNumber>;
|
|
140
|
-
}, z$1.
|
|
141
|
-
}, z$1.
|
|
151
|
+
}, z$1.core.$strip>;
|
|
152
|
+
}, z$1.core.$strip>;
|
|
142
153
|
type CAuthOptions = z$1.infer<typeof CAuthOptionsSchema>;
|
|
143
154
|
//#endregion
|
|
144
155
|
//#region ../core/src/types/dto-schemas.t.d.ts
|
|
145
156
|
declare const LoginSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
146
157
|
email: z.ZodEmail;
|
|
147
158
|
phoneNumber: z.ZodOptional<z.ZodNever>;
|
|
148
|
-
password: z.ZodString
|
|
159
|
+
password: z.ZodOptional<z.ZodString>;
|
|
149
160
|
}, z.core.$strip>, z.ZodObject<{
|
|
150
161
|
phoneNumber: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
151
162
|
email: z.ZodOptional<z.ZodNever>;
|
|
152
|
-
password: z.ZodString
|
|
163
|
+
password: z.ZodOptional<z.ZodString>;
|
|
153
164
|
}, z.core.$strip>]>;
|
|
154
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>;
|
|
155
198
|
declare const RegisterSchema: z.ZodObject<{
|
|
156
199
|
phoneNumber: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
157
200
|
email: z.ZodOptional<z.ZodEmail>;
|
|
158
201
|
role: z.ZodString;
|
|
159
|
-
password: z.ZodString
|
|
202
|
+
password: z.ZodOptional<z.ZodString>;
|
|
160
203
|
}, z.core.$strip>;
|
|
161
204
|
type RegisterSchemaType = z.infer<typeof RegisterSchema>;
|
|
162
205
|
declare const RefreshTokenSchema: z.ZodObject<{
|
|
@@ -224,17 +267,12 @@ declare class _CAuth<T extends string[], TContractor extends RoutesContract<any>
|
|
|
224
267
|
}: ChangePasswordSchemaType) => Promise<Result<unknown>>;
|
|
225
268
|
RequestOTPCode: ({
|
|
226
269
|
...args
|
|
227
|
-
}:
|
|
228
|
-
|
|
229
|
-
usePassword?: boolean;
|
|
230
|
-
otpPurpose: OtpPurpose;
|
|
270
|
+
}: RequestOTP & {
|
|
271
|
+
onCode: (code: string) => any;
|
|
231
272
|
}) => Promise<Result<{
|
|
232
273
|
id: string;
|
|
233
|
-
code: string;
|
|
234
274
|
}>>;
|
|
235
|
-
LoginWithOTP: (args:
|
|
236
|
-
code: string;
|
|
237
|
-
}) => Promise<Result<{
|
|
275
|
+
LoginWithOTP: (args: OTPLogin) => Promise<Result<{
|
|
238
276
|
account: Account;
|
|
239
277
|
tokens: Tokens;
|
|
240
278
|
}>>;
|