requestshield 0.0.0-stage → 1.0.0-stg.276
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 +292 -2
- package/config/.env.stg +8 -0
- package/package.json +25 -4
- package/skills/requestshield/SKILL.md +240 -0
- package/skills/requestshield/assets/AGENTS.codex.md +107 -0
- package/skills/requestshield/references/backend-java-core.md +152 -0
- package/skills/requestshield/references/backend-spring-boot.md +158 -0
- package/skills/requestshield/references/browser-manual.md +244 -0
- package/skills/requestshield/references/browser-seamless.md +213 -0
- package/skills/requestshield/references/cli.md +314 -0
- package/skills/requestshield/references/integration-planning.md +357 -0
- package/skills/requestshield/references/troubleshooting.md +131 -0
- package/src/agent-detector.mjs +102 -0
- package/src/api-client.mjs +155 -0
- package/src/args.mjs +147 -0
- package/src/browser-opener.mjs +32 -0
- package/src/cli.mjs +115 -0
- package/src/command-completion.mjs +10 -0
- package/src/command-registry.mjs +151 -0
- package/src/commands/agent-setup.mjs +195 -0
- package/src/commands/agent-status.mjs +65 -0
- package/src/commands/application-mutations.mjs +37 -0
- package/src/commands/application-response.mjs +55 -0
- package/src/commands/apps-get.mjs +28 -0
- package/src/commands/apps-list.mjs +93 -0
- package/src/commands/auth-status.mjs +42 -0
- package/src/commands/billing.mjs +106 -0
- package/src/commands/contract.mjs +59 -0
- package/src/commands/keys-create.mjs +22 -0
- package/src/commands/mutation-support.mjs +135 -0
- package/src/commands/output.mjs +26 -0
- package/src/commands/secret-commands.mjs +66 -0
- package/src/commands/secret-output.mjs +52 -0
- package/src/commands/service-status.mjs +34 -0
- package/src/commands/signin.mjs +95 -0
- package/src/commands/signout.mjs +14 -0
- package/src/commands/update-check.mjs +205 -0
- package/src/commands/usage.mjs +72 -0
- package/src/config.mjs +200 -0
- package/src/entrypoint.mjs +44 -0
- package/src/errors.mjs +16 -0
- package/src/integration-contract-client.mjs +85 -0
- package/src/integration-contract.mjs +114 -0
- package/src/main.mjs +6 -0
- package/src/oauth-client.mjs +153 -0
- package/src/oauth-loopback.mjs +120 -0
- package/src/package-profile.mjs +24 -0
- package/src/private-path.mjs +33 -0
- package/src/secret-output.mjs +121 -0
- package/src/service-status-client.mjs +87 -0
- package/src/session-files.mjs +192 -0
- package/src/session-store.mjs +204 -0
- package/src/terminal.mjs +143 -0
- package/src/usage-time.mjs +26 -0
|
@@ -0,0 +1,107 @@
|
|
|
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
|
+
Choose Seamless or Manual independently at each layer:
|
|
18
|
+
|
|
19
|
+
- **Frontend Seamless** uses `data-protect` with supported page-scope `fetch` or async
|
|
20
|
+
XHR requests; **frontend Manual** uses `getToken()` and application token transport.
|
|
21
|
+
- **Backend Seamless** uses the Spring Boot 3 MVC starter and annotation on Java 17+;
|
|
22
|
+
**backend Manual** uses explicit Java core SDK verification and enforcement on Java
|
|
23
|
+
17+, including Spring Boot applications.
|
|
24
|
+
|
|
25
|
+
Honor explicit valid preferences and preserve supported existing integrations,
|
|
26
|
+
including mixed pairs. For two new, unselected layers, recommend Seamless + Seamless
|
|
27
|
+
when the release, stack, and transport support it; otherwise recommend Manual + Manual
|
|
28
|
+
when supported. Never silently replace an unsupported explicit choice. Follow the
|
|
29
|
+
planning reference for partial integrations and capability limits. Before integration
|
|
30
|
+
edits, present the supported options and recommendation for any unselected layer,
|
|
31
|
+
ask the user to choose, and wait for their answer. Keep already confirmed choices
|
|
32
|
+
and supported existing modes without asking again. Frontend Manual
|
|
33
|
+
with backend Seamless must use the token header; a body carrier needs backend Manual.
|
|
34
|
+
`BLOCK` and `MONITOR` describe enforcement, not these integration styles.
|
|
35
|
+
Check both halves before proposing edits — finding a backend blocker afterwards leaves
|
|
36
|
+
tokens flowing with nothing enforcing them. Java below 17 blocks both backend modes.
|
|
37
|
+
|
|
38
|
+
For Install, if the request does not
|
|
39
|
+
already name an intentionally narrow endpoint set, scan the application read-only for
|
|
40
|
+
abuse-sensitive browser-to-backend operations. Rank the complete candidate inventory by
|
|
41
|
+
business impact, automation attractiveness, exposure, cost, and trace confidence.
|
|
42
|
+
Present the candidates through the host's native interactive multi-select, each
|
|
43
|
+
showing priority, exact method/path, reason, and current state (a numbered list only
|
|
44
|
+
when the host has no interactive multi-select), then wait. Only the selected operations authorize integration
|
|
45
|
+
edits. Install browser and backend wiring without creating or configuring credentials,
|
|
46
|
+
then run static checks and available focused builds/tests. Do not run runtime or
|
|
47
|
+
negative tests as part of Install. Report runtime activation and enforcement as
|
|
48
|
+
unverified, and recommend that the user configure the canonical App Key and API Secret
|
|
49
|
+
names, restart or redeploy, and test afterwards.
|
|
50
|
+
|
|
51
|
+
For Uninstall, detect the existing integration, present its exact integration-only
|
|
52
|
+
removal inventory, and wait for confirmation before editing.
|
|
53
|
+
|
|
54
|
+
For Test / negative testing, use the existing runtime configuration. Require a fresh,
|
|
55
|
+
successful, non-degraded control before the tokenless comparison. If the control fails,
|
|
56
|
+
report the failure and stop before that comparison without starting credential management.
|
|
57
|
+
|
|
58
|
+
Orientation:
|
|
59
|
+
|
|
60
|
+
- `~/.agents/skills/requestshield/SKILL.md` — the procedure. Start here.
|
|
61
|
+
- `~/.agents/skills/requestshield/references/integration-planning.md` — the planning flow:
|
|
62
|
+
ranked endpoint discovery and selection, detecting an existing integration, gating
|
|
63
|
+
on the backend stack (Java version, Spring MVC vs WebFlux, other frameworks), and
|
|
64
|
+
choosing the two modes.
|
|
65
|
+
Read it before any install, verify, or uninstall.
|
|
66
|
+
- `~/.agents/skills/requestshield/references/cli.md` — supported commands and their
|
|
67
|
+
availability. `service status` checks public HTTP liveness without login;
|
|
68
|
+
passing checks do not establish application protection. `billing get <app-key>`
|
|
69
|
+
reads account-wide approximate usage and cost estimates after sign-in;
|
|
70
|
+
credential-status metadata remains unavailable.
|
|
71
|
+
- `~/.agents/skills/requestshield/references/browser-seamless.md` — frontend Seamless: exact-endpoint
|
|
72
|
+
matching, and the transports it does not cover.
|
|
73
|
+
- `~/.agents/skills/requestshield/references/browser-manual.md` — frontend Manual, carriers, CORS, CSP.
|
|
74
|
+
- `~/.agents/skills/requestshield/references/backend-spring-boot.md` — backend Seamless:
|
|
75
|
+
Spring Boot 3 MVC with `@RequestShieldProtected`.
|
|
76
|
+
- `~/.agents/skills/requestshield/references/backend-java-core.md` — backend Manual:
|
|
77
|
+
explicit `verify()` and enforcement on Java 17+, including Spring Boot.
|
|
78
|
+
- `~/.agents/skills/requestshield/references/troubleshooting.md` — reason codes and diagnosis.
|
|
79
|
+
|
|
80
|
+
Six rules worth carrying without opening a file:
|
|
81
|
+
|
|
82
|
+
- **Use native Yes/No choices for every binary interaction.** When the host supports
|
|
83
|
+
interactive choices, present exactly **Yes** and **No** and do not ask for a typed or
|
|
84
|
+
free-text answer. Fall back to a plain-text `Yes`/`No` question only when the host has
|
|
85
|
+
no interactive-choice capability, and always wait for an explicitly submitted
|
|
86
|
+
choice.
|
|
87
|
+
- The **API Secret** never enters source control, logs, or a reply. It belongs in backend
|
|
88
|
+
runtime secret storage, and `requestshield secret rotate <app-key>` is the fix if it leaks. The
|
|
89
|
+
**App Key** is public and must be identical in browser and backend.
|
|
90
|
+
- **Keep secret output out of tool transcripts.** Before create, rotate, or reveal,
|
|
91
|
+
warn that the API Secret is displayed only once and obtain confirmation with the
|
|
92
|
+
native **Yes**/**No** interaction rule above. Run the
|
|
93
|
+
command only in a private user-visible terminal whose output is unavailable to the
|
|
94
|
+
agent; otherwise tell the user to run it in their own terminal. Never capture or
|
|
95
|
+
redirect the output, and ask only whether the user stored the secret successfully.
|
|
96
|
+
- Every protected request needs its **own token, verified exactly once**. Reuse or a
|
|
97
|
+
second verification path returns `token_replayed` and blocks a legitimate request.
|
|
98
|
+
- **Enforcement lives in the backend.** Never put allow/block/retry logic in the browser,
|
|
99
|
+
and never build a decision table from reason codes in application code.
|
|
100
|
+
- In frontend Manual, attach `X-IntelliFend-Token` **only when `getToken()` returns a
|
|
101
|
+
non-empty string**; when it is empty, omit the header and let the backend apply its
|
|
102
|
+
configured policy.
|
|
103
|
+
|
|
104
|
+
Use `requestshield contract --json` and its documentation links before changing
|
|
105
|
+
the script URL, matching integrity, header, modes or backend baseline. It is a public
|
|
106
|
+
read with no login. Use the intended environment, merge CSP additions and preserve
|
|
107
|
+
the selected release; report missing metadata instead of inventing values.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Backend Manual — 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
|
+
Backend Manual uses the framework-neutral core SDK: application code extracts the
|
|
7
|
+
token, calls `verify()`, and **enforces the returned decision**. It works on Spring Boot
|
|
8
|
+
3 MVC as well as other Java frameworks. Backend Seamless uses the declarative Spring
|
|
9
|
+
Boot 3 starter → `backend-spring-boot.md`. Apply the pairing rules in
|
|
10
|
+
`integration-planning.md`; do not switch an existing backend merely to match names.
|
|
11
|
+
|
|
12
|
+
**Requires Java 17 or newer** — nothing else. So this is the path for every backend that
|
|
13
|
+
is not Spring Boot 3 MVC, including a Spring Boot 3 app built on WebFlux, which is not
|
|
14
|
+
Spring MVC. Also needed: the same App Key the browser uses, the backend-only API Secret,
|
|
15
|
+
and a web framework that can extract the token carrier.
|
|
16
|
+
|
|
17
|
+
The Install flow wires the canonical environment-variable references without requiring
|
|
18
|
+
credential values. Do not start or deploy the credential-dependent runtime until the
|
|
19
|
+
user has configured `INTELLIFEND_REQUESTSHIELD_APP_KEY` and the backend-only
|
|
20
|
+
`INTELLIFEND_REQUESTSHIELD_API_SECRET`.
|
|
21
|
+
|
|
22
|
+
Use the backend values already established by
|
|
23
|
+
[Contract check](integration-planning.md#contract-check), then follow the contract's
|
|
24
|
+
Java SDK documentation link. Do not promise an undocumented backend path; a
|
|
25
|
+
browser-only install protects nothing.
|
|
26
|
+
|
|
27
|
+
Either frontend Seamless or frontend Manual can deliver the token. Application code
|
|
28
|
+
extracts it and passes the value unchanged to `verify()`. Backend Manual does not
|
|
29
|
+
require frontend Manual; an explicit or existing frontend Seamless integration remains
|
|
30
|
+
valid when its transport and release requirements are met.
|
|
31
|
+
|
|
32
|
+
Use exactly **one** verification path per request. A RequestShield token is used once, so
|
|
33
|
+
a handler already covered by `@RequestShieldProtected` must not also call `verify()`.
|
|
34
|
+
|
|
35
|
+
## 1. Dependency
|
|
36
|
+
|
|
37
|
+
For the dependency version, replace `BACKEND_SDK_VERSION_FROM_CONTRACT` below with the
|
|
38
|
+
exact `data.backend.sdkVersion` value retained from **Contract check**. Do not rerun the
|
|
39
|
+
contract, infer a version, use `latest`, or leave the placeholder in the customer build
|
|
40
|
+
file. If Contract check did not establish that field, stop and report the problem.
|
|
41
|
+
|
|
42
|
+
```xml title="pom.xml"
|
|
43
|
+
<repositories>
|
|
44
|
+
<repository>
|
|
45
|
+
<id>intellifend-maven</id>
|
|
46
|
+
<url>https://sdk.intellifend.com/packages/maven</url>
|
|
47
|
+
</repository>
|
|
48
|
+
</repositories>
|
|
49
|
+
|
|
50
|
+
<dependency>
|
|
51
|
+
<groupId>com.intellifend.requestshield</groupId>
|
|
52
|
+
<artifactId>requestshield-backend-sdk</artifactId>
|
|
53
|
+
<version>BACKEND_SDK_VERSION_FROM_CONTRACT</version>
|
|
54
|
+
</dependency>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 2. One reusable client
|
|
58
|
+
|
|
59
|
+
```java
|
|
60
|
+
RequestShieldClient requestShield = RequestShieldClient.builder()
|
|
61
|
+
.appKey(appKey)
|
|
62
|
+
.apiSecret(apiSecret)
|
|
63
|
+
.build();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
One client per App Key, reused across requests, rather than one per request. Keep the
|
|
67
|
+
API secret in backend runtime secret storage.
|
|
68
|
+
|
|
69
|
+
Java SDK 2.0.0 uses API v2 for verification and availability checks. It has no
|
|
70
|
+
API-version option or fallback to v1.
|
|
71
|
+
Preserve existing Java 1.0.0/v1 integrations. See `integration-planning.md` for
|
|
72
|
+
the supported version combinations and mixed-version availability behavior.
|
|
73
|
+
|
|
74
|
+
Bind the secret with no fallback default. A backend that starts with a placeholder
|
|
75
|
+
secret rejects every real token, which surfaces days later as "RequestShield is blocking
|
|
76
|
+
all our users" — a startup failure is far cheaper to diagnose than that.
|
|
77
|
+
|
|
78
|
+
## 3. Verify before the protected operation
|
|
79
|
+
|
|
80
|
+
```java
|
|
81
|
+
String token = request.getHeader("X-IntelliFend-Token");
|
|
82
|
+
VerificationResult result = requestShield.verify(token);
|
|
83
|
+
|
|
84
|
+
if (!result.isAllowed()) {
|
|
85
|
+
rejectRequest(result.getReason().getCode());
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
continueProtectedOperation();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Pass the extracted value unchanged. Do not trim, decode, or pre-validate the shape;
|
|
93
|
+
`verify()` owns that, and pre-parsing only adds a way to reject a valid token. If the
|
|
94
|
+
header is absent, `null`, or empty, pass that value to `verify()` as-is. Do not add a
|
|
95
|
+
separate decision based on token presence.
|
|
96
|
+
|
|
97
|
+
Always enforce `isAllowed()`. Use `getReason().getCode()` for controlled diagnostics or
|
|
98
|
+
a sanitized response contract only — **do not build a separate decision table in
|
|
99
|
+
application code.** Reason codes explain a decision; they do not amend it, and an
|
|
100
|
+
override that allows a blocked reason quietly removes the protection.
|
|
101
|
+
|
|
102
|
+
Backend Manual names this explicit integration style. The core SDK has no starter
|
|
103
|
+
`mode` property; documentation should describe the application's observed handling of
|
|
104
|
+
`isAllowed()` rather than inventing a `BLOCK` or `MONITOR` configuration option.
|
|
105
|
+
|
|
106
|
+
If the application contract carries the token in a request-body field instead of the
|
|
107
|
+
header, extract that field before verification; everything after that is identical.
|
|
108
|
+
|
|
109
|
+
## 4. Validate the request path
|
|
110
|
+
|
|
111
|
+
1. Browser and backend use the same App Key.
|
|
112
|
+
2. A token reaches the backend unchanged.
|
|
113
|
+
3. The protected operation runs only after `isAllowed()` is checked.
|
|
114
|
+
4. A new browser token is obtained before each application retry.
|
|
115
|
+
5. Tokens and the API secret are excluded from logs.
|
|
116
|
+
|
|
117
|
+
## VerificationResult
|
|
118
|
+
|
|
119
|
+
| Method | Returns | Use |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| `isAllowed()` | `boolean` | The decision to enforce. Never override it based on a reason code. |
|
|
122
|
+
| `getReason()` | `VerificationReason` | Stable reason; `getReason().getCode()` is safe to log and chart. |
|
|
123
|
+
| `isDegraded()` | `boolean` | The decision was made under a platform availability failure rather than from a real token. |
|
|
124
|
+
|
|
125
|
+
`isDegraded()` deserves attention on first integration. Confirm the exact behaviour in
|
|
126
|
+
the customer documentation before relying on it, and tell the user which behaviour you
|
|
127
|
+
assumed. The documented intent is that a degraded result reflects a platform
|
|
128
|
+
availability failure rather than a real token decision — so a degraded result is not
|
|
129
|
+
evidence that a token was valid. Log the degraded count as its own metric; a persistent nonzero rate is an
|
|
130
|
+
infrastructure signal, not a bot signal.
|
|
131
|
+
|
|
132
|
+
Reason codes and what each means for debugging: `troubleshooting.md`.
|
|
133
|
+
|
|
134
|
+
## Align the browser side
|
|
135
|
+
|
|
136
|
+
Configure exactly one frontend mode for the same operation — frontend Seamless
|
|
137
|
+
(`browser-seamless.md`) or frontend Manual (`browser-manual.md`). Browser configuration does not
|
|
138
|
+
create backend protection, and a `verify()` call does not create browser configuration.
|
|
139
|
+
For a new integration, land both halves in the same change. For a cross-origin API,
|
|
140
|
+
configure CORS for the request's actual headers; allow `X-IntelliFend-Token` when using
|
|
141
|
+
the header carrier. A documented body carrier does not require that unused header.
|
|
142
|
+
|
|
143
|
+
## Security requirements
|
|
144
|
+
|
|
145
|
+
- The API Secret lives in backend runtime secret storage only — never in source, a
|
|
146
|
+
committed `.env`, an image layer, a client-visible response, or a log line.
|
|
147
|
+
- The App Key must be identical in browser and backend, or every token fails.
|
|
148
|
+
- One token per protected request, verified exactly once.
|
|
149
|
+
- Never log or persist raw tokens, decoded claims, or the secret. Reason codes exist so
|
|
150
|
+
diagnostics need none of that.
|
|
151
|
+
- Enforce the decision before the business mutation, not after — verification that runs
|
|
152
|
+
after the order is written is an audit log, not protection.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Backend Seamless — Spring Boot 3 starter
|
|
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
|
+
Backend Seamless uses the Spring Boot 3 starter to extract, verify, and enforce the
|
|
7
|
+
token before an annotated handler runs. Backend Manual uses the framework-neutral
|
|
8
|
+
core SDK → `backend-java-core.md`. Select the pair using `integration-planning.md`;
|
|
9
|
+
availability of the starter does not override an explicit preference or existing mode.
|
|
10
|
+
|
|
11
|
+
**Requires Spring Boot 3 with Spring MVC, and Java 17 or newer.** A Spring Boot 3 app
|
|
12
|
+
built on WebFlux is not Spring MVC and cannot use this path. Confirm the stack before
|
|
13
|
+
writing anything — the detection recipes are in `integration-planning.md`, under **Gate
|
|
14
|
+
-> Run the check**. Also needed: the same
|
|
15
|
+
App Key the browser uses, and the backend-only API Secret.
|
|
16
|
+
|
|
17
|
+
The Install flow wires the canonical environment-variable references without requiring
|
|
18
|
+
credential values. Do not start or deploy the credential-dependent runtime until the
|
|
19
|
+
user has configured `INTELLIFEND_REQUESTSHIELD_APP_KEY` and the backend-only
|
|
20
|
+
`INTELLIFEND_REQUESTSHIELD_API_SECRET`.
|
|
21
|
+
|
|
22
|
+
Use `requestshield contract --json` for the selected release's Java baseline,
|
|
23
|
+
Maven coordinates and challenge URL, then follow its Java SDK documentation link.
|
|
24
|
+
Do not promise an undocumented backend path; a browser-only install protects nothing.
|
|
25
|
+
|
|
26
|
+
Either frontend Seamless or frontend Manual can deliver the token in
|
|
27
|
+
`X-IntelliFend-Token`; the starter reads that header in both cases. Frontend Manual
|
|
28
|
+
with a request-body carrier needs backend Manual extraction instead. Keep an existing
|
|
29
|
+
supported frontend mode; configuring backend Seamless does not migrate the frontend.
|
|
30
|
+
|
|
31
|
+
Use exactly **one** verification path per request. A RequestShield token is used once, so
|
|
32
|
+
a handler covered by the annotation must not also call `verify()`.
|
|
33
|
+
|
|
34
|
+
## 1. Repository and dependency
|
|
35
|
+
|
|
36
|
+
```xml title="pom.xml"
|
|
37
|
+
<repositories>
|
|
38
|
+
<repository>
|
|
39
|
+
<id>intellifend-maven</id>
|
|
40
|
+
<url>https://sdk.intellifend.com/packages/maven</url>
|
|
41
|
+
</repository>
|
|
42
|
+
</repositories>
|
|
43
|
+
|
|
44
|
+
<dependency>
|
|
45
|
+
<groupId>com.intellifend.requestshield</groupId>
|
|
46
|
+
<artifactId>requestshield-spring-boot3-starter</artifactId>
|
|
47
|
+
<version>2.0.0</version>
|
|
48
|
+
</dependency>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```kotlin title="build.gradle.kts"
|
|
52
|
+
repositories {
|
|
53
|
+
maven { url = uri("https://sdk.intellifend.com/packages/maven") }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
dependencies {
|
|
57
|
+
implementation("com.intellifend.requestshield:requestshield-spring-boot3-starter:2.0.0")
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The starter includes `requestshield-backend-sdk:2.0.0` — do not declare both.
|
|
62
|
+
|
|
63
|
+
## 2. Configure
|
|
64
|
+
|
|
65
|
+
```yaml title="application.yml"
|
|
66
|
+
intellifend:
|
|
67
|
+
requestshield:
|
|
68
|
+
app-key: ${INTELLIFEND_REQUESTSHIELD_APP_KEY}
|
|
69
|
+
api-secret: ${INTELLIFEND_REQUESTSHIELD_API_SECRET}
|
|
70
|
+
challenge-server-url: https://challenge.intellifend.ai
|
|
71
|
+
mode: BLOCK
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
| Property | Required | Notes |
|
|
75
|
+
| --- | --- | --- |
|
|
76
|
+
| `app-key` | Yes | Must be the same value as the browser's `data-app-key`. |
|
|
77
|
+
| `api-secret` | Yes | Backend runtime secret storage. Pass the supplied value unchanged. |
|
|
78
|
+
| `challenge-server-url` | No | Defaults to the RequestShield service. |
|
|
79
|
+
| `mode` | No | Global enforcement mode, `BLOCK` or `MONITOR`. Defaults to `BLOCK`; this setting does not accept `Seamless` or `Manual`. |
|
|
80
|
+
|
|
81
|
+
Java SDK 2.0.0 uses API v2 for verification and availability checks; there is no
|
|
82
|
+
API-version setting. Existing Java 1.0.0 integrations continue using v1.
|
|
83
|
+
|
|
84
|
+
Bind the secret with no fallback default, so an environment that fails to inject it
|
|
85
|
+
fails at startup. A backend that starts with a placeholder secret rejects every real
|
|
86
|
+
token, which surfaces days later as "RequestShield is blocking all our users" — a
|
|
87
|
+
startup failure is far cheaper to diagnose than that.
|
|
88
|
+
|
|
89
|
+
## 3. Annotate the protected method
|
|
90
|
+
|
|
91
|
+
```java
|
|
92
|
+
import com.intellifend.requestshield.spring.boot.ProtectionMode;
|
|
93
|
+
import com.intellifend.requestshield.spring.boot.RequestShieldProtected;
|
|
94
|
+
|
|
95
|
+
@PostMapping("/api/register")
|
|
96
|
+
@RequestShieldProtected(mode = ProtectionMode.DEFAULT)
|
|
97
|
+
public RegisterResponse register(@RequestBody RegisterRequest request) {
|
|
98
|
+
return accountService.create(request);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The annotation applies to methods. Put it on the handler that performs the protected
|
|
103
|
+
mutation — not on a wrapper or a read-only route in front of it, where the mutation
|
|
104
|
+
could still be reached another way.
|
|
105
|
+
|
|
106
|
+
The annotation declares no path, action, domain, or browser URL list. It creates no
|
|
107
|
+
browser configuration.
|
|
108
|
+
|
|
109
|
+
## 4. Enforcement mode
|
|
110
|
+
|
|
111
|
+
| Enforcement mode | Behaviour |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| `BLOCK` | Enforce the decision before the method runs. |
|
|
114
|
+
| `MONITOR` | Evaluate the request while allowing the method to continue. |
|
|
115
|
+
| `DEFAULT` | Inherit the global mode. |
|
|
116
|
+
|
|
117
|
+
The global property accepts `BLOCK` or `MONITOR` and defaults to `BLOCK`. A method-level
|
|
118
|
+
`BLOCK` or `MONITOR` overrides the global setting.
|
|
119
|
+
|
|
120
|
+
Use `BLOCK` by default for the first live deployment and for subsequent deployments.
|
|
121
|
+
Deployment order is not a reason to select `MONITOR`; `BLOCK` is the mode that enforces
|
|
122
|
+
the decision. Choose `MONITOR` only as an explicit, time-bounded rollout when the team
|
|
123
|
+
accepts that the endpoint is not protected during that period. State clearly whenever
|
|
124
|
+
an endpoint is in `MONITOR`, and define the evidence and owner for switching it to
|
|
125
|
+
`BLOCK` because `MONITOR` evaluates requests but allows them to continue.
|
|
126
|
+
|
|
127
|
+
## 5. Align the browser side
|
|
128
|
+
|
|
129
|
+
Configure exactly one frontend integration mode for the same operation:
|
|
130
|
+
|
|
131
|
+
- Frontend Seamless → add the endpoint URL to `data-protect`. See `browser-seamless.md`.
|
|
132
|
+
- Frontend Manual → send the obtained token in `X-IntelliFend-Token`. See
|
|
133
|
+
`browser-manual.md`.
|
|
134
|
+
|
|
135
|
+
Browser configuration does not create backend protection, and the annotation does not
|
|
136
|
+
create browser configuration. For a new integration, land both halves in the same change. For a cross-origin
|
|
137
|
+
API, configure CORS to allow the token header.
|
|
138
|
+
|
|
139
|
+
## Migrating from backend Manual
|
|
140
|
+
|
|
141
|
+
Migrate only when requested or needed to correct an incompatibility; a mixed pair alone
|
|
142
|
+
does not require migration. For an endpoint calling `RequestShieldClient.verify()`, remove that call and add
|
|
143
|
+
the annotation **in the same backend release**. Running both consumes the same
|
|
144
|
+
single-use token twice, so every request fails — and it fails only once both are
|
|
145
|
+
deployed, which makes it look like the annotation broke the endpoint.
|
|
146
|
+
|
|
147
|
+
Reason codes and what each means for debugging: `troubleshooting.md`.
|
|
148
|
+
|
|
149
|
+
## Security requirements
|
|
150
|
+
|
|
151
|
+
- The API Secret lives in backend runtime secret storage only — never in source, a
|
|
152
|
+
committed `.env`, an image layer, a client-visible response, or a log line.
|
|
153
|
+
- The App Key must be identical in browser and backend, or every token fails.
|
|
154
|
+
- One token per protected request, verified exactly once.
|
|
155
|
+
- Never log or persist raw tokens, decoded claims, or the secret. Reason codes exist so
|
|
156
|
+
diagnostics need none of that.
|
|
157
|
+
- Enforce the decision before the business mutation, not after — verification that runs
|
|
158
|
+
after the order is written is an audit log, not protection.
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Frontend Manual — Browser SDK
|
|
2
|
+
|
|
3
|
+
Frontend Manual gives the application control over **when** a token is obtained and
|
|
4
|
+
**how** it is carried to the backend. It supports explicit token timing and supported
|
|
5
|
+
application-controlled carriers. Use the selection rules in `integration-planning.md`
|
|
6
|
+
for preferences, existing integrations, and the default pairing recommendation.
|
|
7
|
+
|
|
8
|
+
Prerequisites: a public App Key, a backend integration that extracts the chosen carrier,
|
|
9
|
+
and a protected operation that is **not** also configured for frontend Seamless. One
|
|
10
|
+
operation uses one frontend mode. Frontend Manual pairs with backend Seamless through
|
|
11
|
+
`X-IntelliFend-Token`, or with backend Manual through the documented carrier the
|
|
12
|
+
application extracts. A request-body carrier requires backend Manual.
|
|
13
|
+
|
|
14
|
+
The Install flow may wire this integration before credentials are created. Use the
|
|
15
|
+
application's public configuration mechanism for the App Key when possible. Otherwise
|
|
16
|
+
leave the explicit `YOUR_APP_KEY` placeholder and report its exact location. Do not run
|
|
17
|
+
runtime checks until the user has supplied the real App Key; they belong to the
|
|
18
|
+
explicit Test flow, never Verify.
|
|
19
|
+
After static installation, recommend supplying the real App Key at the reported
|
|
20
|
+
binding or placeholder, configuring the backend variables in its reference, restarting
|
|
21
|
+
or redeploying, and testing the selected endpoint. Perform these steps only when
|
|
22
|
+
separately requested.
|
|
23
|
+
|
|
24
|
+
## 1. Load the hosted SDK
|
|
25
|
+
|
|
26
|
+
Place the hosted SDK once in `<head>` before any application bundle that can issue a
|
|
27
|
+
protected request. Use the selected contract URL (`data.browser.scriptUrlVersion` or
|
|
28
|
+
`data.browser.scriptUrlLatest`) and only its matching integrity when provided.
|
|
29
|
+
Bind `data-app-key` or `init()`'s `appKey` through public application configuration when
|
|
30
|
+
it can be wired without a value; otherwise keep `YOUR_APP_KEY` and report its location.
|
|
31
|
+
|
|
32
|
+
`defer` preserves ordering relative to other `defer` and module scripts. If a protected
|
|
33
|
+
request can come from a classic script without `defer` or an `async` bundle, add `defer`
|
|
34
|
+
to that bundle or remove it from the SDK tag.
|
|
35
|
+
|
|
36
|
+
Omit `data-protect` when the page uses frontend Manual only — its presence is what turns
|
|
37
|
+
on frontend Seamless interception:
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<script
|
|
41
|
+
src="SDK_SCRIPT_URL_FROM_RELEASE_DOCS"
|
|
42
|
+
data-app-key="YOUR_APP_KEY"
|
|
43
|
+
defer
|
|
44
|
+
></script>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
| Attribute | Required | Notes |
|
|
48
|
+
| --- | --- | --- |
|
|
49
|
+
| `src` | Yes | Use the selected `data.browser.scriptUrlVersion` or `data.browser.scriptUrlLatest` from `requestshield contract --json`; use `data.browser.integrity` only when the selected URL is the immutable versioned script. Follow the manifest's Browser SDK documentation link for the selected release. |
|
|
50
|
+
| `data-app-key` | Yes | Public App Key. Must match the backend's configured key. |
|
|
51
|
+
| `defer` | Recommended | Keeps HTML parsing unblocked. |
|
|
52
|
+
|
|
53
|
+
The SDK reads configuration from `document.currentScript`, so use **exactly one tag**.
|
|
54
|
+
A second tag re-initializes rather than merging, and the last one to run wins.
|
|
55
|
+
|
|
56
|
+
In templated or bundled apps, put the tag in the HTML entry point (`index.html`,
|
|
57
|
+
`_document.tsx`, the base Django/Rails/Thymeleaf layout) rather than injecting it from
|
|
58
|
+
component code, so it is present before any protected request can fire.
|
|
59
|
+
|
|
60
|
+
Applications with a custom script loader may configure the same setup in JavaScript
|
|
61
|
+
instead:
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
IntelliFend.init({appKey: 'YOUR_APP_KEY'});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Use script attributes *or* explicit initialization for initial setup, not both.
|
|
68
|
+
|
|
69
|
+
Browser SDK 2.0.0 accepts `apiVersion: 'v1' | 'v2'` or
|
|
70
|
+
`data-api-version="v1" | "v2"` and defaults to v2. Existing Browser 1.0.0
|
|
71
|
+
integrations use v1 without this option. Preserve existing pins and API choices.
|
|
72
|
+
API selection is independent of Seamless/Manual mode and does not change the
|
|
73
|
+
2.0.0 WebAssembly or CSP requirements. See `integration-planning.md` for
|
|
74
|
+
mixed-version limits.
|
|
75
|
+
|
|
76
|
+
## 2. Obtain a token immediately before the request
|
|
77
|
+
|
|
78
|
+
For the recommended header carrier, call `getToken()` right before the protected
|
|
79
|
+
operation, and attach the header **only when the returned string is non-empty**:
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
async function createAccount(payload) {
|
|
83
|
+
const token = await IntelliFend.getToken();
|
|
84
|
+
const headers = {'Content-Type': 'application/json'};
|
|
85
|
+
|
|
86
|
+
if (token) {
|
|
87
|
+
headers['X-IntelliFend-Token'] = token;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return fetch('/api/register', {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers,
|
|
93
|
+
body: JSON.stringify(payload),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`getToken()` resolves to a string. A non-empty value is that request's token; an empty
|
|
99
|
+
value means the header is omitted and **the backend applies its configured policy**.
|
|
100
|
+
|
|
101
|
+
That last part is why the empty case needs no client-side handling. The decision about
|
|
102
|
+
what an absent token means already lives at the backend, so wrapping this in a retry
|
|
103
|
+
loop, blocking the submit, showing an error, or substituting a placeholder all move
|
|
104
|
+
policy into the browser — the one place it must not be. Attach it when present, omit it
|
|
105
|
+
when not, and let the backend decide.
|
|
106
|
+
|
|
107
|
+
### Optional action
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
const token = await IntelliFend.getToken({action: 'create-account'});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Case-sensitive. Use an action **only** when IntelliFend has supplied a corresponding
|
|
114
|
+
backend integration rule — a browser-supplied value is not backend configuration, and
|
|
115
|
+
inventing one has no effect on the decision.
|
|
116
|
+
|
|
117
|
+
## 3. Backend extracts the carrier
|
|
118
|
+
|
|
119
|
+
Backend Seamless (Spring Boot starter) reads `X-IntelliFend-Token` from the request
|
|
120
|
+
itself, so no extraction code is needed. With backend Manual (Java core SDK),
|
|
121
|
+
application code extracts the header and passes the value unchanged to `verify()`.
|
|
122
|
+
|
|
123
|
+
A dedicated request-body field is available when the application contract requires it;
|
|
124
|
+
backend Manual must then extract that field before verification. Prefer the header
|
|
125
|
+
unless there is a real constraint — it keeps the browser and backend halves symmetric
|
|
126
|
+
and works with the starter as-is.
|
|
127
|
+
|
|
128
|
+
See `backend-spring-boot.md` or `backend-java-core.md` for the extraction side.
|
|
129
|
+
|
|
130
|
+
## Framework call-site patterns
|
|
131
|
+
|
|
132
|
+
The recurring failure is attaching the token in one place while some requests reach the
|
|
133
|
+
endpoint through another. Enumerate every path to the endpoint before editing. Cover
|
|
134
|
+
each call site and retry when frontend Manual is selected; the number of call sites
|
|
135
|
+
does not override the user's choice or authorize migrating an existing integration.
|
|
136
|
+
|
|
137
|
+
**Axios interceptor** — covers calls made through one Axios instance. Resolve both the
|
|
138
|
+
allow-list and each request to an origin-plus-pathname key. Pathname-only matching can
|
|
139
|
+
attach a token to the wrong origin, and resolving `config.url` against `location.origin`
|
|
140
|
+
ignores the instance's `baseURL`:
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
const protectedEndpointKeys = new Set(
|
|
144
|
+
PROTECTED_ENDPOINTS.map((endpoint) => {
|
|
145
|
+
const url = new URL(endpoint, location.origin);
|
|
146
|
+
return `${url.origin}${url.pathname}`;
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
api.interceptors.request.use(async (config) => {
|
|
151
|
+
let requestUrl;
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
requestUrl = new URL(api.getUri(config), document.baseURI);
|
|
155
|
+
} catch {
|
|
156
|
+
// An unresolved destination must never receive a RequestShield token.
|
|
157
|
+
return config;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const requestKey = `${requestUrl.origin}${requestUrl.pathname}`;
|
|
161
|
+
if (protectedEndpointKeys.has(requestKey)) {
|
|
162
|
+
// Remove a token retained by a reused Axios request config.
|
|
163
|
+
for (const headerName of Object.keys(config.headers)) {
|
|
164
|
+
if (headerName.toLowerCase() === 'x-intellifend-token') {
|
|
165
|
+
delete config.headers[headerName];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const token = await IntelliFend.getToken();
|
|
169
|
+
if (token) {
|
|
170
|
+
config.headers['X-IntelliFend-Token'] = token;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return config;
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
`api.getUri(config)` applies that Axios instance's request configuration, including
|
|
178
|
+
`baseURL`; resolving its result against `location.origin` also handles a root-relative
|
|
179
|
+
base URL. Query strings and fragments are deliberately absent from the comparison, but
|
|
180
|
+
the origin is mandatory.
|
|
181
|
+
|
|
182
|
+
Before shipping the interceptor, verify both directions against the actual instance:
|
|
183
|
+
|
|
184
|
+
- With `axios.create({baseURL: '/api'})`, a call to `post('/register')` resolves to the
|
|
185
|
+
current origin plus `/api/register` and matches that protected endpoint.
|
|
186
|
+
- A call to `https://other.example/api/register` does not match the current origin's
|
|
187
|
+
`/api/register`, does not call `getToken()`, and receives no RequestShield header.
|
|
188
|
+
|
|
189
|
+
**React / TanStack Query** — obtain the token inside the mutation function, never in a
|
|
190
|
+
`useEffect` or component state. A token held in state goes stale, gets reused across
|
|
191
|
+
retries, and comes back `token_replayed`.
|
|
192
|
+
|
|
193
|
+
**Retries** — any retry wrapper must call `getToken()` again on each attempt. Reusing
|
|
194
|
+
the first token is the most common cause of a retry being blocked while the original
|
|
195
|
+
attempt succeeded.
|
|
196
|
+
|
|
197
|
+
**Server-rendered forms** — a native form POST cannot carry a header. Either move the
|
|
198
|
+
submission to `fetch`, or agree a request-body carrier with IntelliFend. Never put the
|
|
199
|
+
token in a hidden field improvised on your own, and never in a query parameter.
|
|
200
|
+
|
|
201
|
+
## Cross-origin application APIs
|
|
202
|
+
|
|
203
|
+
When the protected endpoint is on another origin, configure CORS for the page origin,
|
|
204
|
+
intended HTTP methods, and headers actually sent. If the token uses
|
|
205
|
+
`X-IntelliFend-Token`, include that header in `Access-Control-Allow-Headers`; otherwise
|
|
206
|
+
the browser can block the cross-origin request. A documented body carrier does not
|
|
207
|
+
require allowing the unused token header, but other request headers may still require
|
|
208
|
+
preflight permission.
|
|
209
|
+
|
|
210
|
+
## Content Security Policy
|
|
211
|
+
|
|
212
|
+
If the app sends a CSP, merge these sources into the existing directives — never replace
|
|
213
|
+
the policy, and keep every source the app already declares:
|
|
214
|
+
|
|
215
|
+
```http
|
|
216
|
+
Content-Security-Policy:
|
|
217
|
+
script-src 'self' SCRIPT_SRC_FROM_CONTRACT;
|
|
218
|
+
connect-src 'self' CONNECT_SRC_FROM_CONTRACT;
|
|
219
|
+
worker-src 'self' WORKER_SRC_FROM_CONTRACT;
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Take each value from `data.browser.cspAdditions` in `requestshield contract --json`:
|
|
223
|
+
`scriptSrc` → `script-src`, `connectSrc` → `connect-src`, `workerSrc` → `worker-src`.
|
|
224
|
+
Add every entry, including keywords such as `'wasm-unsafe-eval'`. `'self'` stands for
|
|
225
|
+
the sources the app already declares; keep them. Take every host **and directive** from
|
|
226
|
+
the contract — this skill is not the authority on either. If `getToken()` returns an empty string on a page that sends a CSP, a blocked
|
|
227
|
+
directive is the first thing to check: the browser console names what it refused, which
|
|
228
|
+
beats guessing at the policy.
|
|
229
|
+
|
|
230
|
+
Applications that do not send a CSP need no header changes.
|
|
231
|
+
|
|
232
|
+
## Token handling rules
|
|
233
|
+
|
|
234
|
+
- One token for one protected request.
|
|
235
|
+
- A new token before retrying a protected operation.
|
|
236
|
+
- Forward a non-empty token unchanged; when using the header carrier, omit the header
|
|
237
|
+
if the token is empty.
|
|
238
|
+
- Never cache, persist, transform, or log a token.
|
|
239
|
+
- Never place a token in a URL path, query string, or fragment.
|
|
240
|
+
- Never verify the same request with both `@RequestShieldProtected` and a direct
|
|
241
|
+
`RequestShieldClient.verify()`.
|
|
242
|
+
- Exclude `X-IntelliFend-Token` from logs, analytics, and error reporting — check the
|
|
243
|
+
error-reporting SDK's header allowlist, which often captures request headers by
|
|
244
|
+
default.
|