sentri 1.1.2 → 2.1.0

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.
Files changed (64) hide show
  1. package/README.md +268 -448
  2. package/dist/cli.d.ts +0 -2
  3. package/dist/cli.js +113 -107
  4. package/dist/index.d.ts +545 -11
  5. package/dist/index.js +1 -5
  6. package/package.json +9 -7
  7. package/dist/cli.d.ts.map +0 -1
  8. package/dist/cli.js.map +0 -1
  9. package/dist/client.d.ts +0 -160
  10. package/dist/client.d.ts.map +0 -1
  11. package/dist/client.js +0 -45
  12. package/dist/client.js.map +0 -1
  13. package/dist/errors/AuthError.d.ts +0 -99
  14. package/dist/errors/AuthError.d.ts.map +0 -1
  15. package/dist/errors/AuthError.js +0 -97
  16. package/dist/errors/AuthError.js.map +0 -1
  17. package/dist/index.d.ts.map +0 -1
  18. package/dist/index.js.map +0 -1
  19. package/dist/libs/config.d.ts +0 -62
  20. package/dist/libs/config.d.ts.map +0 -1
  21. package/dist/libs/config.js +0 -97
  22. package/dist/libs/config.js.map +0 -1
  23. package/dist/libs/hash.d.ts +0 -17
  24. package/dist/libs/hash.d.ts.map +0 -1
  25. package/dist/libs/hash.js +0 -22
  26. package/dist/libs/hash.js.map +0 -1
  27. package/dist/libs/token.d.ts +0 -46
  28. package/dist/libs/token.d.ts.map +0 -1
  29. package/dist/libs/token.js +0 -118
  30. package/dist/libs/token.js.map +0 -1
  31. package/dist/middleware/authorize.d.ts +0 -18
  32. package/dist/middleware/authorize.d.ts.map +0 -1
  33. package/dist/middleware/authorize.js +0 -30
  34. package/dist/middleware/authorize.js.map +0 -1
  35. package/dist/middleware/errorHandler.d.ts +0 -71
  36. package/dist/middleware/errorHandler.d.ts.map +0 -1
  37. package/dist/middleware/errorHandler.js +0 -74
  38. package/dist/middleware/errorHandler.js.map +0 -1
  39. package/dist/middleware/permit.d.ts +0 -62
  40. package/dist/middleware/permit.d.ts.map +0 -1
  41. package/dist/middleware/permit.js +0 -61
  42. package/dist/middleware/permit.js.map +0 -1
  43. package/dist/middleware/protect.d.ts +0 -31
  44. package/dist/middleware/protect.d.ts.map +0 -1
  45. package/dist/middleware/protect.js +0 -54
  46. package/dist/middleware/protect.js.map +0 -1
  47. package/dist/middleware/router.d.ts +0 -34
  48. package/dist/middleware/router.d.ts.map +0 -1
  49. package/dist/middleware/router.js +0 -264
  50. package/dist/middleware/router.js.map +0 -1
  51. package/dist/services/auth.d.ts +0 -85
  52. package/dist/services/auth.d.ts.map +0 -1
  53. package/dist/services/auth.js +0 -173
  54. package/dist/services/auth.js.map +0 -1
  55. package/dist/types/auth.d.ts +0 -450
  56. package/dist/types/auth.d.ts.map +0 -1
  57. package/dist/types/auth.js +0 -21
  58. package/dist/types/auth.js.map +0 -1
  59. package/templates/drizzle/adapter.ts +0 -154
  60. package/templates/drizzle/auth.ts +0 -82
  61. package/templates/drizzle/schema.ts +0 -47
  62. package/templates/prisma/adapter.ts +0 -122
  63. package/templates/prisma/auth.ts +0 -85
  64. package/templates/prisma/schema.prisma +0 -56
