sentri 1.1.0 → 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 +49 -14
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -1
- package/dist/client.js.map +1 -1
- package/dist/errors/AuthError.d.ts +82 -24
- package/dist/errors/AuthError.d.ts.map +1 -1
- package/dist/errors/AuthError.js +87 -20
- package/dist/errors/AuthError.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -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 +71 -0
- package/dist/middleware/errorHandler.d.ts.map +1 -0
- package/dist/middleware/errorHandler.js +74 -0
- package/dist/middleware/errorHandler.js.map +1 -0
- 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 +11 -13
- 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 +37 -2
- package/templates/prisma/auth.ts +37 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthError.js","sourceRoot":"","sources":["../../src/errors/AuthError.ts"],"names":[],"mappings":"
|
|
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,10 +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
|
-
export {
|
|
12
|
+
export type { ErrorHandlerOptions } from './middleware/errorHandler.js';
|
|
13
|
+
export { SentriError, AUTH_ERROR_STATUS } from './errors/AuthError.js';
|
|
13
14
|
export { createAuth } from './client.js';
|
|
15
|
+
export { createErrorHandler } from './middleware/errorHandler.js';
|
|
16
|
+
export { register } from './services/auth.js';
|
|
14
17
|
export type { PermitCheck, PermitOptions } from './middleware/permit.js';
|
|
15
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,3 +1,5 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { SentriError, AUTH_ERROR_STATUS } from './errors/AuthError.js';
|
|
2
2
|
export { createAuth } from './client.js';
|
|
3
|
+
export { createErrorHandler } from './middleware/errorHandler.js';
|
|
4
|
+
export { register } from './services/auth.js';
|
|
3
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":"AAgCA,OAAO,EAAE,
|
|
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
|
/**
|
package/dist/libs/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/libs/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/libs/config.ts"],"names":[],"mappings":"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"}
|
package/dist/libs/token.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export declare function signRefreshToken(sessionId: string, config: AuthConfig):
|
|
|
29
29
|
* @param token - Compact JWT access token string.
|
|
30
30
|
* @param config - Auth configuration used to derive the secret and algorithm.
|
|
31
31
|
* @returns Decoded `AuthUser` payload (id, identifier, roles).
|
|
32
|
-
* @throws {
|
|
32
|
+
* @throws {SentriError} With `TOKEN_EXPIRED` if expired, `TOKEN_INVALID` otherwise.
|
|
33
33
|
*/
|
|
34
34
|
export declare function verifyAccessToken(token: string, config: AuthConfig): AuthUser;
|
|
35
35
|
/**
|
|
@@ -38,7 +38,7 @@ export declare function verifyAccessToken(token: string, config: AuthConfig): Au
|
|
|
38
38
|
* @param token - Compact JWT refresh token string.
|
|
39
39
|
* @param config - Auth configuration used to derive the secret and algorithm.
|
|
40
40
|
* @returns Object with `sessionId` matching the one passed to {@link signRefreshToken}.
|
|
41
|
-
* @throws {
|
|
41
|
+
* @throws {SentriError} With `TOKEN_EXPIRED` if expired, `TOKEN_INVALID` otherwise.
|
|
42
42
|
*/
|
|
43
43
|
export declare function verifyRefreshToken(token: string, config: AuthConfig): {
|
|
44
44
|
sessionId: string;
|
package/dist/libs/token.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import jwt, {} from 'jsonwebtoken';
|
|
2
|
-
import {
|
|
2
|
+
import { SentriError } from '../errors/AuthError.js';
|
|
3
3
|
import { resolveConfig } from './config.js';
|
|
4
4
|
/**
|
|
5
5
|
* Derive separate HMAC secrets for access and refresh tokens from a single
|
|
@@ -31,30 +31,30 @@ function sign(payload, secret, expiresIn, algorithm) {
|
|
|
31
31
|
return jwt.sign(payload, secret, options);
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
* Verify and decode a JWT, mapping jsonwebtoken errors to typed {@link
|
|
34
|
+
* Verify and decode a JWT, mapping jsonwebtoken errors to typed {@link SentriError}s.
|
|
35
35
|
*
|
|
36
36
|
* @param token - Compact JWT string to verify.
|
|
37
37
|
* @param secret - HMAC key used to sign the token.
|
|
38
38
|
* @param algorithm - Expected signing algorithm.
|
|
39
39
|
* @returns Decoded payload cast to `T`.
|
|
40
|
-
* @throws {
|
|
41
|
-
* @throws {
|
|
40
|
+
* @throws {SentriError} With `TOKEN_EXPIRED` if the token's `exp` claim is in the past.
|
|
41
|
+
* @throws {SentriError} With `TOKEN_INVALID` for any other verification failure.
|
|
42
42
|
*/
|
|
43
43
|
function verify(token, secret, algorithm) {
|
|
44
44
|
try {
|
|
45
45
|
const decoded = jwt.verify(token, secret, { algorithms: [algorithm] });
|
|
46
46
|
if (typeof decoded === 'string' || decoded === null) {
|
|
47
|
-
throw new
|
|
47
|
+
throw new SentriError('TOKEN_INVALID', 'Token payload is not an object');
|
|
48
48
|
}
|
|
49
49
|
return decoded;
|
|
50
50
|
}
|
|
51
51
|
catch (err) {
|
|
52
|
-
if (err instanceof
|
|
52
|
+
if (err instanceof SentriError)
|
|
53
53
|
throw err;
|
|
54
54
|
if (err instanceof jwt.TokenExpiredError) {
|
|
55
|
-
throw new
|
|
55
|
+
throw new SentriError('TOKEN_EXPIRED', 'Token has expired');
|
|
56
56
|
}
|
|
57
|
-
throw new
|
|
57
|
+
throw new SentriError('TOKEN_INVALID', 'Token is invalid or malformed');
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
@@ -95,7 +95,7 @@ export function signRefreshToken(sessionId, config) {
|
|
|
95
95
|
* @param token - Compact JWT access token string.
|
|
96
96
|
* @param config - Auth configuration used to derive the secret and algorithm.
|
|
97
97
|
* @returns Decoded `AuthUser` payload (id, identifier, roles).
|
|
98
|
-
* @throws {
|
|
98
|
+
* @throws {SentriError} With `TOKEN_EXPIRED` if expired, `TOKEN_INVALID` otherwise.
|
|
99
99
|
*/
|
|
100
100
|
export function verifyAccessToken(token, config) {
|
|
101
101
|
const resolved = resolveConfig(config);
|
|
@@ -108,7 +108,7 @@ export function verifyAccessToken(token, config) {
|
|
|
108
108
|
* @param token - Compact JWT refresh token string.
|
|
109
109
|
* @param config - Auth configuration used to derive the secret and algorithm.
|
|
110
110
|
* @returns Object with `sessionId` matching the one passed to {@link signRefreshToken}.
|
|
111
|
-
* @throws {
|
|
111
|
+
* @throws {SentriError} With `TOKEN_EXPIRED` if expired, `TOKEN_INVALID` otherwise.
|
|
112
112
|
*/
|
|
113
113
|
export function verifyRefreshToken(token, config) {
|
|
114
114
|
const resolved = resolveConfig(config);
|
package/dist/libs/token.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/libs/token.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,EAAE,EAAoB,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/libs/token.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import type { RequestHandler } from 'express';
|
|
|
3
3
|
* Express middleware factory for role-based access control (RBAC).
|
|
4
4
|
*
|
|
5
5
|
* Passes if the authenticated user (set by `protect()`) has **at least one**
|
|
6
|
-
* of the specified `allowedRoles`. Calls `next(
|
|
6
|
+
* of the specified `allowedRoles`. Calls `next(SentriError)` with code `FORBIDDEN`
|
|
7
7
|
* if no roles match, or `UNAUTHORIZED` if `req.user` is absent.
|
|
8
8
|
*
|
|
9
9
|
* Must be used **after** `protect()`.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SentriError } from '../errors/AuthError.js';
|
|
2
2
|
/**
|
|
3
3
|
* Express middleware factory for role-based access control (RBAC).
|
|
4
4
|
*
|
|
5
5
|
* Passes if the authenticated user (set by `protect()`) has **at least one**
|
|
6
|
-
* of the specified `allowedRoles`. Calls `next(
|
|
6
|
+
* of the specified `allowedRoles`. Calls `next(SentriError)` with code `FORBIDDEN`
|
|
7
7
|
* if no roles match, or `UNAUTHORIZED` if `req.user` is absent.
|
|
8
8
|
*
|
|
9
9
|
* Must be used **after** `protect()`.
|
|
@@ -17,12 +17,12 @@ import { AuthError } from '../errors/AuthError.js';
|
|
|
17
17
|
export function authorize(...allowedRoles) {
|
|
18
18
|
return (request, _response, next) => {
|
|
19
19
|
if (!request.user) {
|
|
20
|
-
return next(new
|
|
20
|
+
return next(new SentriError('UNAUTHORIZED', 'Not authenticated'));
|
|
21
21
|
}
|
|
22
22
|
const userRoles = request.user.roles;
|
|
23
23
|
const hasRole = allowedRoles.some((role) => userRoles.includes(role));
|
|
24
24
|
if (!hasRole) {
|
|
25
|
-
return next(new
|
|
25
|
+
return next(new SentriError('FORBIDDEN', `Requires one of roles: ${allowedRoles.join(', ')}`));
|
|
26
26
|
}
|
|
27
27
|
next();
|
|
28
28
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../src/middleware/authorize.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../src/middleware/authorize.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAErD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS,CAAuB,GAAG,YAAqB;IACtE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE;QAClC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,IAAI,WAAW,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAC,CAAC;QACpE,CAAC;QACD,MAAM,SAAS,GAAsB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;QACxD,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QACtE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,IAAI,CACT,IAAI,WAAW,CAAC,WAAW,EAAE,0BAA0B,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAClF,CAAC;QACJ,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ErrorRequestHandler } from 'express';
|
|
2
|
+
/**
|
|
3
|
+
* Options for {@link createErrorHandler}.
|
|
4
|
+
*/
|
|
5
|
+
export interface ErrorHandlerOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Called for errors that are **not** a `SentriError` instance (or subclass).
|
|
8
|
+
*
|
|
9
|
+
* Use this to log unexpected server errors before the generic 500 response
|
|
10
|
+
* is sent. The error is passed as-is and may be any unknown value.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* app.use(auth.errorHandler({
|
|
14
|
+
* onUnhandled: (err) => logger.error('Unhandled error', { err }),
|
|
15
|
+
* }));
|
|
16
|
+
*/
|
|
17
|
+
onUnhandled?: (error: unknown) => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Creates an Express error-handling middleware that formats every `SentriError`
|
|
21
|
+
* (including subclasses) into the standard sentri response envelope:
|
|
22
|
+
*
|
|
23
|
+
* ```json
|
|
24
|
+
* { "error": true, "statusCode": 401, "code": "UNAUTHORIZED", "message": "...", "data": null }
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Prefer using `auth.errorHandler()` instead of calling this directly:
|
|
28
|
+
*
|
|
29
|
+
* ```typescript
|
|
30
|
+
* app.use('/auth', auth.router());
|
|
31
|
+
* app.use('/api', apiRouter);
|
|
32
|
+
*
|
|
33
|
+
* // Must come after all route/middleware registrations
|
|
34
|
+
* app.use(auth.errorHandler());
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* ---
|
|
38
|
+
*
|
|
39
|
+
* **Works with built-in sentri errors and your own subclasses**
|
|
40
|
+
*
|
|
41
|
+
* Because `instanceof SentriError` matches any subclass, you can define
|
|
42
|
+
* application-specific error types and have them automatically formatted
|
|
43
|
+
* by this handler:
|
|
44
|
+
*
|
|
45
|
+
* ```typescript
|
|
46
|
+
* import { SentriError } from 'sentri';
|
|
47
|
+
*
|
|
48
|
+
* // Extend SentriError for domain-specific failures
|
|
49
|
+
* export class NotFoundError extends SentriError {
|
|
50
|
+
* constructor(resource: string) {
|
|
51
|
+
* super('NOT_FOUND', `${resource} not found`, 404);
|
|
52
|
+
* }
|
|
53
|
+
* }
|
|
54
|
+
*
|
|
55
|
+
* export class PaymentError extends SentriError {
|
|
56
|
+
* constructor(message: string) {
|
|
57
|
+
* super('PAYMENT_FAILED', message, 402);
|
|
58
|
+
* }
|
|
59
|
+
* }
|
|
60
|
+
*
|
|
61
|
+
* // All of the above are caught and formatted by one handler
|
|
62
|
+
* app.use(auth.errorHandler({
|
|
63
|
+
* onUnhandled: (err) => console.error('Unexpected error:', err),
|
|
64
|
+
* }));
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* @param options - Optional configuration (see {@link ErrorHandlerOptions}).
|
|
68
|
+
* @returns An Express `ErrorRequestHandler` (4-argument middleware).
|
|
69
|
+
*/
|
|
70
|
+
export declare function createErrorHandler(options?: ErrorHandlerOptions): ErrorRequestHandler;
|
|
71
|
+
//# sourceMappingURL=errorHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/middleware/errorHandler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAGnD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,mBAAmB,CAsBrF"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { SentriError } from '../errors/AuthError.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates an Express error-handling middleware that formats every `SentriError`
|
|
4
|
+
* (including subclasses) into the standard sentri response envelope:
|
|
5
|
+
*
|
|
6
|
+
* ```json
|
|
7
|
+
* { "error": true, "statusCode": 401, "code": "UNAUTHORIZED", "message": "...", "data": null }
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* Prefer using `auth.errorHandler()` instead of calling this directly:
|
|
11
|
+
*
|
|
12
|
+
* ```typescript
|
|
13
|
+
* app.use('/auth', auth.router());
|
|
14
|
+
* app.use('/api', apiRouter);
|
|
15
|
+
*
|
|
16
|
+
* // Must come after all route/middleware registrations
|
|
17
|
+
* app.use(auth.errorHandler());
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* ---
|
|
21
|
+
*
|
|
22
|
+
* **Works with built-in sentri errors and your own subclasses**
|
|
23
|
+
*
|
|
24
|
+
* Because `instanceof SentriError` matches any subclass, you can define
|
|
25
|
+
* application-specific error types and have them automatically formatted
|
|
26
|
+
* by this handler:
|
|
27
|
+
*
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { SentriError } from 'sentri';
|
|
30
|
+
*
|
|
31
|
+
* // Extend SentriError for domain-specific failures
|
|
32
|
+
* export class NotFoundError extends SentriError {
|
|
33
|
+
* constructor(resource: string) {
|
|
34
|
+
* super('NOT_FOUND', `${resource} not found`, 404);
|
|
35
|
+
* }
|
|
36
|
+
* }
|
|
37
|
+
*
|
|
38
|
+
* export class PaymentError extends SentriError {
|
|
39
|
+
* constructor(message: string) {
|
|
40
|
+
* super('PAYMENT_FAILED', message, 402);
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
43
|
+
*
|
|
44
|
+
* // All of the above are caught and formatted by one handler
|
|
45
|
+
* app.use(auth.errorHandler({
|
|
46
|
+
* onUnhandled: (err) => console.error('Unexpected error:', err),
|
|
47
|
+
* }));
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @param options - Optional configuration (see {@link ErrorHandlerOptions}).
|
|
51
|
+
* @returns An Express `ErrorRequestHandler` (4-argument middleware).
|
|
52
|
+
*/
|
|
53
|
+
export function createErrorHandler(options) {
|
|
54
|
+
return (err, _req, res, _next) => {
|
|
55
|
+
if (err instanceof SentriError) {
|
|
56
|
+
res.status(err.statusCode).json({
|
|
57
|
+
error: true,
|
|
58
|
+
statusCode: err.statusCode,
|
|
59
|
+
code: err.code,
|
|
60
|
+
message: err.message,
|
|
61
|
+
data: null,
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
options?.onUnhandled?.(err);
|
|
66
|
+
res.status(500).json({
|
|
67
|
+
error: true,
|
|
68
|
+
statusCode: 500,
|
|
69
|
+
message: 'Internal server error',
|
|
70
|
+
data: null,
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=errorHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../src/middleware/errorHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAoBrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAA6B;IAC9D,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;QAC/B,IAAI,GAAG,YAAY,WAAW,EAAE,CAAC;YAC/B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC;gBAC9B,KAAK,EAAE,IAAI;gBACX,UAAU,EAAE,GAAG,CAAC,UAAU;gBAC1B,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,OAAO,EAAE,WAAW,EAAE,CAAC,GAAG,CAAC,CAAC;QAE5B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,KAAK,EAAE,IAAI;YACX,UAAU,EAAE,GAAG;YACf,OAAO,EAAE,uBAAuB;YAChC,IAAI,EAAE,IAAI;SACX,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -31,7 +31,7 @@ export interface PermitOptions<TRole extends string> {
|
|
|
31
31
|
* Express middleware factory for resource-level permission checks.
|
|
32
32
|
*
|
|
33
33
|
* Must be used **after** `protect()`. Evaluates a check function against the
|
|
34
|
-
* current request; calls `next(
|
|
34
|
+
* current request; calls `next(SentriError)` with code `FORBIDDEN` if it returns `false`.
|
|
35
35
|
*
|
|
36
36
|
* Accepts either a bare check function or an options object with an optional
|
|
37
37
|
* `roles` list whose members bypass the check entirely.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SentriError } from '../errors/AuthError.js';
|
|
2
2
|
/**
|
|
3
3
|
* Express middleware factory for resource-level permission checks.
|
|
4
4
|
*
|
|
5
5
|
* Must be used **after** `protect()`. Evaluates a check function against the
|
|
6
|
-
* current request; calls `next(
|
|
6
|
+
* current request; calls `next(SentriError)` with code `FORBIDDEN` if it returns `false`.
|
|
7
7
|
*
|
|
8
8
|
* Accepts either a bare check function or an options object with an optional
|
|
9
9
|
* `roles` list whose members bypass the check entirely.
|
|
@@ -36,7 +36,7 @@ export function permit(optionsOrCheck) {
|
|
|
36
36
|
: optionsOrCheck;
|
|
37
37
|
return async (request, _response, next) => {
|
|
38
38
|
if (!request.user) {
|
|
39
|
-
return next(new
|
|
39
|
+
return next(new SentriError('UNAUTHORIZED', 'Not authenticated'));
|
|
40
40
|
}
|
|
41
41
|
if (options.roles && options.roles.length > 0) {
|
|
42
42
|
const userRoles = request.user.roles;
|
|
@@ -50,7 +50,7 @@ export function permit(optionsOrCheck) {
|
|
|
50
50
|
next();
|
|
51
51
|
}
|
|
52
52
|
else {
|
|
53
|
-
next(new
|
|
53
|
+
next(new SentriError('FORBIDDEN', 'You do not have permission to perform this action'));
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permit.js","sourceRoot":"","sources":["../../src/middleware/permit.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"permit.js","sourceRoot":"","sources":["../../src/middleware/permit.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAgCrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,MAAM,CACpB,cAAkD;IAElD,MAAM,OAAO,GACX,OAAO,cAAc,KAAK,UAAU;QAClC,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE;QAC3B,CAAC,CAAC,cAAc,CAAC;IAErB,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE;QACxC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,IAAI,WAAW,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAC,CAAC;QACpE,CAAC;QAED,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9C,MAAM,SAAS,GAAsB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;YACxD,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;YAC7E,IAAI,aAAa;gBAAE,OAAO,IAAI,EAAE,CAAC;QACnC,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC7C,IAAI,OAAO,EAAE,CAAC;gBACZ,IAAI,EAAE,CAAC;YACT,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,IAAI,WAAW,CAAC,WAAW,EAAE,mDAAmD,CAAC,CAAC,CAAC;YAC1F,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -4,7 +4,7 @@ import type { AuthConfig } from '../types/auth.js';
|
|
|
4
4
|
* Express middleware factory that enforces JWT authentication and session validity.
|
|
5
5
|
*
|
|
6
6
|
* Reads the `Authorization: Bearer <token>` header, verifies the access token,
|
|
7
|
-
* and attaches the decoded payload to `req.user`. Calls `next(
|
|
7
|
+
* and attaches the decoded payload to `req.user`. Calls `next(SentriError)` on
|
|
8
8
|
* any failure so your error handler can convert it to an HTTP response.
|
|
9
9
|
*
|
|
10
10
|
* Since sentri 1.1.0 access tokens embed a `sessionId` claim. When this claim
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SentriError } from '../errors/AuthError.js';
|
|
2
2
|
import { verifyAccessToken } from '../libs/token.js';
|
|
3
3
|
/**
|
|
4
4
|
* Express middleware factory that enforces JWT authentication and session validity.
|
|
5
5
|
*
|
|
6
6
|
* Reads the `Authorization: Bearer <token>` header, verifies the access token,
|
|
7
|
-
* and attaches the decoded payload to `req.user`. Calls `next(
|
|
7
|
+
* and attaches the decoded payload to `req.user`. Calls `next(SentriError)` on
|
|
8
8
|
* any failure so your error handler can convert it to an HTTP response.
|
|
9
9
|
*
|
|
10
10
|
* Since sentri 1.1.0 access tokens embed a `sessionId` claim. When this claim
|
|
@@ -31,7 +31,7 @@ export function protect(config) {
|
|
|
31
31
|
return async (request, _response, next) => {
|
|
32
32
|
const authHeader = request.headers['authorization'];
|
|
33
33
|
if (!authHeader?.startsWith('Bearer ')) {
|
|
34
|
-
return next(new
|
|
34
|
+
return next(new SentriError('UNAUTHORIZED', 'Missing or malformed Authorization header'));
|
|
35
35
|
}
|
|
36
36
|
const token = authHeader.slice(7);
|
|
37
37
|
try {
|
|
@@ -41,7 +41,7 @@ export function protect(config) {
|
|
|
41
41
|
if (payload.sessionId) {
|
|
42
42
|
const session = await config.adapter.session.findById(payload.sessionId);
|
|
43
43
|
if (!session) {
|
|
44
|
-
return next(new
|
|
44
|
+
return next(new SentriError('UNAUTHORIZED', 'Session has been revoked'));
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
next();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protect.js","sourceRoot":"","sources":["../../src/middleware/protect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"protect.js","sourceRoot":"","sources":["../../src/middleware/protect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,OAAO,CAAC,MAAkB;IACxC,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE;QACxC,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QACpD,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACvC,OAAO,IAAI,CAAC,IAAI,WAAW,CAAC,cAAc,EAAE,2CAA2C,CAAC,CAAC,CAAC;QAC5F,CAAC;QACD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAuB,CAAC;YACvE,OAAO,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;YAExF,oFAAoF;YACpF,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;gBACtB,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;gBACzE,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,IAAI,CAAC,IAAI,WAAW,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC,CAAC;gBAC3E,CAAC;YACH,CAAC;YAED,IAAI,EAAE,CAAC;QACT,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/middleware/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAkD,MAAM,SAAS,CAAC;AAEjF,OAAO,KAAK,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/middleware/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAkD,MAAM,SAAS,CAAC;AAEjF,OAAO,KAAK,EAAE,UAAU,EAA6B,MAAM,kBAAkB,CAAC;AA4E9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,SAAS,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,MAAM,CAwLxF"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { signup, login, refresh, logout, logoutAll, assignRoles } from '../services/auth.js';
|
|
2
|
+
import { SentriError } from '../errors/AuthError.js';
|
|
3
|
+
import { register, login, refresh, logout, logoutAll, assignRoles } from '../services/auth.js';
|
|
5
4
|
import { resolveConfig, parseExpiry } from '../libs/config.js';
|
|
6
5
|
import { protect } from './protect.js';
|
|
7
6
|
import { authorize } from './authorize.js';
|
|
@@ -12,18 +11,17 @@ const MIN_PASSWORD_LENGTH = 8;
|
|
|
12
11
|
const MAX_PASSWORD_LENGTH = 72;
|
|
13
12
|
const MAX_IDENTIFIER_LENGTH = 255;
|
|
14
13
|
function badRequest(message) {
|
|
15
|
-
return new
|
|
14
|
+
return new SentriError('VALIDATION_ERROR', message);
|
|
16
15
|
}
|
|
17
16
|
function ok(response, statusCode, message, data) {
|
|
18
17
|
response.status(statusCode).json({ error: false, statusCode, message, data });
|
|
19
18
|
}
|
|
20
19
|
function fail(response, error) {
|
|
21
|
-
|
|
22
|
-
response.status(statusCode).json({ error: true, statusCode, message: error.message, data: null });
|
|
20
|
+
response.status(error.statusCode).json({ error: true, statusCode: error.statusCode, message: error.message, data: null });
|
|
23
21
|
}
|
|
24
22
|
function parseBody(body) {
|
|
25
23
|
if (body === null || body === undefined || typeof body !== 'object' || Array.isArray(body)) {
|
|
26
|
-
throw new
|
|
24
|
+
throw new SentriError('VALIDATION_ERROR', 'Request body is missing or not a JSON object. Did you apply express.json()?');
|
|
27
25
|
}
|
|
28
26
|
return body;
|
|
29
27
|
}
|
|
@@ -58,14 +56,14 @@ function clearCookie(response, config) {
|
|
|
58
56
|
}
|
|
59
57
|
/**
|
|
60
58
|
* Validate the `X-Api-Key` header when `config.apiKey` is set.
|
|
61
|
-
* Throws `
|
|
59
|
+
* Throws `SentriError` with code `UNAUTHORIZED` on mismatch.
|
|
62
60
|
*/
|
|
63
61
|
function validateApiKey(request, config) {
|
|
64
62
|
if (!config.apiKey)
|
|
65
63
|
return;
|
|
66
64
|
const provided = request.headers['x-api-key'];
|
|
67
65
|
if (typeof provided !== 'string' || provided !== config.apiKey) {
|
|
68
|
-
throw new
|
|
66
|
+
throw new SentriError('UNAUTHORIZED', 'Invalid or missing API key');
|
|
69
67
|
}
|
|
70
68
|
}
|
|
71
69
|
/**
|
|
@@ -102,7 +100,7 @@ export function createAuthRouter(config) {
|
|
|
102
100
|
const router = Router();
|
|
103
101
|
// Resolve service functions — use custom override from config.router when provided, else fall back to the built-in service.
|
|
104
102
|
const baseConfig = config;
|
|
105
|
-
const registerFn = config.router?.register ?? ((input) =>
|
|
103
|
+
const registerFn = config.router?.register ?? ((input) => register(input, baseConfig));
|
|
106
104
|
const loginFn = config.router?.login ?? ((input) => login(input, baseConfig));
|
|
107
105
|
const refreshFn = config.router?.refresh ?? ((token) => refresh(token, baseConfig));
|
|
108
106
|
const logoutFn = config.router?.logout ?? ((token) => token !== undefined ? logout(token, baseConfig) : Promise.resolve());
|
|
@@ -186,7 +184,7 @@ export function createAuthRouter(config) {
|
|
|
186
184
|
try {
|
|
187
185
|
const fromCookie = readCookie(request.headers['cookie'], getCookieName(config));
|
|
188
186
|
if (!fromCookie) {
|
|
189
|
-
throw new
|
|
187
|
+
throw new SentriError('UNAUTHORIZED', 'Refresh token cookie is missing');
|
|
190
188
|
}
|
|
191
189
|
const result = await refreshFn(fromCookie);
|
|
192
190
|
if (!result.success) {
|
|
@@ -251,10 +249,10 @@ export function createAuthRouter(config) {
|
|
|
251
249
|
next(error);
|
|
252
250
|
}
|
|
253
251
|
});
|
|
254
|
-
// Centralized error handler — converts
|
|
252
|
+
// Centralized error handler — converts SentriError (and unexpected errors) to the
|
|
255
253
|
// standard envelope so every endpoint produces a consistent shape on failure.
|
|
256
254
|
router.use((error, _request, response, _next) => {
|
|
257
|
-
if (error instanceof
|
|
255
|
+
if (error instanceof SentriError) {
|
|
258
256
|
fail(response, error);
|
|
259
257
|
}
|
|
260
258
|
else {
|