@benefi/auth 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -0,0 +1,171 @@
1
+ @benefi/auth
2
+ ============
3
+
4
+ **Typed JWT verifier with AWS Secrets Manager rotation support.**
5
+
6
+ This package provides a small helper around `jsonwebtoken` and AWS Secrets Manager to verify JWTs that are signed with a secret stored in Secrets Manager, with built‑in support for secret rotation (tries `AWSCURRENT` first, then `AWSPREVIOUS`) and structured error codes.
7
+
8
+ ### Installation
9
+
10
+ ```bash
11
+ npm install @benefi/auth
12
+ # or
13
+ yarn add @benefi/auth
14
+ ```
15
+
16
+ ### When to use this package
17
+
18
+ - **You store your JWT secret in AWS Secrets Manager.**
19
+ - **You rotate the secret using version stages (`AWSCURRENT` / `AWSPREVIOUS`).**
20
+ - **You want a simple verifier function that returns typed success/error results instead of throwing.**
21
+
22
+ ### Quick start
23
+
24
+ ```ts
25
+ import { createTokenVerifier } from "@benefi/auth";
26
+
27
+ // Option 1: pass a Secrets Manager secret ID string
28
+ const verifyToken = createTokenVerifier("my-jwt-secret-id");
29
+
30
+ // Option 2: pass a config object
31
+ // const verifyToken = createTokenVerifier({
32
+ // secretId: "my-jwt-secret-id",
33
+ // region: "us-east-1", // optional, defaults to us-east-1
34
+ // });
35
+
36
+ const result = await verifyToken(tokenFromRequest);
37
+
38
+ if (!result.success) {
39
+ // Use result.error.code to decide response status / message
40
+ // e.g. TOKEN_REQUIRED, TOKEN_EXPIRED, INVALID_TOKEN, SECRET_FETCH_FAILURE
41
+ console.error(result.error);
42
+ // return res.status(401).json({ error: result.error });
43
+ } else {
44
+ // Decoded JWT payload with a stable shape
45
+ const { customerId, iat, exp } = result.data;
46
+ // Attach to request context, etc.
47
+ // req.customerId = customerId;
48
+ }
49
+ ```
50
+
51
+ ### API
52
+
53
+ #### `createTokenVerifier(secretOrConfig: SecretInput): (token: string) => Promise<VerifyResult>`
54
+
55
+ Creates an async verifier function that you can call with a JWT string.
56
+
57
+ - **`secretOrConfig`** (`SecretInput`)
58
+ - `string`: treated as an AWS Secrets Manager `SecretId`, region defaults to `us-east-1`.
59
+ - `{ secretId: string; region?: string }`: explicitly configure the secret id and AWS region.
60
+ - **Return value**: an async function `verify(token: string): Promise<VerifyResult>`.
61
+
62
+ The returned `verify` function:
63
+
64
+ 1. Validates that `token` is a non‑empty string.
65
+ 2. Fetches the secret from AWS Secrets Manager:
66
+ - First with version stage `AWSCURRENT`.
67
+ - If verification fails for any reason other than `TokenExpiredError`, retries with `AWSPREVIOUS`.
68
+ 3. Verifies the token using `jsonwebtoken`.
69
+ 4. Maps the decoded payload into a stable `DecodedJwt` shape:
70
+ - `customerId: string`
71
+ - `iat: number`
72
+ - `exp: number`
73
+ 5. Returns a typed `VerifyResult` instead of throwing.
74
+
75
+ ### Types
76
+
77
+ These types are re‑exported from the main entry point:
78
+
79
+ - **`SecretInput`**
80
+
81
+ ```ts
82
+ type JwtSecretConfig = {
83
+ secretId: string;
84
+ region?: string;
85
+ };
86
+
87
+ type SecretInput = string | JwtSecretConfig;
88
+ ```
89
+
90
+ - **`DecodedJwt`**
91
+
92
+ ```ts
93
+ type DecodedJwt = {
94
+ customerId: string;
95
+ iat: number;
96
+ exp: number;
97
+ };
98
+ ```
99
+
100
+ - **`ErrorCode`**
101
+
102
+ ```ts
103
+ type ErrorCode =
104
+ | "TOKEN_REQUIRED"
105
+ | "TOKEN_EXPIRED"
106
+ | "INVALID_TOKEN"
107
+ | "SECRET_FETCH_FAILURE";
108
+ ```
109
+
110
+ - **`VerifySuccess`**
111
+
112
+ ```ts
113
+ type VerifySuccess = {
114
+ success: true;
115
+ data: DecodedJwt;
116
+ token: string;
117
+ };
118
+ ```
119
+
120
+ - **`VerifyError`**
121
+
122
+ ```ts
123
+ type VerifyError = {
124
+ success: false;
125
+ error: {
126
+ code: ErrorCode;
127
+ message: string;
128
+ };
129
+ };
130
+ ```
131
+
132
+ - **`VerifyResult`**
133
+
134
+ ```ts
135
+ type VerifyResult = VerifySuccess | VerifyError;
136
+ ```
137
+
138
+ You can also import `ERROR_CODES` for convenience:
139
+
140
+ ```ts
141
+ import { ERROR_CODES } from "@benefi/auth";
142
+
143
+ if (!result.success && result.error.code === ERROR_CODES.TOKEN_EXPIRED) {
144
+ // handle expired token branch
145
+ }
146
+ ```
147
+
148
+ ### AWS configuration
149
+
150
+ This package uses `@aws-sdk/client-secrets-manager` under the hood. Make sure your environment is configured so the AWS SDK can authenticate:
151
+
152
+ - **Environment variables** (e.g. `AWS_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`), or
153
+ - **Instance/role credentials** (e.g. EC2 instance profile, ECS task role, Lambda execution role).
154
+
155
+ If no region is provided in `JwtSecretConfig`, the package defaults to `"us-east-1"` (`DEFAULT_AWS_REGION`).
156
+
157
+ ### Error handling
158
+
159
+ The verifier never throws for expected validation failures; instead it returns a `VerifyError`:
160
+
161
+ - **`TOKEN_REQUIRED`** – token is missing, empty, or not a string.
162
+ - **`TOKEN_EXPIRED`** – `jsonwebtoken` reported `TokenExpiredError` while verifying with the current secret.
163
+ - **`INVALID_TOKEN`** – verification failed with both current and previous secrets (bad token, bad signature, wrong algorithm, etc.).
164
+ - **`SECRET_FETCH_FAILURE`** – issues talking to Secrets Manager (e.g. permissions, missing secret, invalid response). The error message will contain the underlying AWS error when available.
165
+
166
+ Use these codes to decide what HTTP status code and response body to send from your API.
167
+
168
+ ### License
169
+
170
+ MIT
171
+
@@ -6,5 +6,5 @@ export declare const ERROR_CODES: {
6
6
  readonly TOKEN_REQUIRED: "TOKEN_REQUIRED";
7
7
  readonly TOKEN_EXPIRED: "TOKEN_EXPIRED";
8
8
  readonly INVALID_TOKEN: "INVALID_TOKEN";
9
- readonly SECRET_ERROR: "SECRET_ERROR";
9
+ readonly SECRET_FETCH_FAILURE: "SECRET_FETCH_FAILURE";
10
10
  };
