@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 CHANGED
@@ -1,140 +1,117 @@
1
1
  # @cauth/express
2
2
 
3
- Express integration for CAuth authentication system.
3
+ [![NPM Version](https://img.shields.io/npm/v/@cauth/express.svg)](https://www.npmjs.com/package/@cauth/express)
4
4
 
5
- ## Features
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
- - **Express Integration**: Seamless integration with Express.js applications
8
- - **Type-Safe Routes**: TypeScript support for route handlers
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
- ## Installation
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
- ## Quick Start
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 { CAuth } from '@cauth/core';
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
- // Initialize CAuth with Express contractor
35
- const CAuthClient = CAuth({
36
- dbContractor: new PrismaContractor(prismaClient),
37
- routeContractor: new ExpressContractor(),
38
- roles: ['USER', 'ADMIN'],
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
- return res.send(result)
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
- app.listen(3000, () => {
77
- console.log('Server running on port 3000');
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
- ### Guard Middleware
85
-
86
- The `Guard` middleware protects routes and adds user data to the request object:
87
-
88
- ```typescript
89
- // Protect route for authenticated users
90
- app.get('/profile', CAuthCliebt.Guard(), (req, res) => {
91
- const data = req.cauth; // TypeScript knows user exists
92
- res.json({ user });
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
- // Protect route for specific roles
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
- ### Request Object
78
+ ---
102
79
 
103
- The middleware augments the Express `Request` object with user data:
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
- ### Error Handling
82
+ ### `auth.Guard(roles?: string[])`
83
+ A middleware that verifies the Access Token in the `Authorization` header (`Bearer <token>`).
115
84
 
116
- Common error status codes:
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
- - 400: Invalid request data
119
- - 401: Unauthorized (invalid credentials)
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
- ## Development
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
- ### Prerequisites
98
+ ---
128
99
 
129
- - Node.js >= 18
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
- ## License
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
- MIT License - see LICENSE file for details.
113
+ ---
137
114
 
138
- ## Support
115
+ ## 📄 License
139
116
 
140
- For issues and feature requests, please visit the [GitHub repository](https://github.com/jonace-mpelule/cauth).
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.ZodOptional<z$1.ZodArray<z$1.ZodString>>;
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.z.core.$strip>;
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
- type: string;
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$1<T, E extends FNError = FNError> = {
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.z.core.$strip>;
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.z.core.$strip>;
142
- }, z$1.z.core.$strip>;
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: (args: LoginSchemaType) => Promise<Result$1<{
244
+ Login: ({
245
+ ...args
246
+ }: LoginSchemaType) => Promise<Result<{
203
247
  account: Account;
204
248
  tokens: Tokens;
205
249
  }>>;
206
- Register: (args: RegisterSchemaType) => Promise<Result<{
250
+ Register: ({
251
+ ...args
252
+ }: RegisterSchemaType) => Promise<Result<{
207
253
  account: Account;
208
254
  tokens: Tokens;
209
255
  }>>;
210
- Logout: (args: LogoutSchemaType) => Promise<Result<any>>;
211
- Refresh: (args: RefreshTokenSchemaType) => Promise<Result$1<{
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: (args: ChangePasswordSchemaType) => Promise<Result<unknown>>;
216
- RequestOTPCode: (args: Omit<LoginSchemaType, "password"> & {
217
- password?: string;
218
- usePassword?: boolean;
219
- otpPurpose: OtpPurpose;
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: Omit<LoginSchemaType, "password"> & {
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>;