You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+25-10Lines changed: 25 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,7 +7,22 @@ This project provides a server-side example of Approov token verification for a
7
7
-`/token-binding` - requires a valid Approov token which is bound to a header value.
8
8
-`/token-double-binding` - requires a valid Approov token which is bound to two header values.
9
9
10
-
In this example, Approov protection is enforced by [app.use(approovAuthMiddleware)](https://github.com/approov/quickstart-nodejs-express-token-check/blob/refactor/nodejs-express-quickstart/ApproovApplication.js#L101-L131). The middleware reads the `Approov-Token` header, validates signature and expiry via [verifyApproovToken](https://github.com/approov/quickstart-nodejs-express-token-check/blob/refactor/nodejs-express-quickstart/ApproovApplication.js#L134-L158), and (when required) enforces token binding via [verifyTokenBinding](https://github.com/approov/quickstart-nodejs-express-token-check/blob/refactor/nodejs-express-quickstart/ApproovApplication.js#L160-L170). Protected routes are selected via [PROTECTED_PATHS](https://github.com/approov/quickstart-nodejs-express-token-check/blob/refactor/nodejs-express-quickstart/ApproovApplication.js#L24-L28).
10
+
In this example, Approov token check is implemented in `ApproovApplication.js`. The responsibilities break down as follows:
11
+
12
+
1.**JWT Approov Token validation (signature + expiry)** is implemented in [verifyApproovToken](https://github.com/approov/quickstart-nodejs-express-token-check/blob/refactor/nodejs-express-quickstart/ApproovApplication.js#L162-L193)
13
+
It verifies the HS256 signature (via `jwt.verify`) and rejects tokens that are missing or past `exp`.
14
+
15
+
2.**Token binding (pay + hash)** is handled by [isBindingValid](https://github.com/approov/quickstart-nodejs-express-token-check/blob/refactor/nodejs-express-quickstart/ApproovApplication.js#L195-L203) and [hashBase64](ApproovApplication.js#L291-L293)
16
+
It computes `base64(sha256(binding_value))` and compares it to `pay` using `timingSafeEquals`.
17
+
18
+
3.**Middleware enforcement** is done by [approovAuthMiddleware](https://github.com/approov/quickstart-nodejs-express-token-check/blob/refactor/nodejs-express-quickstart/ApproovApplication.js#L111-L160)
19
+
Requests without valid token/binding are rejected with 401.
20
+
21
+
4.**Binding value selection (what gets hashed)** is in [extractBindingValue + bindingHeadersFor](https://github.com/approov/quickstart-nodejs-express-token-check/blob/refactor/nodejs-express-quickstart/ApproovApplication.js#L205-L234) It uses the headers configured in `bindingHeadersFor` (currently `Authorization` for single binding, or `Authorization` + `SessionId` for double binding).
22
+
23
+
5.**Protected route requirements** are defined in [PROTECTED_PATHS](https://github.com/approov/quickstart-nodejs-express-token-check/blob/refactor/nodejs-express-quickstart/ApproovApplication.js#L24-L29) and [requiredHeadersFor](https://github.com/approov/quickstart-nodejs-express-token-check/blob/refactor/nodejs-express-quickstart/ApproovApplication.js#L236-L246)
24
+
25
+
6.**Protected routes are registered** in the Express route declarations ([app.get/app.post](https://github.com/approov/quickstart-nodejs-express-token-check/blob/refactor/nodejs-express-quickstart/ApproovApplication.js#L47-L97))
11
26
12
27
## Approov Token Verification Flow
13
28
@@ -29,8 +44,8 @@ In this example, Approov protection is enforced by [app.use(approovAuthMiddlewar
29
44
The protected API then computes the same hash from the incoming request and verifies that it matches the `pay` claim, preventing token reuse or replay attacks. For local testing, you can also generate example tokens with a binding using the Approov CLI.
30
45
31
46
5.**Request Decision:**
32
-
If all checks pass → the request is trusted and processed `200 OK`.
33
-
If validation fails → the server responds with `401 Unauthorized`.
47
+
If all checks pass → the request is trusted and processed `200 OK`.
48
+
If validation fails → the server responds with `401 Unauthorized`.
34
49
35
50
## Requirements:
36
51
@@ -78,7 +93,7 @@ bash test.sh
78
93
This script:
79
94
- Verifies that the `approov` and `curl` commands are installed.
80
95
- Checks Approov status by calling `/approov-state` (enabled vs disabled).
81
-
- Runs endpoint tests against `/unprotected` (no token), `/token-check` (valid/invalid Approov tokens), `/token-binding` (token bound to `Authorization`), and `/token-double-binding` (token bound to `Authorization` + `Content-Digest`).
96
+
- Runs endpoint tests against `/unprotected` (no token), `/token-check` (valid/invalid Approov tokens), `/token-binding` (token bound to `Authorization`), and `/token-double-binding` (token bound to `Authorization` + `SessionId`).
82
97
- Logs full request/response details to `.config/logs/<timestamp>.log`.
83
98
84
99
#### *1. Unprotected Endpoint (No Approov)*
@@ -175,16 +190,16 @@ Cache-Control: no-cache
175
190
- The client sends three headers on authenticated API calls:
176
191
- `Approov-Token`
177
192
- `Authorization`
178
-
- `Content-Digest` It is combined with the `Authorization` header to create a stronger binding.
193
+
- `SessionId` It is combined with the `Authorization` header to create a stronger binding.
179
194
- Both are included in the hash inside the Approov token. This means the server verifies a single hash that covers both authentication credentials.
180
195
- **Use case:** Stronger protection then single binding by tying both headers together.
181
196
182
197
***The following example shows how the API responds when an Approov token with two bindings is required.***
183
198
184
-
*Generate a valid Approov token bound to the `Authorization` and `Content-Digest` headers:*
199
+
*Generate a valid Approov token bound to the `Authorization` and `SessionId` headers:*
0 commit comments