@@ -9,5 +9,5 @@ exports.ERROR_CODES = {
9
9
  TOKEN_REQUIRED: "TOKEN_REQUIRED",
10
10
  TOKEN_EXPIRED: "TOKEN_EXPIRED",
11
11
  INVALID_TOKEN: "INVALID_TOKEN",
12
- SECRET_ERROR: "SECRET_ERROR",
12
+ SECRET_FETCH_FAILURE: "SECRET_FETCH_FAILURE",
13
13
  };
@@ -9,7 +9,7 @@ export type DecodedJwt = {
9
9
  exp: number;
10
10
  };
11
11
  /** Error code returned when verification fails. */
12
- export type ErrorCode = "TOKEN_REQUIRED" | "TOKEN_EXPIRED" | "INVALID_TOKEN" | "SECRET_ERROR";
12
+ export type ErrorCode = "TOKEN_REQUIRED" | "TOKEN_EXPIRED" | "INVALID_TOKEN" | "SECRET_FETCH_FAILURE";
13
13
  /** Success result: decoded payload + original token. */
14
14
  export type VerifySuccess = {
15
15
  success: true;
@@ -51,7 +51,7 @@ function createTokenVerifier(secretOrConfig) {
51
51
  return {
52
52
  success: false,
53
53
  error: {
54
- code: isAwsError ? "SECRET_ERROR" : "INVALID_TOKEN",
54
+ code: isAwsError ? "SECRET_FETCH_FAILURE" : "INVALID_TOKEN",
55
55
  message: isAwsError
56
56
  ? (err instanceof Error ? err.message : "Failed to fetch secret")
57
57
  : constants_1.TOKEN_INVALID_MESSAGE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@benefi/auth",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {
@@ -8,12 +8,12 @@
8
8
  },
9
9
  "license": "MIT",
10
10
  "dependencies": {
11
- "@aws-sdk/client-secrets-manager": "^3.716.0",
11
+ "@aws-sdk/client-secrets-manager": "^3.978.0",
12
12
  "jsonwebtoken": "^9.0.2"
13
13
  },
14
14
  "devDependencies": {
15
- "@types/express": "^5.0.1",
16
- "@types/jsonwebtoken": "^9.0.9",
17
- "typescript": "^5.8.3"
15
+ "@types/express": "^4.17.21",
16
+ "@types/jsonwebtoken": "^9.0.7",
17
+ "typescript": "^5.5.4"
18
18
  }
19
19
  }
package/src/constants.ts CHANGED
@@ -8,5 +8,5 @@ export const ERROR_CODES = {
8
8
  TOKEN_REQUIRED: "TOKEN_REQUIRED",
9
9
  TOKEN_EXPIRED: "TOKEN_EXPIRED",
10
10
  INVALID_TOKEN: "INVALID_TOKEN",
11
- SECRET_ERROR: "SECRET_ERROR",
11
+ SECRET_FETCH_FAILURE: "SECRET_FETCH_FAILURE",
12
12
  } as const;
package/src/types.ts CHANGED
@@ -18,7 +18,7 @@ export type ErrorCode =
18
18
  | "TOKEN_REQUIRED"
19
19
  | "TOKEN_EXPIRED"
20
20
  | "INVALID_TOKEN"
21
- | "SECRET_ERROR";
21
+ | "SECRET_FETCH_FAILURE";
22
22
 
23
23
  /** Success result: decoded payload + original token. */
24
24
  export type VerifySuccess = {
package/src/verify.ts CHANGED
@@ -60,7 +60,7 @@ export function createTokenVerifier(secretOrConfig: SecretInput) {
60
60
  return {
61
61
  success: false,
62
62
  error: {
63
- code: isAwsError ? "SECRET_ERROR" : "INVALID_TOKEN",
63
+ code: isAwsError ? "SECRET_FETCH_FAILURE" : "INVALID_TOKEN",
64
64
  message: isAwsError
65
65
  ? (err instanceof Error ? err.message : "Failed to fetch secret")
66
66
  : TOKEN_INVALID_MESSAGE,