@@ -1,46 +0,0 @@
1
- import type { AccessTokenPayload, AuthConfig, AuthUser } from '../types/auth.js';
2
- /**
3
- * Sign a short-lived access token containing the user's identity, roles, and
4
- * the session ID that `protect()` uses to verify the session is still active.
5
- *
6
- * Uses the access-specific secret derived from config and the configured
7
- * `accessExpiresIn` duration and HMAC algorithm.
8
- *
9
- * @param payload - The {@link AccessTokenPayload} to embed (id, identifier, roles, sessionId).
10
- * @param config - Auth configuration used to derive the secret and options.
11
- * @returns Compact JWT string.
12
- */
13
- export declare function signAccessToken(payload: AuthUser | AccessTokenPayload, config: AuthConfig): string;
14
- /**
15
- * Sign a long-lived refresh token bound to a specific session ID.
16
- *
17
- * Uses the refresh-specific secret derived from config and the configured
18
- * `refreshExpiresIn` duration. The session ID is embedded as the sole claim
19
- * so the token can be used to look up and rotate the session.
20
- *
21
- * @param sessionId - The database session ID to bind to this token.
22
- * @param config - Auth configuration used to derive the secret and options.
23
- * @returns Compact JWT string.
24
- */
25
- export declare function signRefreshToken(sessionId: string, config: AuthConfig): string;
26
- /**
27
- * Verify an access token and return its decoded {@link AuthUser} payload.
28
- *
29
- * @param token - Compact JWT access token string.
30
- * @param config - Auth configuration used to derive the secret and algorithm.
31
- * @returns Decoded `AuthUser` payload (id, identifier, roles).
32
- * @throws {SentriError} With `TOKEN_EXPIRED` if expired, `TOKEN_INVALID` otherwise.
33
- */
34
- export declare function verifyAccessToken(token: string, config: AuthConfig): AuthUser;
35
- /**
36
- * Verify a refresh token and return the embedded session ID.
37
- *
38
- * @param token - Compact JWT refresh token string.
39
- * @param config - Auth configuration used to derive the secret and algorithm.
40
- * @returns Object with `sessionId` matching the one passed to {@link signRefreshToken}.
41
- * @throws {SentriError} With `TOKEN_EXPIRED` if expired, `TOKEN_INVALID` otherwise.
42
- */
43
- export declare function verifyRefreshToken(token: string, config: AuthConfig): {
44
- sessionId: string;
45
- };
46
- //# sourceMappingURL=token.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/libs/token.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAqEjF;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,QAAQ,GAAG,kBAAkB,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAIlG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAI9E;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,QAAQ,CAI7E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAI3F"}
@@ -1,118 +0,0 @@
1
- import jwt, {} from 'jsonwebtoken';
2
- import { SentriError } from '../errors/AuthError.js';
3
- import { resolveConfig } from './config.js';
4
- /**
5
- * Derive separate HMAC secrets for access and refresh tokens from a single
6
- * root secret by appending a domain suffix.
7
- *
8
- * Using distinct secrets prevents a refresh token from being accepted as an
9
- * access token and vice versa.
10
- */
11
- function deriveSecrets(secret) {
12
- return {
13
- access: `${secret}:access`,
14
- refresh: `${secret}:refresh`,
15
- };
16
- }
17
- /**
18
- * Sign a JWT payload with the given secret and options.
19
- *
20
- * @param payload - Claims to embed in the token.
21
- * @param secret - HMAC signing key.
22
- * @param expiresIn - Expiry duration string (e.g. `'15m'`) or seconds.
23
- * @param algorithm - HMAC algorithm variant.
24
- * @returns Compact JWT string.
25
- */
26
- function sign(payload, secret, expiresIn, algorithm) {
27
- const options = {
28
- expiresIn: expiresIn,
29
- algorithm,
30
- };
31
- return jwt.sign(payload, secret, options);
32
- }
33
- /**
34
- * Verify and decode a JWT, mapping jsonwebtoken errors to typed {@link SentriError}s.
35
- *
36
- * @param token - Compact JWT string to verify.
37
- * @param secret - HMAC key used to sign the token.
38
- * @param algorithm - Expected signing algorithm.
39
- * @returns Decoded payload cast to `T`.
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
- */
43
- function verify(token, secret, algorithm) {
44
- try {
45
- const decoded = jwt.verify(token, secret, { algorithms: [algorithm] });
46
- if (typeof decoded === 'string' || decoded === null) {
47
- throw new SentriError('TOKEN_INVALID', 'Token payload is not an object');
48
- }
49
- return decoded;
50
- }
51
- catch (err) {
52
- if (err instanceof SentriError)
53
- throw err;
54
- if (err instanceof jwt.TokenExpiredError) {
55
- throw new SentriError('TOKEN_EXPIRED', 'Token has expired');
56
- }
57
- throw new SentriError('TOKEN_INVALID', 'Token is invalid or malformed');
58
- }
59
- }
60
- /**
61
- * Sign a short-lived access token containing the user's identity, roles, and
62
- * the session ID that `protect()` uses to verify the session is still active.
63
- *
64
- * Uses the access-specific secret derived from config and the configured
65
- * `accessExpiresIn` duration and HMAC algorithm.
66
- *
67
- * @param payload - The {@link AccessTokenPayload} to embed (id, identifier, roles, sessionId).
68
- * @param config - Auth configuration used to derive the secret and options.
69
- * @returns Compact JWT string.
70
- */
71
- export function signAccessToken(payload, config) {
72
- const resolved = resolveConfig(config);
73
- const { access } = deriveSecrets(resolved.secret);
74
- return sign(payload, access, resolved.accessExpiresIn, resolved.algorithm);
75
- }
76
- /**
77
- * Sign a long-lived refresh token bound to a specific session ID.
78
- *
79
- * Uses the refresh-specific secret derived from config and the configured
80
- * `refreshExpiresIn` duration. The session ID is embedded as the sole claim
81
- * so the token can be used to look up and rotate the session.
82
- *
83
- * @param sessionId - The database session ID to bind to this token.
84
- * @param config - Auth configuration used to derive the secret and options.
85
- * @returns Compact JWT string.
86
- */
87
- export function signRefreshToken(sessionId, config) {
88
- const resolved = resolveConfig(config);
89
- const { refresh } = deriveSecrets(resolved.secret);
90
- return sign({ sessionId }, refresh, resolved.refreshExpiresIn, resolved.algorithm);
91
- }
92
- /**
93
- * Verify an access token and return its decoded {@link AuthUser} payload.
94
- *
95
- * @param token - Compact JWT access token string.
96
- * @param config - Auth configuration used to derive the secret and algorithm.
97
- * @returns Decoded `AuthUser` payload (id, identifier, roles).
98
- * @throws {SentriError} With `TOKEN_EXPIRED` if expired, `TOKEN_INVALID` otherwise.
99
- */
100
- export function verifyAccessToken(token, config) {
101
- const resolved = resolveConfig(config);
102
- const { access } = deriveSecrets(resolved.secret);
103
- return verify(token, access, resolved.algorithm);
104
- }
105
- /**
106
- * Verify a refresh token and return the embedded session ID.
107
- *
108
- * @param token - Compact JWT refresh token string.
109
- * @param config - Auth configuration used to derive the secret and algorithm.
110
- * @returns Object with `sessionId` matching the one passed to {@link signRefreshToken}.
111
- * @throws {SentriError} With `TOKEN_EXPIRED` if expired, `TOKEN_INVALID` otherwise.
112
- */
113
- export function verifyRefreshToken(token, config) {
114
- const resolved = resolveConfig(config);
115
- const { refresh } = deriveSecrets(resolved.secret);
116
- return verify(token, refresh, resolved.algorithm);
117
- }
118
- //# sourceMappingURL=token.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/libs/token.ts"],"names":[],"mappings":"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"}
@@ -1,18 +0,0 @@
1
- import type { RequestHandler } from 'express';
2
- /**
3
- * Express middleware factory for role-based access control (RBAC).
4
- *
5
- * Passes if the authenticated user (set by `protect()`) has **at least one**
6
- * of the specified `allowedRoles`. Calls `next(SentriError)` with code `FORBIDDEN`
7
- * if no roles match, or `UNAUTHORIZED` if `req.user` is absent.
8
- *
9
- * Must be used **after** `protect()`.
10
- *
11
- * @param allowedRoles - One or more role strings. The user needs at least one of them.
12
- * @returns An Express `RequestHandler` that enforces the role check.
13
- *
14
- * @example
15
- * router.delete('/posts/:id', protect(config), authorize('admin', 'moderator'), handler);
16
- */
17
- export declare function authorize<TRole extends string>(...allowedRoles: TRole[]): RequestHandler;
18
- //# sourceMappingURL=authorize.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"authorize.d.ts","sourceRoot":"","sources":["../../src/middleware/authorize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,KAAK,SAAS,MAAM,EAAE,GAAG,YAAY,EAAE,KAAK,EAAE,GAAG,cAAc,CAcxF"}
@@ -1,30 +0,0 @@
1
- import { SentriError } from '../errors/AuthError.js';
2
- /**
3
- * Express middleware factory for role-based access control (RBAC).
4
- *
5
- * Passes if the authenticated user (set by `protect()`) has **at least one**
6
- * of the specified `allowedRoles`. Calls `next(SentriError)` with code `FORBIDDEN`
7
- * if no roles match, or `UNAUTHORIZED` if `req.user` is absent.
8
- *
9
- * Must be used **after** `protect()`.
10
- *
11
- * @param allowedRoles - One or more role strings. The user needs at least one of them.
12
- * @returns An Express `RequestHandler` that enforces the role check.
13
- *
14
- * @example
15
- * router.delete('/posts/:id', protect(config), authorize('admin', 'moderator'), handler);
16
- */
17
- export function authorize(...allowedRoles) {
18
- return (request, _response, next) => {
19
- if (!request.user) {
20
- return next(new SentriError('UNAUTHORIZED', 'Not authenticated'));
21
- }
22
- const userRoles = request.user.roles;
23
- const hasRole = allowedRoles.some((role) => userRoles.includes(role));
24
- if (!hasRole) {
25
- return next(new SentriError('FORBIDDEN', `Requires one of roles: ${allowedRoles.join(', ')}`));
26
- }
27
- next();
28
- };
29
- }
30
- //# sourceMappingURL=authorize.js.map
@@ -1 +0,0 @@
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"}
@@ -1,71 +0,0 @@
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
@@ -1 +0,0 @@
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"}
@@ -1,74 +0,0 @@
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
@@ -1 +0,0 @@
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"}
@@ -1,62 +0,0 @@
1
- import type { Request, RequestHandler } from 'express';
2
- /** A function that determines whether the current request is permitted. */
3
- export type PermitCheck = (request: Request) => boolean | Promise<boolean>;
4
- /**
5
- * Options for {@link permit} when you need role-bypass alongside a resource check.
6
- *
7
- * @example
8
- * // Admins can edit any post; others only their own
9
- * auth.permit({
10
- * roles: ['admin'],
11
- * check: async (request) => {
12
- * const post = await db.findPost(request.params['id']);
13
- * return post?.authorId === request.user!.id;
14
- * },
15
- * })
16
- */
17
- export interface PermitOptions<TRole extends string> {
18
- /**
19
- * Roles whose members are granted access without running `check`.
20
- * Use for privileged roles like `'admin'` that should bypass ownership checks.
21
- */
22
- roles?: TRole[];
23
- /**
24
- * Permission check executed when the user has none of the bypass `roles`.
25
- * Return `true` to allow, `false` to deny with `FORBIDDEN`.
26
- * May be async — useful for database-backed ownership checks.
27
- */
28
- check: PermitCheck;
29
- }
30
- /**
31
- * Express middleware factory for resource-level permission checks.
32
- *
33
- * Must be used **after** `protect()`. Evaluates a check function against the
34
- * current request; calls `next(SentriError)` with code `FORBIDDEN` if it returns `false`.
35
- *
36
- * Accepts either a bare check function or an options object with an optional
37
- * `roles` list whose members bypass the check entirely.
38
- *
39
- * @example
40
- * // Simple ownership check
41
- * router.put('/users/:id',
42
- * auth.protect(),
43
- * auth.permit((request) => request.user!.id === request.params['id']),
44
- * updateUserHandler,
45
- * );
46
- *
47
- * @example
48
- * // Admins bypass the check; others must own the post
49
- * router.delete('/posts/:id',
50
- * auth.protect(),
51
- * auth.permit({
52
- * roles: ['admin'],
53
- * check: async (request) => {
54
- * const post = await db.post.findUnique({ where: { id: request.params['id'] } });
55
- * return post?.authorId === request.user!.id;
56
- * },
57
- * }),
58
- * deletePostHandler,
59
- * );
60
- */
61
- export declare function permit<TRole extends string>(optionsOrCheck: PermitOptions<TRole> | PermitCheck): RequestHandler;
62
- //# sourceMappingURL=permit.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"permit.d.ts","sourceRoot":"","sources":["../../src/middleware/permit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGvD,2EAA2E;AAC3E,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAE3E;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,aAAa,CAAC,KAAK,SAAS,MAAM;IACjD;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC;IAChB;;;;OAIG;IACH,KAAK,EAAE,WAAW,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,MAAM,CAAC,KAAK,SAAS,MAAM,EACzC,cAAc,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,WAAW,GACjD,cAAc,CA4BhB"}
@@ -1,61 +0,0 @@
1
- import { SentriError } from '../errors/AuthError.js';
2
- /**
3
- * Express middleware factory for resource-level permission checks.
4
- *
5
- * Must be used **after** `protect()`. Evaluates a check function against the
6
- * current request; calls `next(SentriError)` with code `FORBIDDEN` if it returns `false`.
7
- *
8
- * Accepts either a bare check function or an options object with an optional
9
- * `roles` list whose members bypass the check entirely.
10
- *
11
- * @example
12
- * // Simple ownership check
13
- * router.put('/users/:id',
14
- * auth.protect(),
15
- * auth.permit((request) => request.user!.id === request.params['id']),
16
- * updateUserHandler,
17
- * );
18
- *
19
- * @example
20
- * // Admins bypass the check; others must own the post
21
- * router.delete('/posts/:id',
22
- * auth.protect(),
23
- * auth.permit({
24
- * roles: ['admin'],
25
- * check: async (request) => {
26
- * const post = await db.post.findUnique({ where: { id: request.params['id'] } });
27
- * return post?.authorId === request.user!.id;
28
- * },
29
- * }),
30
- * deletePostHandler,
31
- * );
32
- */
33
- export function permit(optionsOrCheck) {
34
- const options = typeof optionsOrCheck === 'function'
35
- ? { check: optionsOrCheck }
36
- : optionsOrCheck;
37
- return async (request, _response, next) => {
38
- if (!request.user) {
39
- return next(new SentriError('UNAUTHORIZED', 'Not authenticated'));
40
- }
41
- if (options.roles && options.roles.length > 0) {
42
- const userRoles = request.user.roles;
43
- const hasBypassRole = options.roles.some((role) => userRoles.includes(role));
44
- if (hasBypassRole)
45
- return next();
46
- }
47
- try {
48
- const allowed = await options.check(request);
49
- if (allowed) {
50
- next();
51
- }
52
- else {
53
- next(new SentriError('FORBIDDEN', 'You do not have permission to perform this action'));
54
- }
55
- }
56
- catch (error) {
57
- next(error);
58
- }
59
- };
60
- }
61
- //# sourceMappingURL=permit.js.map
@@ -1 +0,0 @@
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"}
@@ -1,31 +0,0 @@
1
- import type { RequestHandler } from 'express';
2
- import type { AuthConfig } from '../types/auth.js';
3
- /**
4
- * Express middleware factory that enforces JWT authentication and session validity.
5
- *
6
- * Reads the `Authorization: Bearer <token>` header, verifies the access token,
7
- * and attaches the decoded payload to `req.user`. Calls `next(SentriError)` on
8
- * any failure so your error handler can convert it to an HTTP response.
9
- *
10
- * Since sentri 1.1.0 access tokens embed a `sessionId` claim. When this claim
11
- * is present, `protect()` performs a lightweight database lookup
12
- * (`adapter.session.findById`) to confirm the session is still active. This
13
- * means a user who has logged out (or been logged out from all devices) cannot
14
- * use an access token that was issued before the logout — even if the token has
15
- * not yet expired.
16
- *
17
- * Tokens issued before 1.1.0 (without the `sessionId` claim) are still accepted
18
- * but bypass the session check.
19
- *
20
- * Must be used **before** `authorize()` or `permit()`.
21
- *
22
- * @param config - Auth configuration (secret, algorithm, adapter).
23
- * @returns An Express `RequestHandler` that populates `req.user` on success.
24
- *
25
- * @example
26
- * router.get('/profile', protect(config), (request, response) => {
27
- * response.json(request.user);
28
- * });
29
- */
30
- export declare function protect(config: AuthConfig): RequestHandler;
31
- //# sourceMappingURL=protect.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"protect.d.ts","sourceRoot":"","sources":["../../src/middleware/protect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,OAAO,KAAK,EAAsB,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,cAAc,CAwB1D"}
@@ -1,54 +0,0 @@
1
- import { SentriError } from '../errors/AuthError.js';
2
- import { verifyAccessToken } from '../libs/token.js';
3
- /**
4
- * Express middleware factory that enforces JWT authentication and session validity.
5
- *
6
- * Reads the `Authorization: Bearer <token>` header, verifies the access token,
7
- * and attaches the decoded payload to `req.user`. Calls `next(SentriError)` on
8
- * any failure so your error handler can convert it to an HTTP response.
9
- *
10
- * Since sentri 1.1.0 access tokens embed a `sessionId` claim. When this claim
11
- * is present, `protect()` performs a lightweight database lookup
12
- * (`adapter.session.findById`) to confirm the session is still active. This
13
- * means a user who has logged out (or been logged out from all devices) cannot
14
- * use an access token that was issued before the logout — even if the token has
15
- * not yet expired.
16
- *
17
- * Tokens issued before 1.1.0 (without the `sessionId` claim) are still accepted
18
- * but bypass the session check.
19
- *
20
- * Must be used **before** `authorize()` or `permit()`.
21
- *
22
- * @param config - Auth configuration (secret, algorithm, adapter).
23
- * @returns An Express `RequestHandler` that populates `req.user` on success.
24
- *
25
- * @example
26
- * router.get('/profile', protect(config), (request, response) => {
27
- * response.json(request.user);
28
- * });
29
- */
30
- export function protect(config) {
31
- return async (request, _response, next) => {
32
- const authHeader = request.headers['authorization'];
33
- if (!authHeader?.startsWith('Bearer ')) {
34
- return next(new SentriError('UNAUTHORIZED', 'Missing or malformed Authorization header'));
35
- }
36
- const token = authHeader.slice(7);
37
- try {
38
- const payload = verifyAccessToken(token, config);
39
- request.user = { id: payload.id, identifier: payload.identifier, roles: payload.roles };
40
- // Session-bound validation: reject the request if the session was revoked (logout).
41
- if (payload.sessionId) {
42
- const session = await config.adapter.session.findById(payload.sessionId);
43
- if (!session) {
44
- return next(new SentriError('UNAUTHORIZED', 'Session has been revoked'));
45
- }
46
- }
47
- next();
48
- }
49
- catch (error) {
50
- next(error);
51
- }
52
- };
53
- }
54
- //# sourceMappingURL=protect.js.map
@@ -1 +0,0 @@
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,34 +0,0 @@
1
- import { Router } from 'express';
2
- import type { AuthConfig } from '../types/auth.js';
3
- /**
4
- * Creates a pre-built Express Router with all standard auth endpoints.
5
- *
6
- * Mount it once and all routes are ready:
7
- *
8
- * ```
9
- * POST /register — register a new user (protected by X-Api-Key when config.apiKey is set)
10
- * POST /login — authenticate and get tokens
11
- * POST /refresh — rotate refresh token
12
- * POST /logout — invalidate current session; access tokens issued before logout become invalid
13
- * POST /logout-all — invalidate all sessions for the authenticated user
14
- * GET /me — return the currently authenticated user
15
- * POST /users/:userId/roles — assign roles (admin only)
16
- * ```
17
- *
18
- * Requires `express.json()` to be applied before the router.
19
- *
20
- * When `cookie` is set in config, the refresh token is stored in an httpOnly
21
- * cookie automatically — no `cookie-parser` needed.
22
- *
23
- * When `apiKey` is set in config, `POST /register` requires the caller to send
24
- * an `X-Api-Key: <key>` header matching the configured value.
25
- *
26
- * When `router` is set in config, individual service functions can be replaced
27
- * while the router still handles validation and response formatting.
28
- *
29
- * @example
30
- * app.use(express.json());
31
- * app.use('/auth', auth.router());
32
- */
33
- export declare function createAuthRouter<TRole extends string>(config: AuthConfig<TRole>): Router;
34
- //# sourceMappingURL=router.d.ts.map
@@ -1 +0,0 @@
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"}