sentri 1.1.1 → 1.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 +107 -43
- package/dist/client.d.ts +10 -10
- package/dist/client.d.ts.map +1 -1
- package/dist/errors/AuthError.d.ts +20 -22
- package/dist/errors/AuthError.d.ts.map +1 -1
- package/dist/errors/AuthError.js +17 -19
- package/dist/errors/AuthError.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/libs/config.d.ts +2 -2
- package/dist/libs/config.js +8 -8
- package/dist/libs/config.js.map +1 -1
- package/dist/libs/token.d.ts +2 -2
- package/dist/libs/token.js +10 -10
- package/dist/libs/token.js.map +1 -1
- package/dist/middleware/authorize.d.ts +1 -1
- package/dist/middleware/authorize.js +4 -4
- package/dist/middleware/authorize.js.map +1 -1
- package/dist/middleware/errorHandler.d.ts +11 -13
- package/dist/middleware/errorHandler.d.ts.map +1 -1
- package/dist/middleware/errorHandler.js +11 -13
- package/dist/middleware/errorHandler.js.map +1 -1
- package/dist/middleware/permit.d.ts +1 -1
- package/dist/middleware/permit.js +4 -4
- package/dist/middleware/permit.js.map +1 -1
- package/dist/middleware/protect.d.ts +1 -1
- package/dist/middleware/protect.js +4 -4
- package/dist/middleware/protect.js.map +1 -1
- package/dist/middleware/router.d.ts.map +1 -1
- package/dist/middleware/router.js +10 -10
- package/dist/middleware/router.js.map +1 -1
- package/dist/services/auth.d.ts +5 -5
- package/dist/services/auth.d.ts.map +1 -1
- package/dist/services/auth.js +15 -15
- package/dist/services/auth.js.map +1 -1
- package/dist/types/auth.d.ts +21 -21
- package/dist/types/auth.d.ts.map +1 -1
- package/dist/types/auth.js +1 -1
- package/dist/types/auth.js.map +1 -1
- package/package.json +1 -1
- package/templates/drizzle/auth.ts +5 -5
- package/templates/prisma/auth.ts +5 -5
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ Auth and authorization library for Express + PostgreSQL. Provides JWT-based auth
|
|
|
17
17
|
- [Programmatic API](#programmatic-api)
|
|
18
18
|
- [Types](#types)
|
|
19
19
|
- [Error Handling](#error-handling)
|
|
20
|
+
- [Migration from 1.0.x](#migration-from-10x)
|
|
20
21
|
|
|
21
22
|
---
|
|
22
23
|
|
|
@@ -55,7 +56,7 @@ prisma/
|
|
|
55
56
|
schema.prisma ← Prisma models (Prisma only, created or appended)
|
|
56
57
|
```
|
|
57
58
|
|
|
58
|
-
### 2. Mount the router
|
|
59
|
+
### 2. Mount the router and error handler
|
|
59
60
|
|
|
60
61
|
```typescript
|
|
61
62
|
import express from 'express';
|
|
@@ -64,6 +65,12 @@ import { auth } from './lib/sentri/auth.js';
|
|
|
64
65
|
const app = express();
|
|
65
66
|
app.use(express.json());
|
|
66
67
|
app.use('/auth', auth.router());
|
|
68
|
+
|
|
69
|
+
// ... your routes ...
|
|
70
|
+
|
|
71
|
+
// Must be last — catches SentriError from sentri and your own subclasses
|
|
72
|
+
app.use(auth.errorHandler());
|
|
73
|
+
app.listen(3000);
|
|
67
74
|
```
|
|
68
75
|
|
|
69
76
|
Done. All endpoints are available at `/auth/*`.
|
|
@@ -189,7 +196,7 @@ The `router` field in config lets you replace the built-in service logic for ind
|
|
|
189
196
|
Each key is optional — only override what you need. Any key you omit falls back to the built-in behaviour.
|
|
190
197
|
|
|
191
198
|
```typescript
|
|
192
|
-
import { createAuth,
|
|
199
|
+
import { createAuth, SentriError } from 'sentri';
|
|
193
200
|
import type { AuthResult } from 'sentri';
|
|
194
201
|
|
|
195
202
|
export const auth = createAuth({
|
|
@@ -202,7 +209,7 @@ export const auth = createAuth({
|
|
|
202
209
|
login: async (input): Promise<AuthResult> => {
|
|
203
210
|
const otpVerified = await redis.get(`otp:${input.identifier}`);
|
|
204
211
|
if (!otpVerified) {
|
|
205
|
-
return { success: false, error: new
|
|
212
|
+
return { success: false, error: new SentriError('INVALID_CREDENTIALS', 'OTP required') };
|
|
206
213
|
}
|
|
207
214
|
return defaultLogin(input);
|
|
208
215
|
},
|
|
@@ -232,7 +239,7 @@ export const auth = createAuth({
|
|
|
232
239
|
|
|
233
240
|
| Key | Signature | Must return |
|
|
234
241
|
|---|---|---|
|
|
235
|
-
| `register` | `(input:
|
|
242
|
+
| `register` | `(input: RegisterInput) => Promise<RegisterResult>` | `RegisterResult` |
|
|
236
243
|
| `login` | `(input: LoginInput) => Promise<AuthResult>` | `AuthResult` |
|
|
237
244
|
| `refresh` | `(refreshToken: string) => Promise<RefreshResult>` | `RefreshResult` |
|
|
238
245
|
| `logout` | `(refreshToken: string \| undefined) => Promise<void>` | `void` |
|
|
@@ -287,7 +294,7 @@ import { createAdapter } from './adapter.js';
|
|
|
287
294
|
export const adapter = createAdapter(db);
|
|
288
295
|
```
|
|
289
296
|
|
|
290
|
-
`createAdapter` throws `
|
|
297
|
+
`createAdapter` throws `SentriError` with code `CONFIGURATION_ERROR` at runtime if called without a `db` argument.
|
|
291
298
|
|
|
292
299
|
---
|
|
293
300
|
|
|
@@ -473,8 +480,8 @@ Token and password utilities are available on the auth client for use outside th
|
|
|
473
480
|
```typescript
|
|
474
481
|
// Token utilities
|
|
475
482
|
const accessToken = auth.signAccessToken({ id, identifier, roles });
|
|
476
|
-
const user = auth.verifyAccessToken(accessToken); // throws
|
|
477
|
-
const { sessionId } = auth.verifyRefreshToken(token); // throws
|
|
483
|
+
const user = auth.verifyAccessToken(accessToken); // throws SentriError if invalid
|
|
484
|
+
const { sessionId } = auth.verifyRefreshToken(token); // throws SentriError if invalid
|
|
478
485
|
const refreshToken = auth.signRefreshToken(sessionId);
|
|
479
486
|
|
|
480
487
|
// Password utilities
|
|
@@ -482,7 +489,26 @@ const hash = await auth.hashPassword('secret123');
|
|
|
482
489
|
const valid = await auth.verifyPassword('secret123', hash);
|
|
483
490
|
```
|
|
484
491
|
|
|
485
|
-
`verifyAccessToken` and `verifyRefreshToken` throw `
|
|
492
|
+
`verifyAccessToken` and `verifyRefreshToken` throw `SentriError` with code `TOKEN_EXPIRED` or `TOKEN_INVALID` — wrap them in a try/catch or use the router which handles this automatically.
|
|
493
|
+
|
|
494
|
+
### `register` — standalone service function
|
|
495
|
+
|
|
496
|
+
The `register` function is also exported directly so you can call it outside the built-in router (e.g. in tests, scripts, or admin tools):
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
import { register } from 'sentri';
|
|
500
|
+
|
|
501
|
+
const result = await register(
|
|
502
|
+
{ identifier: 'alice@example.com', password: 'hunter2', roles: ['user'] },
|
|
503
|
+
config,
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
if (result.success) {
|
|
507
|
+
console.log(result.user.id);
|
|
508
|
+
} else {
|
|
509
|
+
console.error(result.error.code); // 'USER_ALREADY_EXISTS' | 'INVALID_ROLE'
|
|
510
|
+
}
|
|
511
|
+
```
|
|
486
512
|
|
|
487
513
|
---
|
|
488
514
|
|
|
@@ -495,11 +521,11 @@ import type {
|
|
|
495
521
|
AuthAdapter,
|
|
496
522
|
AuthUser,
|
|
497
523
|
AuthResult,
|
|
498
|
-
|
|
524
|
+
RegisterResult,
|
|
499
525
|
AssignRolesResult,
|
|
500
526
|
RefreshResult,
|
|
501
527
|
ApiResponse,
|
|
502
|
-
|
|
528
|
+
RegisterInput,
|
|
503
529
|
LoginInput,
|
|
504
530
|
RouterHandlers,
|
|
505
531
|
UserRecord,
|
|
@@ -508,17 +534,17 @@ import type {
|
|
|
508
534
|
CookieConfig,
|
|
509
535
|
PermitCheck,
|
|
510
536
|
PermitOptions,
|
|
511
|
-
|
|
537
|
+
SentriErrorCode,
|
|
512
538
|
} from 'sentri';
|
|
513
539
|
|
|
514
|
-
import {
|
|
540
|
+
import { SentriError, AUTH_ERROR_STATUS, createAuth, register } from 'sentri';
|
|
515
541
|
```
|
|
516
542
|
|
|
517
543
|
---
|
|
518
544
|
|
|
519
545
|
## Error Handling
|
|
520
546
|
|
|
521
|
-
All errors thrown by the library are instances of `
|
|
547
|
+
All errors thrown by the library are instances of `SentriError` with a machine-readable `code` and an HTTP `statusCode`:
|
|
522
548
|
|
|
523
549
|
| Code | HTTP | Meaning |
|
|
524
550
|
|---|---|---|
|
|
@@ -533,44 +559,70 @@ All errors thrown by the library are instances of `AuthError` with a machine-rea
|
|
|
533
559
|
| `VALIDATION_ERROR` | 400 | Missing or invalid input field |
|
|
534
560
|
| `CONFIGURATION_ERROR` | 500 | Invalid `createAuth` configuration |
|
|
535
561
|
|
|
536
|
-
|
|
562
|
+
### `auth.errorHandler()`
|
|
563
|
+
|
|
564
|
+
Mount `auth.errorHandler()` **after all your routes** to automatically format every `SentriError` (and any subclass) into the standard envelope:
|
|
537
565
|
|
|
538
566
|
```typescript
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
567
|
+
app.use('/auth', auth.router());
|
|
568
|
+
app.use('/api', apiRouter);
|
|
569
|
+
|
|
570
|
+
// Must be last
|
|
571
|
+
app.use(auth.errorHandler());
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
Optional logger for unexpected errors:
|
|
575
|
+
|
|
576
|
+
```typescript
|
|
577
|
+
app.use(auth.errorHandler({
|
|
578
|
+
onUnhandled: (err) => logger.error('Unexpected error', { err }),
|
|
579
|
+
}));
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
### Extending `SentriError`
|
|
583
|
+
|
|
584
|
+
Define application-specific errors by extending `SentriError`. They are caught automatically by `auth.errorHandler()` via `instanceof`:
|
|
585
|
+
|
|
586
|
+
```typescript
|
|
587
|
+
import { SentriError } from 'sentri';
|
|
588
|
+
|
|
589
|
+
export class NotFoundError extends SentriError {
|
|
590
|
+
constructor(resource: string) {
|
|
591
|
+
super('NOT_FOUND', `${resource} not found`, 404);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
553
594
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
return response.status(statusCode).json({
|
|
558
|
-
error: true,
|
|
559
|
-
statusCode,
|
|
560
|
-
code: error.code,
|
|
561
|
-
message: error.message,
|
|
562
|
-
data: null,
|
|
563
|
-
});
|
|
595
|
+
export class PaymentError extends SentriError {
|
|
596
|
+
constructor(message: string) {
|
|
597
|
+
super('PAYMENT_FAILED', message, 402);
|
|
564
598
|
}
|
|
565
|
-
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Throw anywhere in your routes — auth.errorHandler() catches them all
|
|
602
|
+
app.get('/items/:id', auth.protect(), async (req, res) => {
|
|
603
|
+
const item = await db.items.findById(req.params['id']);
|
|
604
|
+
if (!item) throw new NotFoundError('Item');
|
|
605
|
+
res.json(item);
|
|
566
606
|
});
|
|
567
607
|
```
|
|
568
608
|
|
|
609
|
+
Response shape for any `SentriError` (built-in or custom):
|
|
610
|
+
|
|
611
|
+
```json
|
|
612
|
+
{
|
|
613
|
+
"error": true,
|
|
614
|
+
"statusCode": 404,
|
|
615
|
+
"code": "NOT_FOUND",
|
|
616
|
+
"message": "Item not found",
|
|
617
|
+
"data": null
|
|
618
|
+
}
|
|
619
|
+
```
|
|
620
|
+
|
|
569
621
|
---
|
|
570
622
|
|
|
571
623
|
## Migration from 1.0.x
|
|
572
624
|
|
|
573
|
-
### Breaking changes
|
|
625
|
+
### Breaking changes in 1.1.0
|
|
574
626
|
|
|
575
627
|
| What changed | Action required |
|
|
576
628
|
|---|---|
|
|
@@ -578,7 +630,19 @@ app.use((error, _request, response, next) => {
|
|
|
578
630
|
| `RouterHandlers.signup` renamed to `RouterHandlers.register` | Update config if you used a custom signup handler |
|
|
579
631
|
| `protect()` now performs one DB read per request | Ensure your adapter's `session.findById` is indexed on session ID |
|
|
580
632
|
|
|
581
|
-
###
|
|
633
|
+
### Breaking changes in 1.2.0
|
|
582
634
|
|
|
583
|
-
|
|
584
|
-
|
|
635
|
+
| What changed | Action required |
|
|
636
|
+
|---|---|
|
|
637
|
+
| `AuthError` renamed to `SentriError` | Replace all `import { AuthError }` with `import { SentriError }` |
|
|
638
|
+
| `AuthErrorCode` renamed to `SentriErrorCode` | Replace all `AuthErrorCode` type references |
|
|
639
|
+
| `SignupResult` renamed to `RegisterResult` | Replace type references |
|
|
640
|
+
| `SignupInput` renamed to `RegisterInput` | Replace type references |
|
|
641
|
+
| `signup` service renamed to `register` | Replace `import { signup }` with `import { register }` |
|
|
642
|
+
|
|
643
|
+
### New features in 1.2.0
|
|
644
|
+
|
|
645
|
+
- **`auth.errorHandler()`** — built-in Express error handler mounted like `auth.router()`.
|
|
646
|
+
- **`SentriError.statusCode`** — each error carries its own HTTP status; no need for a manual status map.
|
|
647
|
+
- **Extensible errors** — subclass `SentriError` with a custom `code` and `statusCode`; `auth.errorHandler()` catches all subclasses automatically.
|
|
648
|
+
- **`register` exported** — the registration service function is now a named export for use outside the built-in router.
|
package/dist/client.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface AuthClient<TRole extends string = string> {
|
|
|
17
17
|
*
|
|
18
18
|
* Reads the `Authorization: Bearer <token>` header, verifies the access token,
|
|
19
19
|
* confirms the session is still active in the database, and injects the decoded
|
|
20
|
-
* payload as `request.user`. Calls `next(
|
|
20
|
+
* payload as `request.user`. Calls `next(SentriError)` on any failure.
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
23
23
|
* router.get('/me', auth.protect(), (request, response) => {
|
|
@@ -29,7 +29,7 @@ export interface AuthClient<TRole extends string = string> {
|
|
|
29
29
|
* Express middleware factory that enforces role-based access.
|
|
30
30
|
*
|
|
31
31
|
* Must be used **after** `protect()`. Passes if the authenticated user has
|
|
32
|
-
* at least one of the specified roles; otherwise calls `next(
|
|
32
|
+
* at least one of the specified roles; otherwise calls `next(SentriError)` with
|
|
33
33
|
* code `FORBIDDEN`.
|
|
34
34
|
*
|
|
35
35
|
* @example
|
|
@@ -40,7 +40,7 @@ export interface AuthClient<TRole extends string = string> {
|
|
|
40
40
|
* Express middleware factory for resource-level permission checks.
|
|
41
41
|
*
|
|
42
42
|
* Must be used **after** `protect()`. Evaluates a check function against the
|
|
43
|
-
* current request and calls `next(
|
|
43
|
+
* current request and calls `next(SentriError)` with `FORBIDDEN` if it returns `false`.
|
|
44
44
|
*
|
|
45
45
|
* Accepts either a bare check function or an options object with an optional
|
|
46
46
|
* `roles` list whose members bypass the check entirely.
|
|
@@ -77,9 +77,9 @@ export interface AuthClient<TRole extends string = string> {
|
|
|
77
77
|
signAccessToken(payload: AuthUser<TRole>): string;
|
|
78
78
|
/** Sign a refresh token bound to a session ID. */
|
|
79
79
|
signRefreshToken(sessionId: string): string;
|
|
80
|
-
/** Verify and decode an access token. Throws `
|
|
80
|
+
/** Verify and decode an access token. Throws `SentriError` if invalid or expired. */
|
|
81
81
|
verifyAccessToken(token: string): AuthUser<TRole>;
|
|
82
|
-
/** Verify and decode a refresh token. Throws `
|
|
82
|
+
/** Verify and decode a refresh token. Throws `SentriError` if invalid or expired. */
|
|
83
83
|
verifyRefreshToken(token: string): {
|
|
84
84
|
sessionId: string;
|
|
85
85
|
};
|
|
@@ -103,7 +103,7 @@ export interface AuthClient<TRole extends string = string> {
|
|
|
103
103
|
*/
|
|
104
104
|
router(): Router;
|
|
105
105
|
/**
|
|
106
|
-
* Returns an Express error-handling middleware that formats every `
|
|
106
|
+
* Returns an Express error-handling middleware that formats every `SentriError`
|
|
107
107
|
* (and any subclass) into the standard sentri response envelope:
|
|
108
108
|
*
|
|
109
109
|
* ```json
|
|
@@ -111,13 +111,13 @@ export interface AuthClient<TRole extends string = string> {
|
|
|
111
111
|
* ```
|
|
112
112
|
*
|
|
113
113
|
* Mount it **after all your routes** so it acts as the global catch-all for
|
|
114
|
-
* both sentri errors and your own `
|
|
114
|
+
* both sentri errors and your own `SentriError` subclasses.
|
|
115
115
|
*
|
|
116
116
|
* @example
|
|
117
|
-
* import {
|
|
117
|
+
* import { SentriError } from 'sentri';
|
|
118
118
|
*
|
|
119
|
-
* // Define app-specific errors by extending
|
|
120
|
-
* class NotFoundError extends
|
|
119
|
+
* // Define app-specific errors by extending SentriError
|
|
120
|
+
* class NotFoundError extends SentriError {
|
|
121
121
|
* constructor(resource: string) {
|
|
122
122
|
* super('NOT_FOUND', `${resource} not found`, 404);
|
|
123
123
|
* }
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAE3E;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IACvD;;;;;;;;;;;OAWG;IACH,OAAO,IAAI,cAAc,CAAC;IAE1B;;;;;;;;;OASG;IACH,SAAS,CAAC,GAAG,KAAK,EAAE,KAAK,EAAE,GAAG,cAAc,CAAC;IAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,cAAc,CAAC;IAC3C,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,cAAc,CAAC;IAEtD,oEAAoE;IACpE,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE7C,kEAAkE;IAClE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE9D,uDAAuD;IACvD,eAAe,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC;IAElD,kDAAkD;IAClD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAE5C,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAE3E;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IACvD;;;;;;;;;;;OAWG;IACH,OAAO,IAAI,cAAc,CAAC;IAE1B;;;;;;;;;OASG;IACH,SAAS,CAAC,GAAG,KAAK,EAAE,KAAK,EAAE,GAAG,cAAc,CAAC;IAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,cAAc,CAAC;IAC3C,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,cAAc,CAAC;IAEtD,oEAAoE;IACpE,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE7C,kEAAkE;IAClE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE9D,uDAAuD;IACvD,eAAe,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC;IAElD,kDAAkD;IAClD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAE5C,qFAAqF;IACrF,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAElD,qFAAqF;IACrF,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAEzD;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,IAAI,MAAM,CAAC;IAEjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,YAAY,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,mBAAmB,CAAC;CAClE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM,EACtD,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,GACxB,UAAU,CAAC,KAAK,CAAC,CAiBnB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Discriminant codes for built-in {@link
|
|
2
|
+
* Discriminant codes for built-in {@link SentriError} instances.
|
|
3
3
|
*
|
|
4
4
|
* - `INVALID_CREDENTIALS` — identifier or password did not match (intentionally vague to prevent user enumeration)
|
|
5
5
|
* - `USER_NOT_FOUND` — an operation required a user that does not exist
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
* - `VALIDATION_ERROR` — a required field was missing or had an invalid value
|
|
13
13
|
* - `CONFIGURATION_ERROR` — `createAuth` was called with an invalid configuration
|
|
14
14
|
*
|
|
15
|
-
* When you extend {@link
|
|
15
|
+
* When you extend {@link SentriError} for your own error types you can use any
|
|
16
16
|
* string as `code` — it does not need to be one of these built-in values.
|
|
17
17
|
*/
|
|
18
|
-
export type
|
|
18
|
+
export type SentriErrorCode = 'INVALID_CREDENTIALS' | 'USER_NOT_FOUND' | 'USER_ALREADY_EXISTS' | 'TOKEN_EXPIRED' | 'TOKEN_INVALID' | 'FORBIDDEN' | 'UNAUTHORIZED' | 'INVALID_ROLE' | 'VALIDATION_ERROR' | 'CONFIGURATION_ERROR';
|
|
19
19
|
/**
|
|
20
20
|
* Default HTTP status codes for built-in error codes.
|
|
21
21
|
* Custom codes that are not in this map default to 500.
|
|
@@ -24,37 +24,37 @@ export type AuthErrorCode = 'INVALID_CREDENTIALS' | 'USER_NOT_FOUND' | 'USER_ALR
|
|
|
24
24
|
*/
|
|
25
25
|
export declare const AUTH_ERROR_STATUS: Record<string, number>;
|
|
26
26
|
/**
|
|
27
|
-
* Base error class for all authentication and authorization failures.
|
|
27
|
+
* Base error class for all authentication and authorization failures in sentri.
|
|
28
28
|
*
|
|
29
|
-
* Every error thrown by sentri is an instance of `
|
|
29
|
+
* Every error thrown by sentri is an instance of `SentriError`. The `code`
|
|
30
30
|
* property is a machine-readable string that lets you distinguish error
|
|
31
31
|
* types without string-matching on the message. Built-in codes are listed
|
|
32
|
-
* in {@link
|
|
32
|
+
* in {@link SentriErrorCode}; custom subclasses may use any string.
|
|
33
33
|
*
|
|
34
34
|
* The `statusCode` property holds the HTTP status that the built-in router
|
|
35
|
-
* and `
|
|
35
|
+
* and `auth.errorHandler()` will use in the response. For built-in codes
|
|
36
36
|
* it is derived automatically. Pass it explicitly when subclassing with a
|
|
37
37
|
* custom code.
|
|
38
38
|
*
|
|
39
39
|
* ---
|
|
40
40
|
*
|
|
41
|
-
* **Extending
|
|
41
|
+
* **Extending SentriError**
|
|
42
42
|
*
|
|
43
|
-
* You can create application-specific error classes by extending `
|
|
44
|
-
* Any subclass will be caught automatically by `
|
|
45
|
-
* `instanceof
|
|
43
|
+
* You can create application-specific error classes by extending `SentriError`.
|
|
44
|
+
* Any subclass will be caught automatically by `auth.errorHandler()` because
|
|
45
|
+
* `instanceof SentriError` is `true` for all subclasses.
|
|
46
46
|
*
|
|
47
47
|
* ```typescript
|
|
48
|
-
* import {
|
|
48
|
+
* import { SentriError } from 'sentri';
|
|
49
49
|
*
|
|
50
50
|
* // Domain error with a custom code and explicit HTTP status
|
|
51
|
-
* export class PaymentError extends
|
|
51
|
+
* export class PaymentError extends SentriError {
|
|
52
52
|
* constructor(message: string) {
|
|
53
53
|
* super('PAYMENT_FAILED', message, 402);
|
|
54
54
|
* }
|
|
55
55
|
* }
|
|
56
56
|
*
|
|
57
|
-
* // Throw it anywhere in your routes —
|
|
57
|
+
* // Throw it anywhere in your routes — auth.errorHandler() catches it
|
|
58
58
|
* router.post('/checkout', auth.protect(), async (req, res) => {
|
|
59
59
|
* const ok = await chargeCard(req.body.cardToken);
|
|
60
60
|
* if (!ok) throw new PaymentError('Card declined');
|
|
@@ -67,19 +67,17 @@ export declare const AUTH_ERROR_STATUS: Record<string, number>;
|
|
|
67
67
|
* **Error handling in custom routes**
|
|
68
68
|
*
|
|
69
69
|
* ```typescript
|
|
70
|
-
* import { AuthError, createErrorHandler } from 'sentri';
|
|
71
|
-
*
|
|
72
70
|
* app.use('/auth', auth.router());
|
|
73
71
|
* app.use('/api', apiRouter);
|
|
74
72
|
*
|
|
75
|
-
* // Mount after all routes — catches
|
|
76
|
-
* app.use(
|
|
73
|
+
* // Mount after all routes — catches SentriError from sentri AND your subclasses
|
|
74
|
+
* app.use(auth.errorHandler());
|
|
77
75
|
* ```
|
|
78
76
|
*/
|
|
79
|
-
export declare class
|
|
77
|
+
export declare class SentriError extends Error {
|
|
80
78
|
/**
|
|
81
79
|
* Machine-readable error code.
|
|
82
|
-
* Built-in codes are defined by {@link
|
|
80
|
+
* Built-in codes are defined by {@link SentriErrorCode}.
|
|
83
81
|
* Custom subclasses may use any string.
|
|
84
82
|
*/
|
|
85
83
|
readonly code: string;
|
|
@@ -90,12 +88,12 @@ export declare class AuthError extends Error {
|
|
|
90
88
|
*/
|
|
91
89
|
readonly statusCode: number;
|
|
92
90
|
/**
|
|
93
|
-
* @param code - Machine-readable error code. Use a built-in {@link
|
|
91
|
+
* @param code - Machine-readable error code. Use a built-in {@link SentriErrorCode}
|
|
94
92
|
* or any string for custom subclasses.
|
|
95
93
|
* @param message - Human-readable description of the error.
|
|
96
94
|
* @param statusCode - HTTP status to use in the response. For built-in codes
|
|
97
95
|
* this is derived automatically; for custom codes it defaults to `500`.
|
|
98
96
|
*/
|
|
99
|
-
constructor(code:
|
|
97
|
+
constructor(code: SentriErrorCode | (string & {}), message: string, statusCode?: number);
|
|
100
98
|
}
|
|
101
99
|
//# sourceMappingURL=AuthError.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthError.d.ts","sourceRoot":"","sources":["../../src/errors/AuthError.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"AuthError.d.ts","sourceRoot":"","sources":["../../src/errors/AuthError.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,eAAe,GACvB,qBAAqB,GACrB,gBAAgB,GAChB,qBAAqB,GACrB,eAAe,GACf,eAAe,GACf,WAAW,GACX,cAAc,GACd,cAAc,GACd,kBAAkB,GAClB,qBAAqB,CAAC;AAE1B;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAWpD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC;;;;OAIG;IACH,SAAgB,IAAI,EAAE,MAAM,CAAC;IAE7B;;;;OAIG;IACH,SAAgB,UAAU,EAAE,MAAM,CAAC;IAEnC;;;;;;OAMG;gBAED,IAAI,EAAE,eAAe,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,EACrC,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM;CAOtB"}
|
package/dist/errors/AuthError.js
CHANGED
|
@@ -17,37 +17,37 @@ export const AUTH_ERROR_STATUS = {
|
|
|
17
17
|
CONFIGURATION_ERROR: 500,
|
|
18
18
|
};
|
|
19
19
|
/**
|
|
20
|
-
* Base error class for all authentication and authorization failures.
|
|
20
|
+
* Base error class for all authentication and authorization failures in sentri.
|
|
21
21
|
*
|
|
22
|
-
* Every error thrown by sentri is an instance of `
|
|
22
|
+
* Every error thrown by sentri is an instance of `SentriError`. The `code`
|
|
23
23
|
* property is a machine-readable string that lets you distinguish error
|
|
24
24
|
* types without string-matching on the message. Built-in codes are listed
|
|
25
|
-
* in {@link
|
|
25
|
+
* in {@link SentriErrorCode}; custom subclasses may use any string.
|
|
26
26
|
*
|
|
27
27
|
* The `statusCode` property holds the HTTP status that the built-in router
|
|
28
|
-
* and `
|
|
28
|
+
* and `auth.errorHandler()` will use in the response. For built-in codes
|
|
29
29
|
* it is derived automatically. Pass it explicitly when subclassing with a
|
|
30
30
|
* custom code.
|
|
31
31
|
*
|
|
32
32
|
* ---
|
|
33
33
|
*
|
|
34
|
-
* **Extending
|
|
34
|
+
* **Extending SentriError**
|
|
35
35
|
*
|
|
36
|
-
* You can create application-specific error classes by extending `
|
|
37
|
-
* Any subclass will be caught automatically by `
|
|
38
|
-
* `instanceof
|
|
36
|
+
* You can create application-specific error classes by extending `SentriError`.
|
|
37
|
+
* Any subclass will be caught automatically by `auth.errorHandler()` because
|
|
38
|
+
* `instanceof SentriError` is `true` for all subclasses.
|
|
39
39
|
*
|
|
40
40
|
* ```typescript
|
|
41
|
-
* import {
|
|
41
|
+
* import { SentriError } from 'sentri';
|
|
42
42
|
*
|
|
43
43
|
* // Domain error with a custom code and explicit HTTP status
|
|
44
|
-
* export class PaymentError extends
|
|
44
|
+
* export class PaymentError extends SentriError {
|
|
45
45
|
* constructor(message: string) {
|
|
46
46
|
* super('PAYMENT_FAILED', message, 402);
|
|
47
47
|
* }
|
|
48
48
|
* }
|
|
49
49
|
*
|
|
50
|
-
* // Throw it anywhere in your routes —
|
|
50
|
+
* // Throw it anywhere in your routes — auth.errorHandler() catches it
|
|
51
51
|
* router.post('/checkout', auth.protect(), async (req, res) => {
|
|
52
52
|
* const ok = await chargeCard(req.body.cardToken);
|
|
53
53
|
* if (!ok) throw new PaymentError('Card declined');
|
|
@@ -60,19 +60,17 @@ export const AUTH_ERROR_STATUS = {
|
|
|
60
60
|
* **Error handling in custom routes**
|
|
61
61
|
*
|
|
62
62
|
* ```typescript
|
|
63
|
-
* import { AuthError, createErrorHandler } from 'sentri';
|
|
64
|
-
*
|
|
65
63
|
* app.use('/auth', auth.router());
|
|
66
64
|
* app.use('/api', apiRouter);
|
|
67
65
|
*
|
|
68
|
-
* // Mount after all routes — catches
|
|
69
|
-
* app.use(
|
|
66
|
+
* // Mount after all routes — catches SentriError from sentri AND your subclasses
|
|
67
|
+
* app.use(auth.errorHandler());
|
|
70
68
|
* ```
|
|
71
69
|
*/
|
|
72
|
-
export class
|
|
70
|
+
export class SentriError extends Error {
|
|
73
71
|
/**
|
|
74
72
|
* Machine-readable error code.
|
|
75
|
-
* Built-in codes are defined by {@link
|
|
73
|
+
* Built-in codes are defined by {@link SentriErrorCode}.
|
|
76
74
|
* Custom subclasses may use any string.
|
|
77
75
|
*/
|
|
78
76
|
code;
|
|
@@ -83,7 +81,7 @@ export class AuthError extends Error {
|
|
|
83
81
|
*/
|
|
84
82
|
statusCode;
|
|
85
83
|
/**
|
|
86
|
-
* @param code - Machine-readable error code. Use a built-in {@link
|
|
84
|
+
* @param code - Machine-readable error code. Use a built-in {@link SentriErrorCode}
|
|
87
85
|
* or any string for custom subclasses.
|
|
88
86
|
* @param message - Human-readable description of the error.
|
|
89
87
|
* @param statusCode - HTTP status to use in the response. For built-in codes
|
|
@@ -91,7 +89,7 @@ export class AuthError extends Error {
|
|
|
91
89
|
*/
|
|
92
90
|
constructor(code, message, statusCode) {
|
|
93
91
|
super(message);
|
|
94
|
-
this.name = '
|
|
92
|
+
this.name = 'SentriError';
|
|
95
93
|
this.code = code;
|
|
96
94
|
this.statusCode = statusCode ?? AUTH_ERROR_STATUS[code] ?? 500;
|
|
97
95
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthError.js","sourceRoot":"","sources":["../../src/errors/AuthError.ts"],"names":[],"mappings":"AA6BA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAA2B;IACvD,YAAY,EAAE,GAAG;IACjB,aAAa,EAAE,GAAG;IAClB,aAAa,EAAE,GAAG;IAClB,mBAAmB,EAAE,GAAG;IACxB,SAAS,EAAE,GAAG;IACd,cAAc,EAAE,GAAG;IACnB,mBAAmB,EAAE,GAAG;IACxB,YAAY,EAAE,GAAG;IACjB,gBAAgB,EAAE,GAAG;IACrB,mBAAmB,EAAE,GAAG;CACzB,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"AuthError.js","sourceRoot":"","sources":["../../src/errors/AuthError.ts"],"names":[],"mappings":"AA6BA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAA2B;IACvD,YAAY,EAAE,GAAG;IACjB,aAAa,EAAE,GAAG;IAClB,aAAa,EAAE,GAAG;IAClB,mBAAmB,EAAE,GAAG;IACxB,SAAS,EAAE,GAAG;IACd,cAAc,EAAE,GAAG;IACnB,mBAAmB,EAAE,GAAG;IACxB,YAAY,EAAE,GAAG;IACjB,gBAAgB,EAAE,GAAG;IACrB,mBAAmB,EAAE,GAAG;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpC;;;;OAIG;IACa,IAAI,CAAS;IAE7B;;;;OAIG;IACa,UAAU,CAAS;IAEnC;;;;;;OAMG;IACH,YACE,IAAqC,EACrC,OAAe,EACf,UAAmB;QAEnB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,UAAU,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC;IACjE,CAAC;CACF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,12 +6,13 @@ declare global {
|
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
-
export type { AuthConfig, CookieConfig, AuthUser, ApiResponse, AuthAdapter, UserRecord, SessionRecord, CreateUserData, RouterHandlers,
|
|
10
|
-
export type {
|
|
9
|
+
export type { AuthConfig, CookieConfig, AuthUser, ApiResponse, AuthAdapter, UserRecord, SessionRecord, CreateUserData, RouterHandlers, RegisterInput, LoginInput, RegisterResult, AuthResult, RefreshResult, AssignRolesResult, } from './types/auth.js';
|
|
10
|
+
export type { SentriErrorCode } from './errors/AuthError.js';
|
|
11
11
|
export type { AuthClient } from './client.js';
|
|
12
12
|
export type { ErrorHandlerOptions } from './middleware/errorHandler.js';
|
|
13
|
-
export {
|
|
13
|
+
export { SentriError, AUTH_ERROR_STATUS } from './errors/AuthError.js';
|
|
14
14
|
export { createAuth } from './client.js';
|
|
15
15
|
export { createErrorHandler } from './middleware/errorHandler.js';
|
|
16
|
+
export { register } from './services/auth.js';
|
|
16
17
|
export type { PermitCheck, PermitOptions } from './middleware/permit.js';
|
|
17
18
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAGhD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,IAAI,CAAC,EAAE,QAAQ,CAAC;SACjB;KACF;CACF;AAED,YAAY,EACV,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,WAAW,EACX,UAAU,EACV,aAAa,EACb,cAAc,EACd,cAAc,EACd,aAAa,EACb,UAAU,EACV,cAAc,EACd,UAAU,EACV,aAAa,EACb,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAExE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { SentriError, AUTH_ERROR_STATUS } from './errors/AuthError.js';
|
|
2
2
|
export { createAuth } from './client.js';
|
|
3
3
|
export { createErrorHandler } from './middleware/errorHandler.js';
|
|
4
|
+
export { register } from './services/auth.js';
|
|
4
5
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgCA,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/libs/config.d.ts
CHANGED
|
@@ -17,14 +17,14 @@ export interface ResolvedConfig {
|
|
|
17
17
|
* Validate configuration at startup so misconfiguration is caught immediately,
|
|
18
18
|
* not at the first login attempt.
|
|
19
19
|
*
|
|
20
|
-
* Throws {@link
|
|
20
|
+
* Throws {@link SentriError} with code `CONFIGURATION_ERROR` for any of:
|
|
21
21
|
* - `secret` missing or shorter than 32 characters
|
|
22
22
|
* - `saltRounds` outside the range 10–31
|
|
23
23
|
* - `validRoles` is empty or missing
|
|
24
24
|
* - `adapter` is missing
|
|
25
25
|
*
|
|
26
26
|
* @param config - The raw config passed to {@link createAuth}.
|
|
27
|
-
* @throws {
|
|
27
|
+
* @throws {SentriError} With code `CONFIGURATION_ERROR` on any invalid field.
|
|
28
28
|
*/
|
|
29
29
|
export declare function validateConfig(config: AuthConfig): void;
|
|
30
30
|
/**
|
package/dist/libs/config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SentriError } from '../errors/AuthError.js';
|
|
2
2
|
const MIN_SECRET_LENGTH = 32;
|
|
3
3
|
const MIN_SALT_ROUNDS = 10;
|
|
4
4
|
const MAX_SALT_ROUNDS = 31;
|
|
@@ -6,31 +6,31 @@ const MAX_SALT_ROUNDS = 31;
|
|
|
6
6
|
* Validate configuration at startup so misconfiguration is caught immediately,
|
|
7
7
|
* not at the first login attempt.
|
|
8
8
|
*
|
|
9
|
-
* Throws {@link
|
|
9
|
+
* Throws {@link SentriError} with code `CONFIGURATION_ERROR` for any of:
|
|
10
10
|
* - `secret` missing or shorter than 32 characters
|
|
11
11
|
* - `saltRounds` outside the range 10–31
|
|
12
12
|
* - `validRoles` is empty or missing
|
|
13
13
|
* - `adapter` is missing
|
|
14
14
|
*
|
|
15
15
|
* @param config - The raw config passed to {@link createAuth}.
|
|
16
|
-
* @throws {
|
|
16
|
+
* @throws {SentriError} With code `CONFIGURATION_ERROR` on any invalid field.
|
|
17
17
|
*/
|
|
18
18
|
export function validateConfig(config) {
|
|
19
19
|
if (!config.secret || config.secret.trim().length === 0) {
|
|
20
|
-
throw new
|
|
20
|
+
throw new SentriError('CONFIGURATION_ERROR', 'secret must not be empty');
|
|
21
21
|
}
|
|
22
22
|
if (config.secret.length < MIN_SECRET_LENGTH) {
|
|
23
|
-
throw new
|
|
23
|
+
throw new SentriError('CONFIGURATION_ERROR', `secret must be at least ${MIN_SECRET_LENGTH} characters to be cryptographically safe`);
|
|
24
24
|
}
|
|
25
25
|
const saltRounds = config.saltRounds ?? 12;
|
|
26
26
|
if (!Number.isInteger(saltRounds) || saltRounds < MIN_SALT_ROUNDS || saltRounds > MAX_SALT_ROUNDS) {
|
|
27
|
-
throw new
|
|
27
|
+
throw new SentriError('CONFIGURATION_ERROR', `saltRounds must be an integer between ${MIN_SALT_ROUNDS} and ${MAX_SALT_ROUNDS}`);
|
|
28
28
|
}
|
|
29
29
|
if (!config.validRoles || config.validRoles.length === 0) {
|
|
30
|
-
throw new
|
|
30
|
+
throw new SentriError('CONFIGURATION_ERROR', 'validRoles must contain at least one role');
|
|
31
31
|
}
|
|
32
32
|
if (!config.adapter) {
|
|
33
|
-
throw new
|
|
33
|
+
throw new SentriError('CONFIGURATION_ERROR', 'adapter is required');
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
/**
|