@cauth/express 0.0.5 → 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
@@ -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.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>>;
27
30
  createdAt: z$1.ZodDate;
28
31
  updatedAt: z$1.ZodDate;
29
- }, z$1.z.core.$strip>;
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
- type: string;
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.z.core.$strip>;
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.z.core.$strip>;
141
- }, z$1.z.core.$strip>;
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
- }: Omit<LoginSchemaType, "password"> & {
228
- password?: string;
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: Omit<LoginSchemaType, "password"> & {
236
- code: string;
237
- }) => Promise<Result<{
275
+ LoginWithOTP: (args: OTPLogin) => Promise<Result<{
238
276
  account: Account;
239
277
  tokens: Tokens;
240
278
  }>>;