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.
Files changed (54) hide show
  1. package/README.md +292 -2
  2. package/config/.env.stg +8 -0
  3. package/package.json +25 -4
  4. package/skills/requestshield/SKILL.md +240 -0
  5. package/skills/requestshield/assets/AGENTS.codex.md +107 -0
  6. package/skills/requestshield/references/backend-java-core.md +152 -0
  7. package/skills/requestshield/references/backend-spring-boot.md +158 -0
  8. package/skills/requestshield/references/browser-manual.md +244 -0
  9. package/skills/requestshield/references/browser-seamless.md +213 -0
  10. package/skills/requestshield/references/cli.md +314 -0
  11. package/skills/requestshield/references/integration-planning.md +357 -0
  12. package/skills/requestshield/references/troubleshooting.md +131 -0
  13. package/src/agent-detector.mjs +102 -0
  14. package/src/api-client.mjs +155 -0
  15. package/src/args.mjs +147 -0
  16. package/src/browser-opener.mjs +32 -0
  17. package/src/cli.mjs +115 -0
  18. package/src/command-completion.mjs +10 -0
  19. package/src/command-registry.mjs +151 -0
  20. package/src/commands/agent-setup.mjs +195 -0
  21. package/src/commands/agent-status.mjs +65 -0
  22. package/src/commands/application-mutations.mjs +37 -0
  23. package/src/commands/application-response.mjs +55 -0
  24. package/src/commands/apps-get.mjs +28 -0
  25. package/src/commands/apps-list.mjs +93 -0
  26. package/src/commands/auth-status.mjs +42 -0
  27. package/src/commands/billing.mjs +106 -0
  28. package/src/commands/contract.mjs +59 -0
  29. package/src/commands/keys-create.mjs +22 -0
  30. package/src/commands/mutation-support.mjs +135 -0
  31. package/src/commands/output.mjs +26 -0
  32. package/src/commands/secret-commands.mjs +66 -0
  33. package/src/commands/secret-output.mjs +52 -0
  34. package/src/commands/service-status.mjs +34 -0
  35. package/src/commands/signin.mjs +95 -0
  36. package/src/commands/signout.mjs +14 -0
  37. package/src/commands/update-check.mjs +205 -0
  38. package/src/commands/usage.mjs +72 -0
  39. package/src/config.mjs +200 -0
  40. package/src/entrypoint.mjs +44 -0
  41. package/src/errors.mjs +16 -0
  42. package/src/integration-contract-client.mjs +85 -0
  43. package/src/integration-contract.mjs +114 -0
  44. package/src/main.mjs +6 -0
  45. package/src/oauth-client.mjs +153 -0
  46. package/src/oauth-loopback.mjs +120 -0
  47. package/src/package-profile.mjs +24 -0
  48. package/src/private-path.mjs +33 -0
  49. package/src/secret-output.mjs +121 -0
  50. package/src/service-status-client.mjs +87 -0
  51. package/src/session-files.mjs +192 -0
  52. package/src/session-store.mjs +204 -0
  53. package/src/terminal.mjs +143 -0
  54. 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.