requestshield 0.1.4 → 0.1.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 +421 -85
- package/config/.env.prod +7 -0
- package/package.json +21 -12
- package/skills/requestshield/SKILL.md +299 -307
- package/skills/requestshield/assets/AGENTS.codex.md +62 -62
- package/skills/requestshield/references/backend-java-core.md +128 -128
- package/skills/requestshield/references/backend-spring-boot.md +145 -145
- package/skills/requestshield/references/browser-manual.md +210 -210
- package/skills/requestshield/references/browser-seamless.md +156 -164
- package/skills/requestshield/references/cli.md +107 -182
- package/skills/requestshield/references/integration-planning.md +362 -389
- package/skills/requestshield/references/troubleshooting.md +114 -118
- package/src/agent-detector.mjs +102 -74
- package/src/api-client.mjs +115 -79
- package/src/args.mjs +140 -80
- package/src/browser-opener.mjs +32 -0
- package/src/cli.mjs +277 -51
- package/src/commands/agent-setup.mjs +182 -185
- package/src/commands/application-mutations.mjs +33 -0
- package/src/commands/application-response.mjs +55 -0
- package/src/commands/apps-get.mjs +20 -0
- package/src/commands/apps-list.mjs +94 -0
- package/src/commands/auth-status.mjs +37 -0
- package/src/commands/keys-create.mjs +7 -38
- package/src/commands/mutation-support.mjs +110 -0
- package/src/commands/secret-commands.mjs +45 -0
- package/src/commands/signin.mjs +70 -57
- package/src/commands/signout.mjs +9 -0
- package/src/commands/update-check.mjs +12 -4
- package/src/config.mjs +150 -0
- package/src/entrypoint.mjs +24 -0
- package/src/errors.mjs +3 -1
- package/src/main.mjs +5 -24
- package/src/oauth-client.mjs +153 -0
- package/src/oauth-loopback.mjs +120 -0
- package/src/session-files.mjs +213 -0
- package/src/session-store.mjs +177 -64
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
## RequestShield integration
|
|
3
|
-
|
|
4
|
-
This repository uses IntelliFend RequestShield to protect selected operations from
|
|
5
|
-
automated abuse. Before installing, verifying, changing, or removing that protection —
|
|
6
|
-
and before hand-editing any file that already contains a RequestShield script tag,
|
|
7
|
-
`data-protect`, `X-IntelliFend-Token`, `IntelliFend.getToken()`, `verify()`, or
|
|
8
|
-
`@RequestShieldProtected` — read
|
|
9
|
-
|
|
10
|
-
The procedure exists because the failure mode here is silent. Every integration has two
|
|
11
|
-
halves: the browser obtains and transports a token, and the backend evaluates it before
|
|
12
|
-
the protected operation. They are configured **independently and neither implies the
|
|
13
|
-
other** — a page that obtains tokens whose backend never verifies them has zero
|
|
14
|
-
protection and looks fully integrated, and an annotated handler creates no browser
|
|
15
|
-
configuration.
|
|
16
|
-
|
|
17
|
-
The backend path is decided by the backend stack, not by browser preference: the
|
|
18
|
-
**Spring Boot 3 starter** needs Spring Boot 3 with Spring MVC and Java 17+, while the
|
|
19
|
-
**Java core SDK** needs Java 17+. The browser mode is a separate transport choice:
|
|
20
|
-
Seamless needs a released Seamless mode and a protected request sent by `fetch` or
|
|
21
|
-
asynchronous XHR; Manual is for explicit token timing or other supported carriers.
|
|
22
|
-
Check both halves before proposing edits — finding a backend blocker afterwards leaves
|
|
23
|
-
tokens flowing with nothing enforcing them. Java below 17 blocks both backend paths.
|
|
24
|
-
|
|
25
|
-
Orientation:
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
credentials check, detecting an existing integration, gating on the backend stack
|
|
30
|
-
(Java version, Spring MVC vs WebFlux, other frameworks), and choosing the browser mode.
|
|
31
|
-
Read it before any install, verify, or uninstall.
|
|
32
|
-
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
`
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
1
|
+
<!-- Optional repository guidance template. Agent setup installs the personal skill; it does not write this file. -->
|
|
2
|
+
## RequestShield integration
|
|
3
|
+
|
|
4
|
+
This repository uses IntelliFend RequestShield to protect selected operations from
|
|
5
|
+
automated abuse. Before installing, verifying, changing, or removing that protection —
|
|
6
|
+
and before hand-editing any file that already contains a RequestShield script tag,
|
|
7
|
+
`data-protect`, `X-IntelliFend-Token`, `IntelliFend.getToken()`, `verify()`, or
|
|
8
|
+
`@RequestShieldProtected` — read `~/.agents/skills/requestshield/SKILL.md` and follow it.
|
|
9
|
+
|
|
10
|
+
The procedure exists because the failure mode here is silent. Every integration has two
|
|
11
|
+
halves: the browser obtains and transports a token, and the backend evaluates it before
|
|
12
|
+
the protected operation. They are configured **independently and neither implies the
|
|
13
|
+
other** — a page that obtains tokens whose backend never verifies them has zero
|
|
14
|
+
protection and looks fully integrated, and an annotated handler creates no browser
|
|
15
|
+
configuration.
|
|
16
|
+
|
|
17
|
+
The backend path is decided by the backend stack, not by browser preference: the
|
|
18
|
+
**Spring Boot 3 starter** needs Spring Boot 3 with Spring MVC and Java 17+, while the
|
|
19
|
+
**Java core SDK** needs Java 17+. The browser mode is a separate transport choice:
|
|
20
|
+
Seamless needs a released Seamless mode and a protected request sent by `fetch` or
|
|
21
|
+
asynchronous XHR; Manual is for explicit token timing or other supported carriers.
|
|
22
|
+
Check both halves before proposing edits — finding a backend blocker afterwards leaves
|
|
23
|
+
tokens flowing with nothing enforcing them. Java below 17 blocks both backend paths.
|
|
24
|
+
|
|
25
|
+
Orientation:
|
|
26
|
+
|
|
27
|
+
- `~/.agents/skills/requestshield/SKILL.md` — the procedure. Start here.
|
|
28
|
+
- `~/.agents/skills/requestshield/references/integration-planning.md` — the planning flow:
|
|
29
|
+
credentials check, detecting an existing integration, gating on the backend stack
|
|
30
|
+
(Java version, Spring MVC vs WebFlux, other frameworks), and choosing the browser mode.
|
|
31
|
+
Read it before any install, verify, or uninstall.
|
|
32
|
+
- `~/.agents/skills/requestshield/references/cli.md` — supported sign-in, app and secret
|
|
33
|
+
commands. Contract, service-health, credential-status, traffic and billing commands
|
|
34
|
+
are unavailable; use customer release docs and authorized runtime observations.
|
|
35
|
+
- `~/.agents/skills/requestshield/references/browser-seamless.md` — Seamless mode: exact-endpoint
|
|
36
|
+
matching, and the transports it does not cover.
|
|
37
|
+
- `~/.agents/skills/requestshield/references/browser-manual.md` — Manual mode, carriers, CORS, CSP.
|
|
38
|
+
- `~/.agents/skills/requestshield/references/backend-spring-boot.md` — enforcement on Spring Boot 3
|
|
39
|
+
MVC: the `@RequestShieldProtected` annotation path.
|
|
40
|
+
- `~/.agents/skills/requestshield/references/backend-java-core.md` — enforcement on any other
|
|
41
|
+
Java 17 backend: the `verify()` path.
|
|
42
|
+
- `~/.agents/skills/requestshield/references/troubleshooting.md` — reason codes and diagnosis.
|
|
43
|
+
|
|
44
|
+
Five rules worth carrying without opening a file:
|
|
45
|
+
|
|
46
|
+
- The **API Secret** never enters source control, logs, or a reply. It belongs in backend
|
|
47
|
+
runtime secret storage, and `requestshield keys rotate <app-key>` is the fix if it leaks. The
|
|
48
|
+
**App Key** is public and must be identical in browser and backend.
|
|
49
|
+
- **Keep secret output out of tool transcripts.** Have the user run create, rotate
|
|
50
|
+
and reveal in their own terminal, then have them
|
|
51
|
+
export `INTELLIFEND_REQUESTSHIELD_APP_KEY` and
|
|
52
|
+
`INTELLIFEND_REQUESTSHIELD_API_SECRET`, and confirm by presence only.
|
|
53
|
+
- Every protected request needs its **own token, verified exactly once**. Reuse or a
|
|
54
|
+
second verification path returns `token_replayed` and blocks a legitimate request.
|
|
55
|
+
- **Enforcement lives in the backend.** Never put allow/block/retry logic in the browser,
|
|
56
|
+
and never build a decision table from reason codes in application code.
|
|
57
|
+
- In Manual mode, attach `X-IntelliFend-Token` **only when `getToken()` returns a
|
|
58
|
+
non-empty string**; when it is empty, omit the header and let the backend apply its
|
|
59
|
+
configured policy.
|
|
60
|
+
|
|
61
|
+
Consult customer documentation for the chosen SDK release before changing the script
|
|
62
|
+
URL, header name, released modes or backend baseline. The contract CLI is unavailable.
|
|
@@ -1,128 +1,128 @@
|
|
|
1
|
-
# Backend SDK — Java core SDK
|
|
2
|
-
|
|
3
|
-
The backend is where protection actually happens. Everything the browser does is
|
|
4
|
-
transport to deliver a token here; this file is where the request is allowed or refused.
|
|
5
|
-
|
|
6
|
-
This is one of two backend paths. It is framework-neutral: the SDK returns a decision and
|
|
7
|
-
**the application enforces it**. The other path is the declarative Spring Boot 3 starter
|
|
8
|
-
→ `backend-spring-boot.md`; prefer that one when the stack allows it, because it removes
|
|
9
|
-
the enforcement code below and the mistakes that come with it.
|
|
10
|
-
|
|
11
|
-
**Requires Java 17 or newer** — nothing else. So this is the path for every backend that
|
|
12
|
-
is not Spring Boot 3 MVC, including a Spring Boot 3 app built on WebFlux, which is not
|
|
13
|
-
Spring MVC. Also needed: the same App Key the browser uses, the backend-only API Secret,
|
|
14
|
-
and a web framework that can extract the token carrier.
|
|
15
|
-
|
|
16
|
-
Confirm the baseline and supported languages
|
|
17
|
-
|
|
18
|
-
not
|
|
19
|
-
|
|
20
|
-
The browser mode does not decide this path. Either Seamless or Manual mode delivers the
|
|
21
|
-
token, and the application extracts it and passes the value unchanged to `verify()`.
|
|
22
|
-
|
|
23
|
-
Use exactly **one** verification path per request. A RequestShield token is used once, so
|
|
24
|
-
a handler already covered by `@RequestShieldProtected` must not also call `verify()`.
|
|
25
|
-
|
|
26
|
-
## 1. Dependency
|
|
27
|
-
|
|
28
|
-
```xml title="pom.xml"
|
|
29
|
-
<repositories>
|
|
30
|
-
<repository>
|
|
31
|
-
<id>intellifend-maven</id>
|
|
32
|
-
<url>https://sdk.intellifend.com/packages/maven</url>
|
|
33
|
-
</repository>
|
|
34
|
-
</repositories>
|
|
35
|
-
|
|
36
|
-
<dependency>
|
|
37
|
-
<groupId>com.intellifend.requestshield</groupId>
|
|
38
|
-
<artifactId>requestshield-backend-sdk</artifactId>
|
|
39
|
-
<version>2.0.0</version>
|
|
40
|
-
</dependency>
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## 2. One reusable client
|
|
44
|
-
|
|
45
|
-
```java
|
|
46
|
-
RequestShieldClient requestShield = RequestShieldClient.builder()
|
|
47
|
-
.appKey(appKey)
|
|
48
|
-
.apiSecret(apiSecret)
|
|
49
|
-
.build();
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
One client per App Key, reused across requests, rather than one per request. Keep the
|
|
53
|
-
API secret in backend runtime secret storage.
|
|
54
|
-
|
|
55
|
-
Bind the secret with no fallback default. A backend that starts with a placeholder
|
|
56
|
-
secret rejects every real token, which surfaces days later as "RequestShield is blocking
|
|
57
|
-
all our users" — a startup failure is far cheaper to diagnose than that.
|
|
58
|
-
|
|
59
|
-
## 3. Verify before the protected operation
|
|
60
|
-
|
|
61
|
-
```java
|
|
62
|
-
String token = request.getHeader("X-IntelliFend-Token");
|
|
63
|
-
VerificationResult result = requestShield.verify(token);
|
|
64
|
-
|
|
65
|
-
if (!result.isAllowed()) {
|
|
66
|
-
rejectRequest(result.getReason().getCode());
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
continueProtectedOperation();
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Pass the extracted value unchanged. Do not trim, decode, or pre-validate the shape;
|
|
74
|
-
`verify()` owns that, and pre-parsing only adds a way to reject a valid token.If the
|
|
75
|
-
header is absent, `null`, or empty, pass that value to `verify()` as-is. Do not add a
|
|
76
|
-
separate decision based on token presence.
|
|
77
|
-
|
|
78
|
-
Always enforce `isAllowed()`. Use `getReason().getCode()` for controlled diagnostics or
|
|
79
|
-
a sanitized response contract only — **do not build a separate decision table in
|
|
80
|
-
application code.** Reason codes explain a decision; they do not amend it, and an
|
|
81
|
-
override that allows a blocked reason quietly removes the protection.
|
|
82
|
-
|
|
83
|
-
If the application contract carries the token in a request-body field instead of the
|
|
84
|
-
header, extract that field before verification; everything after that is identical.
|
|
85
|
-
|
|
86
|
-
## 4. Validate the request path
|
|
87
|
-
|
|
88
|
-
1. Browser and backend use the same App Key.
|
|
89
|
-
2. A token reaches the backend unchanged.
|
|
90
|
-
3. The protected operation runs only after `isAllowed()` is checked.
|
|
91
|
-
4. A new browser token is obtained before each application retry.
|
|
92
|
-
5. Tokens and the API secret are excluded from logs.
|
|
93
|
-
|
|
94
|
-
## VerificationResult
|
|
95
|
-
|
|
96
|
-
| Method | Returns | Use |
|
|
97
|
-
| --- | --- | --- |
|
|
98
|
-
| `isAllowed()` | `boolean` | The decision to enforce. Never override it based on a reason code. |
|
|
99
|
-
| `getReason()` | `VerificationReason` | Stable reason; `getReason().getCode()` is safe to log and chart. |
|
|
100
|
-
| `isDegraded()` | `boolean` | The decision was made under a platform availability failure rather than from a real token. |
|
|
101
|
-
|
|
102
|
-
`isDegraded()` deserves attention on first integration. Confirm the exact behaviour in
|
|
103
|
-
the customer documentation before relying on it, and tell the user which behaviour you
|
|
104
|
-
assumed. The documented intent is that a degraded result reflects a platform
|
|
105
|
-
availability failure rather than a real token decision — so a degraded result is not
|
|
106
|
-
evidence that a token was valid. Log the degraded count as its own metric; a persistent nonzero rate is an
|
|
107
|
-
infrastructure signal, not a bot signal.
|
|
108
|
-
|
|
109
|
-
Reason codes and what each means for debugging: `troubleshooting.md`.
|
|
110
|
-
|
|
111
|
-
## Align the browser side
|
|
112
|
-
|
|
113
|
-
Configure exactly one browser mode for the same operation — Seamless
|
|
114
|
-
(`browser-seamless.md`) or Manual (`browser-manual.md`). Browser configuration does not
|
|
115
|
-
create backend protection, and a `verify()` call does not create browser configuration.
|
|
116
|
-
Land both halves in the same change. For a cross-origin API, configure CORS to allow the
|
|
117
|
-
token header.
|
|
118
|
-
|
|
119
|
-
## Security requirements
|
|
120
|
-
|
|
121
|
-
- The API Secret lives in backend runtime secret storage only — never in source, a
|
|
122
|
-
committed `.env`, an image layer, a client-visible response, or a log line.
|
|
123
|
-
- The App Key must be identical in browser and backend, or every token fails.
|
|
124
|
-
- One token per protected request, verified exactly once.
|
|
125
|
-
- Never log or persist raw tokens, decoded claims, or the secret. Reason codes exist so
|
|
126
|
-
diagnostics need none of that.
|
|
127
|
-
- Enforce the decision before the business mutation, not after — verification that runs
|
|
128
|
-
after the order is written is an audit log, not protection.
|
|
1
|
+
# Backend SDK — Java core SDK
|
|
2
|
+
|
|
3
|
+
The backend is where protection actually happens. Everything the browser does is
|
|
4
|
+
transport to deliver a token here; this file is where the request is allowed or refused.
|
|
5
|
+
|
|
6
|
+
This is one of two backend paths. It is framework-neutral: the SDK returns a decision and
|
|
7
|
+
**the application enforces it**. The other path is the declarative Spring Boot 3 starter
|
|
8
|
+
→ `backend-spring-boot.md`; prefer that one when the stack allows it, because it removes
|
|
9
|
+
the enforcement code below and the mistakes that come with it.
|
|
10
|
+
|
|
11
|
+
**Requires Java 17 or newer** — nothing else. So this is the path for every backend that
|
|
12
|
+
is not Spring Boot 3 MVC, including a Spring Boot 3 app built on WebFlux, which is not
|
|
13
|
+
Spring MVC. Also needed: the same App Key the browser uses, the backend-only API Secret,
|
|
14
|
+
and a web framework that can extract the token carrier.
|
|
15
|
+
|
|
16
|
+
Confirm the baseline and supported languages in the customer SDK documentation
|
|
17
|
+
for the selected release. The CLI integration-contract command is unavailable. Do
|
|
18
|
+
not promise an undocumented backend path; a browser-only install protects nothing.
|
|
19
|
+
|
|
20
|
+
The browser mode does not decide this path. Either Seamless or Manual mode delivers the
|
|
21
|
+
token, and the application extracts it and passes the value unchanged to `verify()`.
|
|
22
|
+
|
|
23
|
+
Use exactly **one** verification path per request. A RequestShield token is used once, so
|
|
24
|
+
a handler already covered by `@RequestShieldProtected` must not also call `verify()`.
|
|
25
|
+
|
|
26
|
+
## 1. Dependency
|
|
27
|
+
|
|
28
|
+
```xml title="pom.xml"
|
|
29
|
+
<repositories>
|
|
30
|
+
<repository>
|
|
31
|
+
<id>intellifend-maven</id>
|
|
32
|
+
<url>https://sdk.intellifend.com/packages/maven</url>
|
|
33
|
+
</repository>
|
|
34
|
+
</repositories>
|
|
35
|
+
|
|
36
|
+
<dependency>
|
|
37
|
+
<groupId>com.intellifend.requestshield</groupId>
|
|
38
|
+
<artifactId>requestshield-backend-sdk</artifactId>
|
|
39
|
+
<version>2.0.0</version>
|
|
40
|
+
</dependency>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 2. One reusable client
|
|
44
|
+
|
|
45
|
+
```java
|
|
46
|
+
RequestShieldClient requestShield = RequestShieldClient.builder()
|
|
47
|
+
.appKey(appKey)
|
|
48
|
+
.apiSecret(apiSecret)
|
|
49
|
+
.build();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
One client per App Key, reused across requests, rather than one per request. Keep the
|
|
53
|
+
API secret in backend runtime secret storage.
|
|
54
|
+
|
|
55
|
+
Bind the secret with no fallback default. A backend that starts with a placeholder
|
|
56
|
+
secret rejects every real token, which surfaces days later as "RequestShield is blocking
|
|
57
|
+
all our users" — a startup failure is far cheaper to diagnose than that.
|
|
58
|
+
|
|
59
|
+
## 3. Verify before the protected operation
|
|
60
|
+
|
|
61
|
+
```java
|
|
62
|
+
String token = request.getHeader("X-IntelliFend-Token");
|
|
63
|
+
VerificationResult result = requestShield.verify(token);
|
|
64
|
+
|
|
65
|
+
if (!result.isAllowed()) {
|
|
66
|
+
rejectRequest(result.getReason().getCode());
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
continueProtectedOperation();
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Pass the extracted value unchanged. Do not trim, decode, or pre-validate the shape;
|
|
74
|
+
`verify()` owns that, and pre-parsing only adds a way to reject a valid token.If the
|
|
75
|
+
header is absent, `null`, or empty, pass that value to `verify()` as-is. Do not add a
|
|
76
|
+
separate decision based on token presence.
|
|
77
|
+
|
|
78
|
+
Always enforce `isAllowed()`. Use `getReason().getCode()` for controlled diagnostics or
|
|
79
|
+
a sanitized response contract only — **do not build a separate decision table in
|
|
80
|
+
application code.** Reason codes explain a decision; they do not amend it, and an
|
|
81
|
+
override that allows a blocked reason quietly removes the protection.
|
|
82
|
+
|
|
83
|
+
If the application contract carries the token in a request-body field instead of the
|
|
84
|
+
header, extract that field before verification; everything after that is identical.
|
|
85
|
+
|
|
86
|
+
## 4. Validate the request path
|
|
87
|
+
|
|
88
|
+
1. Browser and backend use the same App Key.
|
|
89
|
+
2. A token reaches the backend unchanged.
|
|
90
|
+
3. The protected operation runs only after `isAllowed()` is checked.
|
|
91
|
+
4. A new browser token is obtained before each application retry.
|
|
92
|
+
5. Tokens and the API secret are excluded from logs.
|
|
93
|
+
|
|
94
|
+
## VerificationResult
|
|
95
|
+
|
|
96
|
+
| Method | Returns | Use |
|
|
97
|
+
| --- | --- | --- |
|
|
98
|
+
| `isAllowed()` | `boolean` | The decision to enforce. Never override it based on a reason code. |
|
|
99
|
+
| `getReason()` | `VerificationReason` | Stable reason; `getReason().getCode()` is safe to log and chart. |
|
|
100
|
+
| `isDegraded()` | `boolean` | The decision was made under a platform availability failure rather than from a real token. |
|
|
101
|
+
|
|
102
|
+
`isDegraded()` deserves attention on first integration. Confirm the exact behaviour in
|
|
103
|
+
the customer documentation before relying on it, and tell the user which behaviour you
|
|
104
|
+
assumed. The documented intent is that a degraded result reflects a platform
|
|
105
|
+
availability failure rather than a real token decision — so a degraded result is not
|
|
106
|
+
evidence that a token was valid. Log the degraded count as its own metric; a persistent nonzero rate is an
|
|
107
|
+
infrastructure signal, not a bot signal.
|
|
108
|
+
|
|
109
|
+
Reason codes and what each means for debugging: `troubleshooting.md`.
|
|
110
|
+
|
|
111
|
+
## Align the browser side
|
|
112
|
+
|
|
113
|
+
Configure exactly one browser mode for the same operation — Seamless
|
|
114
|
+
(`browser-seamless.md`) or Manual (`browser-manual.md`). Browser configuration does not
|
|
115
|
+
create backend protection, and a `verify()` call does not create browser configuration.
|
|
116
|
+
Land both halves in the same change. For a cross-origin API, configure CORS to allow the
|
|
117
|
+
token header.
|
|
118
|
+
|
|
119
|
+
## Security requirements
|
|
120
|
+
|
|
121
|
+
- The API Secret lives in backend runtime secret storage only — never in source, a
|
|
122
|
+
committed `.env`, an image layer, a client-visible response, or a log line.
|
|
123
|
+
- The App Key must be identical in browser and backend, or every token fails.
|
|
124
|
+
- One token per protected request, verified exactly once.
|
|
125
|
+
- Never log or persist raw tokens, decoded claims, or the secret. Reason codes exist so
|
|
126
|
+
diagnostics need none of that.
|
|
127
|
+
- Enforce the decision before the business mutation, not after — verification that runs
|
|
128
|
+
after the order is written is an audit log, not protection.
|