nextjs-secure 0.7.0 → 0.8.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.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
6
6
  [![Next.js](https://img.shields.io/badge/Next.js-13+-black.svg)](https://nextjs.org/)
7
- [![Tests](https://img.shields.io/badge/tests-568%20passing-brightgreen.svg)]()
7
+ [![Tests](https://img.shields.io/badge/tests-709%20passing-brightgreen.svg)]()
8
8
 
9
9
  Production-ready security middleware for Next.js 13+ App Router. Zero config, maximum protection.
10
10
 
@@ -53,6 +53,7 @@ pnpm add nextjs-secure
53
53
  - [Input Validation](#input-validation)
54
54
  - [Audit Logging](#audit-logging)
55
55
  - [Bot Detection](#bot-detection)
56
+ - [API Security](#api-security)
56
57
  - [Utilities](#utilities)
57
58
  - [API Reference](#api-reference)
58
59
  - [Examples](#examples)
@@ -965,6 +966,210 @@ if (result.isBot) {
965
966
 
966
967
  ---
967
968
 
969
+ ## API Security
970
+
971
+ Protect your APIs with request signing, replay prevention, and versioning.
972
+
973
+ ### Request Signing (HMAC)
974
+
975
+ Sign and verify requests using HMAC to prevent tampering.
976
+
977
+ ```typescript
978
+ import { withRequestSigning, generateSignature } from 'nextjs-secure/api'
979
+
980
+ // Server: Verify signed requests
981
+ export const POST = withRequestSigning(handler, {
982
+ secret: process.env.API_SECRET,
983
+ algorithm: 'sha256', // or 'sha512'
984
+ timestampTolerance: 300, // 5 minutes
985
+ })
986
+
987
+ // Client: Sign outgoing requests
988
+ const timestamp = Math.floor(Date.now() / 1000).toString()
989
+ const signature = await generateSignature(request, {
990
+ secret: API_SECRET,
991
+ algorithm: 'sha256',
992
+ })
993
+
994
+ fetch('/api/data', {
995
+ method: 'POST',
996
+ headers: {
997
+ 'x-timestamp': timestamp,
998
+ 'x-signature': signature,
999
+ },
1000
+ body: JSON.stringify(data),
1001
+ })
1002
+ ```
1003
+
1004
+ ### Custom Signing Components
1005
+
1006
+ ```typescript
1007
+ export const POST = withRequestSigning(handler, {
1008
+ secret: process.env.API_SECRET,
1009
+ components: {
1010
+ method: true,
1011
+ path: true,
1012
+ query: true,
1013
+ body: true,
1014
+ timestamp: true,
1015
+ },
1016
+ signatureHeader: 'x-signature',
1017
+ timestampHeader: 'x-timestamp',
1018
+ })
1019
+ ```
1020
+
1021
+ ### Replay Prevention
1022
+
1023
+ Prevent replay attacks using nonces.
1024
+
1025
+ ```typescript
1026
+ import { withReplayPrevention, MemoryNonceStore, generateNonce } from 'nextjs-secure/api'
1027
+
1028
+ const store = new MemoryNonceStore({ maxSize: 10000, ttl: 300000 })
1029
+
1030
+ // Server: Block replay attacks
1031
+ export const POST = withReplayPrevention(handler, {
1032
+ store,
1033
+ ttl: 300000, // 5 minutes
1034
+ required: true,
1035
+ nonceHeader: 'x-nonce',
1036
+ })
1037
+
1038
+ // Client: Generate unique nonce
1039
+ const nonce = generateNonce(32)
1040
+ fetch('/api/payment', {
1041
+ method: 'POST',
1042
+ headers: { 'x-nonce': nonce },
1043
+ body: JSON.stringify(data),
1044
+ })
1045
+ ```
1046
+
1047
+ ### Timestamp Validation
1048
+
1049
+ Reject old or future-dated requests.
1050
+
1051
+ ```typescript
1052
+ import { withTimestamp, validateTimestamp } from 'nextjs-secure/api'
1053
+
1054
+ export const POST = withTimestamp(handler, {
1055
+ maxAge: 300, // 5 minutes
1056
+ format: 'unix', // 'unix' | 'unix-ms' | 'iso8601'
1057
+ required: true,
1058
+ allowFuture: false,
1059
+ timestampHeader: 'x-timestamp',
1060
+ })
1061
+
1062
+ // Manual validation
1063
+ const result = validateTimestamp(request, { maxAge: 300 })
1064
+ if (!result.valid) {
1065
+ console.log(result.reason) // 'Timestamp too old'
1066
+ }
1067
+ ```
1068
+
1069
+ ### API Versioning
1070
+
1071
+ Manage API versions with deprecation support.
1072
+
1073
+ ```typescript
1074
+ import { withAPIVersion, createVersionRouter, extractVersion } from 'nextjs-secure/api'
1075
+
1076
+ // Single version validation
1077
+ export const GET = withAPIVersion(handler, {
1078
+ current: 'v2',
1079
+ supported: ['v1', 'v2', 'v3'],
1080
+ deprecated: ['v1'],
1081
+ sunset: ['v0'],
1082
+ source: 'header', // 'header' | 'query' | 'path' | 'accept'
1083
+ addDeprecationHeaders: true,
1084
+ })
1085
+
1086
+ // Version-based routing
1087
+ const router = createVersionRouter({
1088
+ v1: v1Handler,
1089
+ v2: v2Handler,
1090
+ v3: v3Handler,
1091
+ }, { default: 'v2' })
1092
+
1093
+ export const GET = router
1094
+ ```
1095
+
1096
+ ### Idempotency Keys
1097
+
1098
+ Ensure safe retries for payment and critical operations.
1099
+
1100
+ ```typescript
1101
+ import { withIdempotency, MemoryIdempotencyStore, generateIdempotencyKey } from 'nextjs-secure/api'
1102
+
1103
+ const store = new MemoryIdempotencyStore({ maxSize: 10000 })
1104
+
1105
+ // Server: Handle idempotent requests
1106
+ export const POST = withIdempotency(handler, {
1107
+ store,
1108
+ ttl: 86400000, // 24 hours
1109
+ required: true,
1110
+ keyHeader: 'idempotency-key',
1111
+ hashRequestBody: true, // Detect body mismatches
1112
+ })
1113
+
1114
+ // Client: Use idempotency key
1115
+ const key = generateIdempotencyKey()
1116
+ fetch('/api/payment', {
1117
+ method: 'POST',
1118
+ headers: { 'idempotency-key': key },
1119
+ body: JSON.stringify(payment),
1120
+ })
1121
+ ```
1122
+
1123
+ ### Combined API Protection
1124
+
1125
+ Use presets for common security profiles.
1126
+
1127
+ ```typescript
1128
+ import { withAPIProtection, withAPIProtectionPreset } from 'nextjs-secure/api'
1129
+
1130
+ // Full configuration
1131
+ export const POST = withAPIProtection(handler, {
1132
+ signing: {
1133
+ secret: process.env.API_SECRET,
1134
+ algorithm: 'sha256',
1135
+ },
1136
+ replay: {
1137
+ store: nonceStore,
1138
+ ttl: 300000,
1139
+ },
1140
+ timestamp: {
1141
+ maxAge: 300,
1142
+ required: true,
1143
+ },
1144
+ versioning: {
1145
+ current: 'v2',
1146
+ supported: ['v1', 'v2'],
1147
+ },
1148
+ idempotency: {
1149
+ store: idempotencyStore,
1150
+ required: true,
1151
+ },
1152
+ })
1153
+
1154
+ // Presets
1155
+ export const POST = withAPIProtectionPreset(handler, 'basic') // Minimal
1156
+ export const POST = withAPIProtectionPreset(handler, 'standard') // Balanced
1157
+ export const POST = withAPIProtectionPreset(handler, 'strict') // Maximum
1158
+ export const POST = withAPIProtectionPreset(handler, 'financial') // Banking/Payment
1159
+ ```
1160
+
1161
+ ### Preset Comparison
1162
+
1163
+ | Feature | Basic | Standard | Strict | Financial |
1164
+ |---------|-------|----------|--------|-----------|
1165
+ | Signing | ❌ | ❌ | SHA-256 | SHA-512 |
1166
+ | Replay Prevention | ❌ | ✅ (5min) | ✅ (5min) | ✅ (24h) |
1167
+ | Timestamp Validation | ✅ (10min) | ✅ (5min) | ✅ (5min) | ✅ (1min) |
1168
+ | Idempotency | ❌ | ❌ | ✅ (1h) | ✅ (24h) |
1169
+ | Versioning | ❌ | ❌ | ✅ | ✅ |
1170
+
1171
+ ---
1172
+
968
1173
  ## Utilities
969
1174
 
970
1175
  ### Duration Parsing
@@ -1087,6 +1292,27 @@ isLocalhost('127.0.0.1') // true
1087
1292
  | `generateHoneypotHTML(config)` | Generate honeypot HTML |
1088
1293
  | `generateHoneypotCSS(config)` | Generate honeypot CSS |
1089
1294
 
1295
+ ### API Security
1296
+
1297
+ | Function | Description |
1298
+ |----------|-------------|
1299
+ | `withRequestSigning(handler, config)` | HMAC request signing |
1300
+ | `withReplayPrevention(handler, config)` | Nonce-based replay prevention |
1301
+ | `withTimestamp(handler, config)` | Timestamp validation |
1302
+ | `withAPIVersion(handler, config)` | API version validation |
1303
+ | `withIdempotency(handler, config)` | Idempotency key support |
1304
+ | `withAPIProtection(handler, config)` | Combined API protection |
1305
+ | `withAPIProtectionPreset(handler, preset)` | Use preset configuration |
1306
+ | `generateSignature(request, config)` | Generate HMAC signature |
1307
+ | `verifySignature(request, config)` | Verify HMAC signature |
1308
+ | `generateNonce(length)` | Generate secure nonce |
1309
+ | `checkReplay(request, config)` | Check for replay attack |
1310
+ | `validateTimestamp(request, config)` | Validate request timestamp |
1311
+ | `extractVersion(request, config)` | Extract API version |
1312
+ | `createVersionRouter(handlers, config)` | Create version-based router |
1313
+ | `generateIdempotencyKey(length)` | Generate idempotency key |
1314
+ | `checkIdempotency(request, config)` | Check idempotency status |
1315
+
1090
1316
  ---
1091
1317
 
1092
1318
  ## Examples
@@ -1192,6 +1418,7 @@ export async function GET(req) {
1192
1418
  - [x] **v0.5.0** - Input Validation
1193
1419
  - [x] **v0.6.0** - Audit Logging
1194
1420
  - [x] **v0.7.0** - Bot Detection
1421
+ - [x] **v0.8.0** - API Security
1195
1422
 
1196
1423
  See [ROADMAP.md](ROADMAP.md) for detailed progress and future plans.
1197
1424