@tozielinski/next-upstash-nonce 1.4.1 → 1.4.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/CHANGELOG.md +7 -0
- package/dist/index.d.ts +13 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.4.2](https://github.com/tozielinski/next-upstash-nonce/compare/v1.4.1...v1.4.2) (2025-12-27)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* add rateLimiter for server action, minor bugfixes ([967269d](https://github.com/tozielinski/next-upstash-nonce/commit/967269d87c12119765e05d150aa9458d730dc53c))
|
|
9
|
+
|
|
3
10
|
## [1.4.1](https://github.com/tozielinski/next-upstash-nonce/compare/v1.4.0...v1.4.1) (2025-12-17)
|
|
4
11
|
|
|
5
12
|
|
package/dist/index.d.ts
CHANGED
|
@@ -22,14 +22,14 @@ export type RateLimitResult = {
|
|
|
22
22
|
ip: string;
|
|
23
23
|
requests: number;
|
|
24
24
|
reason: `too-many-requests: ${number}`;
|
|
25
|
-
response
|
|
25
|
+
response?: Response;
|
|
26
26
|
};
|
|
27
27
|
export declare class NonceManager {
|
|
28
28
|
private redis;
|
|
29
|
-
private ttlNonce;
|
|
30
|
-
private prefix;
|
|
31
|
-
private ttlRateLimit;
|
|
32
|
-
private countRateLimit;
|
|
29
|
+
private readonly ttlNonce;
|
|
30
|
+
private readonly prefix;
|
|
31
|
+
private readonly ttlRateLimit;
|
|
32
|
+
private readonly countRateLimit;
|
|
33
33
|
constructor(redis: Redis, opts?: NonceOptions);
|
|
34
34
|
/**
|
|
35
35
|
* makes option parameters available in the server in environment files
|
|
@@ -39,6 +39,10 @@ export declare class NonceManager {
|
|
|
39
39
|
* extracts client IP from request headers
|
|
40
40
|
*/
|
|
41
41
|
private getClientIp;
|
|
42
|
+
/**
|
|
43
|
+
* extracts client IP from headers for server actions
|
|
44
|
+
*/
|
|
45
|
+
getClientIpFromHeaders(headers: Headers): Promise<string>;
|
|
42
46
|
/**
|
|
43
47
|
* generates a new, secure nonce,
|
|
44
48
|
* inserts it into Redis with a TTL,
|
|
@@ -56,23 +60,24 @@ export declare class NonceManager {
|
|
|
56
60
|
*/
|
|
57
61
|
verifyAndDelete(nonce: string): Promise<boolean>;
|
|
58
62
|
/**
|
|
59
|
-
* verifies
|
|
63
|
+
* verifies nonce from the Header of a request
|
|
60
64
|
* returns a NonceCheckResult if the nonce exists and has not expired.
|
|
61
65
|
*/
|
|
62
66
|
verifyNonceFromRequest(req: Request): Promise<NonceCheckResult>;
|
|
63
67
|
/**
|
|
64
|
-
* verifies
|
|
68
|
+
* verifies nonce from the Header of a request and deletes it from Redis
|
|
65
69
|
* returns a NonceCheckResult if the nonce exists and has not expired.
|
|
66
70
|
*/
|
|
67
71
|
verifyAndDeleteNonceFromRequest(req: Request): Promise<NonceCheckResult>;
|
|
68
72
|
/**
|
|
69
|
-
* Optional: delete
|
|
73
|
+
* Optional: delete nonce from Redis manually
|
|
70
74
|
*/
|
|
71
75
|
delete(nonce: string): Promise<void>;
|
|
72
76
|
/**
|
|
73
77
|
* rate limits requests from the same IP address
|
|
74
78
|
*/
|
|
75
79
|
rateLimiter(req: Request): Promise<RateLimitResult>;
|
|
80
|
+
rateLimiterByIp(ip: string): Promise<RateLimitResult>;
|
|
76
81
|
}
|
|
77
82
|
export default NonceManager;
|
|
78
83
|
//# 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":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,KAAK,EAAC,MAAM,gBAAgB,CAAC;AAGrC,MAAM,MAAM,YAAY,GAAG;IAEvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAK;IAC7B,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,MAAM,CAAA;CAChB,GAAG;IACA,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,gBAAgB,GAAG,oBAAoB,CAAC;IAChD,QAAQ,EAAE,QAAQ,CAAA;CACrB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAK;IAC5B,KAAK,EAAE,IAAI,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;CACpB,GAAG;IACA,KAAK,EAAE,KAAK,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,sBAAsB,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACvB,CAAC;AAEF,qBAAa,YAAY;IACrB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAG5B,KAAK,EAAE,KAAK,EAAE,IAAI,GAAE,YAAiB;IAQjD;;OAEG;IACH,OAAO,CAAC,WAAW;IAWnB;;OAEG;IACH,OAAO,CAAC,WAAW;IAYnB;;OAEG;IACG,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAc/D;;;;OAIG;IACG,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAU/B;;;OAGG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAa7C;;;OAGG;IACG,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAetD;;;OAGG;IACG,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAoCrE;;;OAGG;IACG,+BAA+B,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAoC9E;;OAEG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM1C;;OAEG;IACG,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC;IA0BnD,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;CAsB9D;AAED,eAAe,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -41,6 +41,20 @@ class NonceManager {
|
|
|
41
41
|
}
|
|
42
42
|
return "unknown";
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* extracts client IP from headers for server actions
|
|
46
|
+
*/
|
|
47
|
+
async getClientIpFromHeaders(headers) {
|
|
48
|
+
const forwardedFor = headers.get("x-forwarded-for");
|
|
49
|
+
if (forwardedFor) {
|
|
50
|
+
return forwardedFor.split(",")[0].trim();
|
|
51
|
+
}
|
|
52
|
+
const realIp = headers.get("x-real-ip");
|
|
53
|
+
if (realIp) {
|
|
54
|
+
return realIp;
|
|
55
|
+
}
|
|
56
|
+
return "unknown";
|
|
57
|
+
}
|
|
44
58
|
/**
|
|
45
59
|
* generates a new, secure nonce,
|
|
46
60
|
* inserts it into Redis with a TTL,
|
|
@@ -89,7 +103,7 @@ class NonceManager {
|
|
|
89
103
|
}
|
|
90
104
|
}
|
|
91
105
|
/**
|
|
92
|
-
* verifies
|
|
106
|
+
* verifies nonce from the Header of a request
|
|
93
107
|
* returns a NonceCheckResult if the nonce exists and has not expired.
|
|
94
108
|
*/
|
|
95
109
|
async verifyNonceFromRequest(req) {
|
|
@@ -117,7 +131,7 @@ class NonceManager {
|
|
|
117
131
|
};
|
|
118
132
|
}
|
|
119
133
|
/**
|
|
120
|
-
* verifies
|
|
134
|
+
* verifies nonce from the Header of a request and deletes it from Redis
|
|
121
135
|
* returns a NonceCheckResult if the nonce exists and has not expired.
|
|
122
136
|
*/
|
|
123
137
|
async verifyAndDeleteNonceFromRequest(req) {
|
|
@@ -145,7 +159,7 @@ class NonceManager {
|
|
|
145
159
|
};
|
|
146
160
|
}
|
|
147
161
|
/**
|
|
148
|
-
* Optional: delete
|
|
162
|
+
* Optional: delete nonce from Redis manually
|
|
149
163
|
*/
|
|
150
164
|
async delete(nonce) {
|
|
151
165
|
const key = this.prefix + nonce;
|
|
@@ -163,7 +177,29 @@ class NonceManager {
|
|
|
163
177
|
}
|
|
164
178
|
if (requests > this.getEnvValue("RATE_LIMIT_COUNT", this.countRateLimit)) {
|
|
165
179
|
const response = Response.json({ error: "Too many requests" }, { status: 429 });
|
|
166
|
-
return {
|
|
180
|
+
return {
|
|
181
|
+
valid: false,
|
|
182
|
+
ip,
|
|
183
|
+
requests,
|
|
184
|
+
reason: `too-many-requests: ${requests}`,
|
|
185
|
+
response: response
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
return { valid: true, ip, requests };
|
|
189
|
+
}
|
|
190
|
+
async rateLimiterByIp(ip) {
|
|
191
|
+
const key = `rate:${ip}`;
|
|
192
|
+
const requests = (await this.redis.incr(key)) ?? 0;
|
|
193
|
+
if (requests === 1) {
|
|
194
|
+
await this.redis.expire(key, this.getEnvValue("RATE_LIMIT_TTL_SECONDS", this.ttlRateLimit));
|
|
195
|
+
}
|
|
196
|
+
if (requests > this.getEnvValue("RATE_LIMIT_COUNT", this.countRateLimit)) {
|
|
197
|
+
return {
|
|
198
|
+
valid: false,
|
|
199
|
+
ip,
|
|
200
|
+
requests,
|
|
201
|
+
reason: `too-many-requests: ${requests}`,
|
|
202
|
+
};
|
|
167
203
|
}
|
|
168
204
|
return { valid: true, ip, requests };
|
|
169
205
|
}
|
package/package.json
CHANGED