@benefi/auth 1.0.4

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 ADDED
File without changes
@@ -0,0 +1,3 @@
1
+ export { createTokenVerifier } from "./src/verify";
2
+ export { ERROR_CODES } from "./src/constants";
3
+ export type { VerifyResult, VerifySuccess, VerifyError, DecodedJwt, SecretInput, JwtSecretConfig, ErrorCode, } from "./src/types";
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ERROR_CODES = exports.createTokenVerifier = void 0;
4
+ var verify_1 = require("./src/verify");
5
+ Object.defineProperty(exports, "createTokenVerifier", { enumerable: true, get: function () { return verify_1.createTokenVerifier; } });
6
+ var constants_1 = require("./src/constants");
7
+ Object.defineProperty(exports, "ERROR_CODES", { enumerable: true, get: function () { return constants_1.ERROR_CODES; } });
@@ -0,0 +1,9 @@
1
+ import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
2
+ import type { SecretInput } from "./types";
3
+ export type { JwtSecretConfig, SecretInput } from "./types";
4
+ export declare function normalizeSecretConfig(secretOrConfig: SecretInput): {
5
+ secretId: string;
6
+ region: string;
7
+ };
8
+ export declare function createSecretsManagerClient(region: string): SecretsManagerClient;
9
+ export declare function getSecret(client: SecretsManagerClient, secretId: string, versionStage: "AWSCURRENT" | "AWSPREVIOUS"): Promise<string>;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeSecretConfig = normalizeSecretConfig;
4
+ exports.createSecretsManagerClient = createSecretsManagerClient;
5
+ exports.getSecret = getSecret;
6
+ const client_secrets_manager_1 = require("@aws-sdk/client-secrets-manager");
7
+ const constants_1 = require("./constants");
8
+ function normalizeSecretConfig(secretOrConfig) {
9
+ if (typeof secretOrConfig === "string") {
10
+ return { secretId: secretOrConfig, region: constants_1.DEFAULT_AWS_REGION };
11
+ }
12
+ return {
13
+ secretId: secretOrConfig.secretId,
14
+ region: secretOrConfig.region ?? constants_1.DEFAULT_AWS_REGION,
15
+ };
16
+ }
17
+ function createSecretsManagerClient(region) {
18
+ return new client_secrets_manager_1.SecretsManagerClient({ region });
19
+ }
20
+ async function getSecret(client, secretId, versionStage) {
21
+ const command = new client_secrets_manager_1.GetSecretValueCommand({
22
+ SecretId: secretId,
23
+ VersionStage: versionStage,
24
+ });
25
+ const response = await client.send(command);
26
+ if (!response.SecretString) {
27
+ throw new Error(`JWT secret "${secretId}" has empty SecretString for ${versionStage}`);
28
+ }
29
+ return response.SecretString;
30
+ }
@@ -0,0 +1,10 @@
1
+ export declare const DEFAULT_AWS_REGION = "us-east-1";
2
+ export declare const TOKEN_REQUIRED_MESSAGE = "Token is required";
3
+ export declare const TOKEN_EXPIRED_MESSAGE = "Token has expired";
4
+ export declare const TOKEN_INVALID_MESSAGE = "Invalid token";
5
+ export declare const ERROR_CODES: {
6
+ readonly TOKEN_REQUIRED: "TOKEN_REQUIRED";
7
+ readonly TOKEN_EXPIRED: "TOKEN_EXPIRED";
8
+ readonly INVALID_TOKEN: "INVALID_TOKEN";
9
+ readonly SECRET_ERROR: "SECRET_ERROR";
10
+ };
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ERROR_CODES = exports.TOKEN_INVALID_MESSAGE = exports.TOKEN_EXPIRED_MESSAGE = exports.TOKEN_REQUIRED_MESSAGE = exports.DEFAULT_AWS_REGION = void 0;
4
+ exports.DEFAULT_AWS_REGION = "us-east-1";
5
+ exports.TOKEN_REQUIRED_MESSAGE = "Token is required";
6
+ exports.TOKEN_EXPIRED_MESSAGE = "Token has expired";
7
+ exports.TOKEN_INVALID_MESSAGE = "Invalid token";
8
+ exports.ERROR_CODES = {
9
+ TOKEN_REQUIRED: "TOKEN_REQUIRED",
10
+ TOKEN_EXPIRED: "TOKEN_EXPIRED",
11
+ INVALID_TOKEN: "INVALID_TOKEN",
12
+ SECRET_ERROR: "SECRET_ERROR",
13
+ };
@@ -0,0 +1,2 @@
1
+ import { Request, Response, NextFunction } from "express";
2
+ export declare function tokenValidationMiddleware(secret: string): (req: Request, res: Response, next: NextFunction) => Response<any, Record<string, any>> | undefined;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.tokenValidationMiddleware = tokenValidationMiddleware;
37
+ const jsonwebtoken_1 = __importStar(require("jsonwebtoken"));
38
+ function tokenValidationMiddleware(secret) {
39
+ return function (req, res, next) {
40
+ try {
41
+ const token = req.header("Authorization")?.replace(/^Bearer\s+/, "") ||
42
+ req.cookies.accessToken;
43
+ if (!token) {
44
+ return res
45
+ .status(401)
46
+ .json({ data: {}, error: { message: "Token is required" } });
47
+ }
48
+ const decoded = jsonwebtoken_1.default.verify(token, secret);
49
+ req.body.customerId = decoded.customerId;
50
+ next();
51
+ }
52
+ catch (err) {
53
+ if (err instanceof jsonwebtoken_1.TokenExpiredError) {
54
+ return res
55
+ .status(401)
56
+ .json({ data: {}, error: { message: "Token has expired" } });
57
+ }
58
+ return res
59
+ .status(401)
60
+ .json({ data: {}, error: { message: "Invalid token" } });
61
+ }
62
+ };
63
+ }
@@ -0,0 +1,10 @@
1
+ import { Request, Response, NextFunction } from "express";
2
+ import { NestMiddleware } from "@nestjs/common";
3
+ import { SecretInput } from "./aws";
4
+ export declare class TokenValidationMiddleware implements NestMiddleware {
5
+ private readonly verifyToken;
6
+ constructor(secretOrConfig: SecretInput);
7
+ private handle;
8
+ use(req: Request, res: Response, next: NextFunction): void;
9
+ express(): (req: Request, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
10
+ }
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
3
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
4
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
5
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
6
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
7
+ var _, done = false;
8
+ for (var i = decorators.length - 1; i >= 0; i--) {
9
+ var context = {};
10
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
11
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
12
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
13
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
14
+ if (kind === "accessor") {
15
+ if (result === void 0) continue;
16
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
17
+ if (_ = accept(result.get)) descriptor.get = _;
18
+ if (_ = accept(result.set)) descriptor.set = _;
19
+ if (_ = accept(result.init)) initializers.unshift(_);
20
+ }
21
+ else if (_ = accept(result)) {
22
+ if (kind === "field") initializers.unshift(_);
23
+ else descriptor[key] = _;
24
+ }
25
+ }
26
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
27
+ done = true;
28
+ };
29
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
30
+ var useValue = arguments.length > 2;
31
+ for (var i = 0; i < initializers.length; i++) {
32
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
33
+ }
34
+ return useValue ? value : void 0;
35
+ };
36
+ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
37
+ if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
38
+ return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
39
+ };
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.TokenValidationMiddleware = void 0;
42
+ const common_1 = require("@nestjs/common");
43
+ const jsonwebtoken_1 = require("jsonwebtoken");
44
+ const constants_1 = require("./constants");
45
+ const validation_1 = require("./validation");
46
+ let TokenValidationMiddleware = (() => {
47
+ let _classDecorators = [(0, common_1.Injectable)()];
48
+ let _classDescriptor;
49
+ let _classExtraInitializers = [];
50
+ let _classThis;
51
+ var TokenValidationMiddleware = _classThis = class {
52
+ constructor(secretOrConfig) {
53
+ this.verifyToken = (0, validation_1.createJwtVerifier)(secretOrConfig);
54
+ }
55
+ async handle(req, res, next) {
56
+ try {
57
+ const token = req.header("Authorization")?.replace(/^Bearer\s+/, "") ||
58
+ req.cookies.accessToken;
59
+ if (!token) {
60
+ return res
61
+ .status(401)
62
+ .json({ data: {}, error: { message: constants_1.TOKEN_REQUIRED_MESSAGE } });
63
+ }
64
+ const decoded = await this.verifyToken(token);
65
+ req.body.customerId = decoded.customerId;
66
+ return next();
67
+ }
68
+ catch (err) {
69
+ if (err instanceof jsonwebtoken_1.TokenExpiredError) {
70
+ return res
71
+ .status(401)
72
+ .json({ data: {}, error: { message: constants_1.TOKEN_EXPIRED_MESSAGE } });
73
+ }
74
+ return res
75
+ .status(401)
76
+ .json({ data: {}, error: { message: constants_1.TOKEN_INVALID_MESSAGE } });
77
+ }
78
+ }
79
+ // NestJS calls this
80
+ use(req, res, next) {
81
+ void this.handle(req, res, next);
82
+ }
83
+ express() {
84
+ return this.handle.bind(this);
85
+ }
86
+ };
87
+ __setFunctionName(_classThis, "TokenValidationMiddleware");
88
+ (() => {
89
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
90
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
91
+ TokenValidationMiddleware = _classThis = _classDescriptor.value;
92
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
93
+ __runInitializers(_classThis, _classExtraInitializers);
94
+ })();
95
+ return TokenValidationMiddleware = _classThis;
96
+ })();
97
+ exports.TokenValidationMiddleware = TokenValidationMiddleware;
@@ -0,0 +1,7 @@
1
+ import { NestMiddleware } from "@nestjs/common";
2
+ import { Request, Response, NextFunction } from "express";
3
+ export declare class TokenValidationMiddleware implements NestMiddleware {
4
+ private secret;
5
+ constructor(secret: string);
6
+ use(req: Request, res: Response, next: NextFunction): Response<any, Record<string, any>> | undefined;
7
+ }
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
19
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
20
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
21
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
22
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
23
+ var _, done = false;
24
+ for (var i = decorators.length - 1; i >= 0; i--) {
25
+ var context = {};
26
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
27
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
28
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
29
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
30
+ if (kind === "accessor") {
31
+ if (result === void 0) continue;
32
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
33
+ if (_ = accept(result.get)) descriptor.get = _;
34
+ if (_ = accept(result.set)) descriptor.set = _;
35
+ if (_ = accept(result.init)) initializers.unshift(_);
36
+ }
37
+ else if (_ = accept(result)) {
38
+ if (kind === "field") initializers.unshift(_);
39
+ else descriptor[key] = _;
40
+ }
41
+ }
42
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
43
+ done = true;
44
+ };
45
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
46
+ var useValue = arguments.length > 2;
47
+ for (var i = 0; i < initializers.length; i++) {
48
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
49
+ }
50
+ return useValue ? value : void 0;
51
+ };
52
+ var __importStar = (this && this.__importStar) || (function () {
53
+ var ownKeys = function(o) {
54
+ ownKeys = Object.getOwnPropertyNames || function (o) {
55
+ var ar = [];
56
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
57
+ return ar;
58
+ };
59
+ return ownKeys(o);
60
+ };
61
+ return function (mod) {
62
+ if (mod && mod.__esModule) return mod;
63
+ var result = {};
64
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
65
+ __setModuleDefault(result, mod);
66
+ return result;
67
+ };
68
+ })();
69
+ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
70
+ if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
71
+ return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
72
+ };
73
+ Object.defineProperty(exports, "__esModule", { value: true });
74
+ exports.TokenValidationMiddleware = void 0;
75
+ const common_1 = require("@nestjs/common");
76
+ const jsonwebtoken_1 = __importStar(require("jsonwebtoken"));
77
+ let TokenValidationMiddleware = (() => {
78
+ let _classDecorators = [(0, common_1.Injectable)()];
79
+ let _classDescriptor;
80
+ let _classExtraInitializers = [];
81
+ let _classThis;
82
+ var TokenValidationMiddleware = _classThis = class {
83
+ constructor(secret) {
84
+ this.secret = secret;
85
+ }
86
+ use(req, res, next) {
87
+ try {
88
+ const token = req.header("Authorization")?.replace(/^Bearer\s+/, "") ||
89
+ req.cookies.accessToken;
90
+ if (!token) {
91
+ return res
92
+ .status(401)
93
+ .json({ data: {}, error: { message: "Token is required" } });
94
+ }
95
+ const decoded = jsonwebtoken_1.default.verify(token, this.secret);
96
+ req.body.customerId = decoded.customerId;
97
+ next();
98
+ }
99
+ catch (err) {
100
+ if (err instanceof jsonwebtoken_1.TokenExpiredError) {
101
+ return res
102
+ .status(401)
103
+ .json({ data: {}, error: { message: "Token has expired" } });
104
+ }
105
+ return res
106
+ .status(401)
107
+ .json({ data: {}, error: { message: "Invalid token" } });
108
+ }
109
+ }
110
+ };
111
+ __setFunctionName(_classThis, "TokenValidationMiddleware");
112
+ (() => {
113
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
114
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
115
+ TokenValidationMiddleware = _classThis = _classDescriptor.value;
116
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
117
+ __runInitializers(_classThis, _classExtraInitializers);
118
+ })();
119
+ return TokenValidationMiddleware = _classThis;
120
+ })();
121
+ exports.TokenValidationMiddleware = TokenValidationMiddleware;
@@ -0,0 +1,26 @@
1
+ export type JwtSecretConfig = {
2
+ secretId: string;
3
+ region?: string;
4
+ };
5
+ export type SecretInput = string | JwtSecretConfig;
6
+ export type DecodedJwt = {
7
+ customerId: string;
8
+ iat: number;
9
+ exp: number;
10
+ };
11
+ /** Error code returned when verification fails. */
12
+ export type ErrorCode = "TOKEN_REQUIRED" | "TOKEN_EXPIRED" | "INVALID_TOKEN" | "SECRET_ERROR";
13
+ /** Success result: decoded payload + original token. */
14
+ export type VerifySuccess = {
15
+ success: true;
16
+ data: DecodedJwt;
17
+ token: string;
18
+ };
19
+ export type VerifyError = {
20
+ success: false;
21
+ error: {
22
+ code: ErrorCode;
23
+ message: string;
24
+ };
25
+ };
26
+ export type VerifyResult = VerifySuccess | VerifyError;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ import { TokenExpiredError } from "jsonwebtoken";
2
+ import type { DecodedJwt, SecretInput } from "./types";
3
+ export type { DecodedJwt } from "./types";
4
+ export { TokenExpiredError };
5
+ export declare function createJwtVerifier(secretOrConfig: SecretInput): (token: string) => Promise<DecodedJwt>;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.TokenExpiredError = void 0;
37
+ exports.createJwtVerifier = createJwtVerifier;
38
+ const jsonwebtoken_1 = __importStar(require("jsonwebtoken"));
39
+ Object.defineProperty(exports, "TokenExpiredError", { enumerable: true, get: function () { return jsonwebtoken_1.TokenExpiredError; } });
40
+ const aws_1 = require("./aws");
41
+ function toDecodedJwt(payload) {
42
+ return {
43
+ customerId: String(payload.customerId ?? ""),
44
+ iat: Number(payload.iat ?? 0),
45
+ exp: Number(payload.exp ?? 0),
46
+ };
47
+ }
48
+ function createJwtVerifier(secretOrConfig) {
49
+ const { secretId, region } = (0, aws_1.normalizeSecretConfig)(secretOrConfig);
50
+ const client = (0, aws_1.createSecretsManagerClient)(region);
51
+ return async function verifyTokenWithRotation(token) {
52
+ // 1) Try latest version (AWSCURRENT)
53
+ const currentSecret = await (0, aws_1.getSecret)(client, secretId, "AWSCURRENT");
54
+ try {
55
+ const payload = jsonwebtoken_1.default.verify(token, currentSecret);
56
+ return toDecodedJwt(payload);
57
+ }
58
+ catch (err) {
59
+ // If token is expired with the current secret, surface that directly.
60
+ if (err instanceof jsonwebtoken_1.TokenExpiredError) {
61
+ throw err;
62
+ }
63
+ // 2) Any other verification error with current → try previous version
64
+ const previousSecret = await (0, aws_1.getSecret)(client, secretId, "AWSPREVIOUS");
65
+ const payload = jsonwebtoken_1.default.verify(token, previousSecret);
66
+ return toDecodedJwt(payload);
67
+ }
68
+ };
69
+ }
@@ -0,0 +1,15 @@
1
+ import type { SecretInput, VerifyResult } from "./types";
2
+ export type { VerifyResult, VerifySuccess, VerifyError } from "./types";
3
+ /**
4
+ * Creates a verifier that takes a token and returns either the decoded payload
5
+ * or a structured error. You handle 401/other status codes in your app.
6
+ *
7
+ * @example
8
+ * const verifier = createTokenVerifier({ secretId: process.env.DEV_JWT_SECRET!, region: "us-east-1" });
9
+ * const result = await verifier(token);
10
+ * if (!result.success) {
11
+ * return res.status(401).json({ error: result.error });
12
+ * }
13
+ * req.customerId = result.data.customerId;
14
+ */
15
+ export declare function createTokenVerifier(secretOrConfig: SecretInput): (token: string) => Promise<VerifyResult>;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createTokenVerifier = createTokenVerifier;
4
+ const jsonwebtoken_1 = require("jsonwebtoken");
5
+ const constants_1 = require("./constants");
6
+ const validation_1 = require("./validation");
7
+ /**
8
+ * Creates a verifier that takes a token and returns either the decoded payload
9
+ * or a structured error. You handle 401/other status codes in your app.
10
+ *
11
+ * @example
12
+ * const verifier = createTokenVerifier({ secretId: process.env.DEV_JWT_SECRET!, region: "us-east-1" });
13
+ * const result = await verifier(token);
14
+ * if (!result.success) {
15
+ * return res.status(401).json({ error: result.error });
16
+ * }
17
+ * req.customerId = result.data.customerId;
18
+ */
19
+ function createTokenVerifier(secretOrConfig) {
20
+ const verifyToken = (0, validation_1.createJwtVerifier)(secretOrConfig);
21
+ return async function verify(token) {
22
+ if (!token || typeof token !== "string" || !token.trim()) {
23
+ return {
24
+ success: false,
25
+ error: {
26
+ code: "TOKEN_REQUIRED",
27
+ message: constants_1.TOKEN_REQUIRED_MESSAGE,
28
+ },
29
+ };
30
+ }
31
+ const trimmedToken = token.trim();
32
+ try {
33
+ const data = await verifyToken(trimmedToken);
34
+ return { success: true, data, token: trimmedToken };
35
+ }
36
+ catch (err) {
37
+ if (err instanceof jsonwebtoken_1.TokenExpiredError) {
38
+ return {
39
+ success: false,
40
+ error: {
41
+ code: "TOKEN_EXPIRED",
42
+ message: constants_1.TOKEN_EXPIRED_MESSAGE,
43
+ },
44
+ };
45
+ }
46
+ // AWS or other verification failure
47
+ const isAwsError = err &&
48
+ typeof err === "object" &&
49
+ "name" in err &&
50
+ err.name?.startsWith("SecretsManager");
51
+ return {
52
+ success: false,
53
+ error: {
54
+ code: isAwsError ? "SECRET_ERROR" : "INVALID_TOKEN",
55
+ message: isAwsError
56
+ ? (err instanceof Error ? err.message : "Failed to fetch secret")
57
+ : constants_1.TOKEN_INVALID_MESSAGE,
58
+ },
59
+ };
60
+ }
61
+ };
62
+ }
package/index.ts ADDED
@@ -0,0 +1,12 @@
1
+ export { createTokenVerifier } from "./src/verify";
2
+ export { ERROR_CODES } from "./src/constants";
3
+
4
+ export type {
5
+ VerifyResult,
6
+ VerifySuccess,
7
+ VerifyError,
8
+ DecodedJwt,
9
+ SecretInput,
10
+ JwtSecretConfig,
11
+ ErrorCode,
12
+ } from "./src/types";
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "@benefi/auth",
3
+ "version": "1.0.4",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "scripts": {
7
+ "build": "tsc"
8
+ },
9
+ "license": "MIT",
10
+ "dependencies": {
11
+ "@aws-sdk/client-secrets-manager": "^3.716.0",
12
+ "jsonwebtoken": "^9.0.2"
13
+ },
14
+ "devDependencies": {
15
+ "@types/express": "^5.0.1",
16
+ "@types/jsonwebtoken": "^9.0.9",
17
+ "typescript": "^5.8.3"
18
+ }
19
+ }
package/src/aws.ts ADDED
@@ -0,0 +1,42 @@
1
+ import {
2
+ SecretsManagerClient,
3
+ GetSecretValueCommand,
4
+ } from "@aws-sdk/client-secrets-manager";
5
+ import { DEFAULT_AWS_REGION } from "./constants";
6
+ import type { SecretInput } from "./types";
7
+
8
+ export type { JwtSecretConfig, SecretInput } from "./types";
9
+
10
+ export function normalizeSecretConfig(
11
+ secretOrConfig: SecretInput
12
+ ): { secretId: string; region: string } {
13
+ if (typeof secretOrConfig === "string") {
14
+ return { secretId: secretOrConfig, region: DEFAULT_AWS_REGION };
15
+ }
16
+ return {
17
+ secretId: secretOrConfig.secretId,
18
+ region: secretOrConfig.region ?? DEFAULT_AWS_REGION,
19
+ };
20
+ }
21
+
22
+ export function createSecretsManagerClient(region: string) {
23
+ return new SecretsManagerClient({ region });
24
+ }
25
+
26
+ export async function getSecret(
27
+ client: SecretsManagerClient,
28
+ secretId: string,
29
+ versionStage: "AWSCURRENT" | "AWSPREVIOUS"
30
+ ): Promise<string> {
31
+ const command = new GetSecretValueCommand({
32
+ SecretId: secretId,
33
+ VersionStage: versionStage,
34
+ });
35
+ const response = await client.send(command);
36
+ if (!response.SecretString) {
37
+ throw new Error(
38
+ `JWT secret "${secretId}" has empty SecretString for ${versionStage}`
39
+ );
40
+ }
41
+ return response.SecretString;
42
+ }
@@ -0,0 +1,12 @@
1
+ export const DEFAULT_AWS_REGION = "us-east-1";
2
+
3
+ export const TOKEN_REQUIRED_MESSAGE = "Token is required";
4
+ export const TOKEN_EXPIRED_MESSAGE = "Token has expired";
5
+ export const TOKEN_INVALID_MESSAGE = "Invalid token";
6
+
7
+ export const ERROR_CODES = {
8
+ TOKEN_REQUIRED: "TOKEN_REQUIRED",
9
+ TOKEN_EXPIRED: "TOKEN_EXPIRED",
10
+ INVALID_TOKEN: "INVALID_TOKEN",
11
+ SECRET_ERROR: "SECRET_ERROR",
12
+ } as const;
package/src/types.ts ADDED
@@ -0,0 +1,38 @@
1
+ export type JwtSecretConfig = {
2
+ secretId: string;
3
+ region?: string;
4
+ };
5
+
6
+
7
+ export type SecretInput = string | JwtSecretConfig;
8
+
9
+
10
+ export type DecodedJwt = {
11
+ customerId: string;
12
+ iat: number;
13
+ exp: number;
14
+ };
15
+
16
+ /** Error code returned when verification fails. */
17
+ export type ErrorCode =
18
+ | "TOKEN_REQUIRED"
19
+ | "TOKEN_EXPIRED"
20
+ | "INVALID_TOKEN"
21
+ | "SECRET_ERROR";
22
+
23
+ /** Success result: decoded payload + original token. */
24
+ export type VerifySuccess = {
25
+ success: true;
26
+ data: DecodedJwt;
27
+ token: string;
28
+ };
29
+
30
+ export type VerifyError = {
31
+ success: false;
32
+ error: {
33
+ code: ErrorCode;
34
+ message: string;
35
+ };
36
+ };
37
+
38
+ export type VerifyResult = VerifySuccess | VerifyError;
@@ -0,0 +1,46 @@
1
+ import jwt, { TokenExpiredError } from "jsonwebtoken";
2
+ import {
3
+ normalizeSecretConfig,
4
+ createSecretsManagerClient,
5
+ getSecret,
6
+ } from "./aws";
7
+ import type { DecodedJwt, SecretInput } from "./types";
8
+
9
+ export type { DecodedJwt } from "./types";
10
+
11
+ function toDecodedJwt(payload: Record<string, unknown>): DecodedJwt {
12
+ return {
13
+ customerId: String(payload.customerId ?? ""),
14
+ iat: Number(payload.iat ?? 0),
15
+ exp: Number(payload.exp ?? 0),
16
+ };
17
+ }
18
+
19
+ export { TokenExpiredError };
20
+
21
+ export function createJwtVerifier(secretOrConfig: SecretInput) {
22
+ const { secretId, region } = normalizeSecretConfig(secretOrConfig);
23
+ const client = createSecretsManagerClient(region);
24
+
25
+ return async function verifyTokenWithRotation(
26
+ token: string
27
+ ): Promise<DecodedJwt> {
28
+ // 1) Try latest version (AWSCURRENT)
29
+ const currentSecret = await getSecret(client, secretId, "AWSCURRENT");
30
+
31
+ try {
32
+ const payload = jwt.verify(token, currentSecret) as Record<string, unknown>;
33
+ return toDecodedJwt(payload);
34
+ } catch (err) {
35
+ // If token is expired with the current secret, surface that directly.
36
+ if (err instanceof TokenExpiredError) {
37
+ throw err;
38
+ }
39
+
40
+ // 2) Any other verification error with current → try previous version
41
+ const previousSecret = await getSecret(client, secretId, "AWSPREVIOUS");
42
+ const payload = jwt.verify(token, previousSecret) as Record<string, unknown>;
43
+ return toDecodedJwt(payload);
44
+ }
45
+ };
46
+ }
package/src/verify.ts ADDED
@@ -0,0 +1,71 @@
1
+ import { TokenExpiredError } from "jsonwebtoken";
2
+ import {
3
+ TOKEN_REQUIRED_MESSAGE,
4
+ TOKEN_EXPIRED_MESSAGE,
5
+ TOKEN_INVALID_MESSAGE,
6
+ } from "./constants";
7
+ import type { SecretInput, VerifyResult } from "./types";
8
+ import { createJwtVerifier } from "./validation";
9
+
10
+ export type { VerifyResult, VerifySuccess, VerifyError } from "./types";
11
+
12
+ /**
13
+ * Creates a verifier that takes a token and returns either the decoded payload
14
+ * or a structured error. You handle 401/other status codes in your app.
15
+ *
16
+ * @example
17
+ * const verifier = createTokenVerifier({ secretId: process.env.DEV_JWT_SECRET!, region: "us-east-1" });
18
+ * const result = await verifier(token);
19
+ * if (!result.success) {
20
+ * return res.status(401).json({ error: result.error });
21
+ * }
22
+ * req.customerId = result.data.customerId;
23
+ */
24
+ export function createTokenVerifier(secretOrConfig: SecretInput) {
25
+ const verifyToken = createJwtVerifier(secretOrConfig);
26
+
27
+ return async function verify(token: string): Promise<VerifyResult> {
28
+ if (!token || typeof token !== "string" || !token.trim()) {
29
+ return {
30
+ success: false,
31
+ error: {
32
+ code: "TOKEN_REQUIRED",
33
+ message: TOKEN_REQUIRED_MESSAGE,
34
+ },
35
+ };
36
+ }
37
+
38
+ const trimmedToken = token.trim();
39
+ try {
40
+ const data = await verifyToken(trimmedToken);
41
+ return { success: true, data, token: trimmedToken };
42
+ } catch (err) {
43
+ if (err instanceof TokenExpiredError) {
44
+ return {
45
+ success: false,
46
+ error: {
47
+ code: "TOKEN_EXPIRED",
48
+ message: TOKEN_EXPIRED_MESSAGE,
49
+ },
50
+ };
51
+ }
52
+
53
+ // AWS or other verification failure
54
+ const isAwsError =
55
+ err &&
56
+ typeof err === "object" &&
57
+ "name" in err &&
58
+ (err as { name?: string }).name?.startsWith("SecretsManager");
59
+
60
+ return {
61
+ success: false,
62
+ error: {
63
+ code: isAwsError ? "SECRET_ERROR" : "INVALID_TOKEN",
64
+ message: isAwsError
65
+ ? (err instanceof Error ? err.message : "Failed to fetch secret")
66
+ : TOKEN_INVALID_MESSAGE,
67
+ },
68
+ };
69
+ }
70
+ };
71
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "CommonJS",
5
+ "declaration": true,
6
+ "outDir": "./dist",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "moduleResolution": "node",
10
+ "forceConsistentCasingInFileNames": true,
11
+ "skipLibCheck": true
12
+ },
13
+ "include": ["src/**/*", "index.ts"]
14
+ }