@t4h.framework/hmac 0.3.8 → 0.3.11
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/.ai/skills/framework-hmac/SKILL.md +76 -201
- package/CHANGELOG.md +14 -0
- package/README.md +12 -64
- package/package.json +2 -2
|
@@ -1,244 +1,119 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: framework-hmac
|
|
3
3
|
description: >-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
Verifies shared-secret HMAC signatures with @t4h.framework/hmac. Use when a
|
|
5
|
+
T4H Framework workflow or App needs HMACAuthorization, when an async activity
|
|
6
|
+
such as webhook() declares auth { type: 'hmac' } for its callbacks, or when
|
|
7
|
+
the user mentions a signed webhook body (GitHub, Shopify, Slack style),
|
|
8
|
+
HMACAlgorithm, signatureFormat, signaturePrefix, signedPayload, timestamp
|
|
9
|
+
replay protection or toleranceInSeconds.
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# @t4h.framework/hmac
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
The package has two uses. Pick the one that matches the task:
|
|
15
15
|
|
|
16
|
-
**
|
|
16
|
+
- **Protect a workflow or an `App`**: `new HMACAuthorization(context)`.
|
|
17
|
+
- **Verify the callbacks of an async activity**: declare
|
|
18
|
+
`auth: { type: 'hmac', … }` on the activity (for `webhook()`, see the
|
|
19
|
+
**framework-webhook** skill). The host verifies each callback with
|
|
20
|
+
`@t4h.framework/hmac/callback`. You do not import it.
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
## Protect a workflow or an App
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
| Entry | Purpose |
|
|
27
|
-
|-------|---------|
|
|
28
|
-
| `@t4h.framework/hmac` | `HMACAuthorization`, `HMACAuthorizationContext`, `HMACAuthorizationManifest`, `HMACAlgorithm`, `HMAC_DEFAULT_ALGORITHM`, `HMAC_DEFAULT_SIGNATURE_FORMAT`, re-exports authorize module |
|
|
29
|
-
| `@t4h.framework/hmac/authorize` | Default async handler: `(context) => Promise<boolean>` |
|
|
30
|
-
| `@t4h.framework/hmac/callback` | Default export `hmacCallback`: `(auth, request) => Promise<boolean>`, the `hmac` authorizer hosts pass to the core `AsyncActivityCallback.authorize` |
|
|
24
|
+
1. Declare the secret with `type: String` in the `EnvDesign` in
|
|
25
|
+
`src/constants/environments.ts`. `HMACAuthorizationContext.secret` is a
|
|
26
|
+
`string`, not a `Secret`.
|
|
27
|
+
2. Construct the authorization and attach it to the `authorization` option of
|
|
28
|
+
a `Workflow` or an `App`.
|
|
31
29
|
|
|
32
30
|
```typescript
|
|
33
|
-
import {
|
|
34
|
-
HMACAuthorization,
|
|
35
|
-
type HMACAuthorizationContext,
|
|
36
|
-
HMACAlgorithm,
|
|
37
|
-
} from '@t4h.framework/hmac'
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Do **not** assume header parsing, env-variable mapping, or provider-specific presets — this package does not read HTTP or env. Your app or hosting layer supplies `secret`, `signature`, `rawPayload` and `timestamp`.
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## Wiring authorization (core)
|
|
45
|
-
|
|
46
|
-
`HMACAuthorization` extends core `Authorization`. Attach to a **Workflow** or **App**; `toManifest()` registers the handler module for the platform.
|
|
47
|
-
|
|
48
|
-
```typescript
|
|
49
|
-
import { App, Workflow } from '@t4h.framework/core'
|
|
50
31
|
import { HMACAuthorization } from '@t4h.framework/hmac'
|
|
51
32
|
|
|
52
|
-
import { environments } from '
|
|
33
|
+
import { environments } from '../constants/environments.js'
|
|
53
34
|
|
|
54
|
-
const authorization = new HMACAuthorization({
|
|
35
|
+
export const authorization = new HMACAuthorization({
|
|
55
36
|
secret: environments.HMAC_SECRET,
|
|
56
|
-
signature: '<signature-from-
|
|
57
|
-
rawPayload: '<exact-
|
|
37
|
+
signature: '<signature-from-the-request-header>',
|
|
38
|
+
rawPayload: '<the-exact-request-body>',
|
|
58
39
|
signaturePrefix: 'sha256=',
|
|
59
40
|
})
|
|
60
|
-
|
|
61
|
-
const workflow = new Workflow(
|
|
62
|
-
{ id: 'protected/v1', authorization },
|
|
63
|
-
async () => ({ ok: true }),
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
const app = new App({
|
|
67
|
-
id: 'my-app',
|
|
68
|
-
workflows: [workflow],
|
|
69
|
-
authorization, // optional app-level default
|
|
70
|
-
})
|
|
71
41
|
```
|
|
72
42
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
{
|
|
77
|
-
pathToModule: '@t4h.framework/hmac/authorize',
|
|
78
|
-
context: {
|
|
79
|
-
secret: string
|
|
80
|
-
signature: string
|
|
81
|
-
rawPayload: string
|
|
82
|
-
algorithm?: string
|
|
83
|
-
signatureFormat?: string
|
|
84
|
-
signaturePrefix?: string
|
|
85
|
-
timestamp?: string
|
|
86
|
-
toleranceInSeconds?: number
|
|
87
|
-
},
|
|
88
|
-
}
|
|
89
|
-
```
|
|
43
|
+
## Verify async activity callbacks
|
|
90
44
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
Declare the secret in the `EnvDesign` in `src/constants/environments.ts`. The context types `secret` as `string`, so declare the entry with `type: String`, not `type: Secret`. This is an exception to the `type: Secret` rule of **framework-core**.
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
## Context: `HMACAuthorizationContext`
|
|
98
|
-
|
|
99
|
-
| Field | Required | Description |
|
|
100
|
-
|-------|----------|-------------|
|
|
101
|
-
| `secret` | Yes | Shared secret, used as **UTF-8 bytes** for the HMAC key. A secret delivered hex/base64-encoded must be decoded by the caller before it is passed in. |
|
|
102
|
-
| `signature` | Yes | Signature the caller presented. Empty string → handler returns `false`. |
|
|
103
|
-
| `rawPayload` | Yes | The **exact** bytes that were signed. Never re-serialize parsed JSON — key order and whitespace change and the digest will not match. |
|
|
104
|
-
| `algorithm` | No | `HMACAlgorithm` key: `SHA1`, `SHA256`, `SHA384` or `SHA512` (not `sha256`). Defaults to `SHA256`. Other values throw. |
|
|
105
|
-
| `signatureFormat` | No | Encoding of `signature`: `hex`, `base64`, `base64url` or `binary`. Defaults to `hex`. Other values throw. |
|
|
106
|
-
| `signaturePrefix` | No | Stripped before comparison, e.g. `sha256=`. If set and absent from `signature` → `false`. |
|
|
107
|
-
| `timestamp` | Conditional | Used with `toleranceInSeconds`. All-digit string → Unix **seconds**; otherwise `Date.parse` (ISO 8601). |
|
|
108
|
-
| `toleranceInSeconds` | No | Enables replay protection. Symmetric window (past **and** future, for clock skew). |
|
|
109
|
-
|
|
110
|
-
---
|
|
111
|
-
|
|
112
|
-
## Runtime handler (`@t4h.framework/hmac/authorize`)
|
|
113
|
-
|
|
114
|
-
Default export:
|
|
45
|
+
The `auth` object names request headers, not values. Its `secret` IS a
|
|
46
|
+
`Secret`: declare it with `type: Secret` and pass it whole.
|
|
115
47
|
|
|
116
48
|
```typescript
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
2. Check the configuration (see **Return values vs errors**).
|
|
124
|
-
3. If `toleranceInSeconds` is set: `timestamp` outside the window or unparseable → **`false`**.
|
|
125
|
-
4. If `signaturePrefix` is set and `signature` does not start with it → **`false`**; otherwise strip it.
|
|
126
|
-
5. Compute `crypto.createHmac(algorithm, secret).update(rawPayload).digest()` and compare with `crypto.timingSafeEqual`.
|
|
127
|
-
|
|
128
|
-
The candidate is decoded from `signatureFormat` into a Buffer before comparing, so **hex casing and base64 padding never cause a false negative**, and a candidate of a different decoded length short-circuits to `false` (`timingSafeEqual` throws on length mismatch).
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
export enum HMACAlgorithm {
|
|
132
|
-
SHA1 = 'sha1',
|
|
133
|
-
SHA256 = 'sha256',
|
|
134
|
-
SHA384 = 'sha384',
|
|
135
|
-
SHA512 = 'sha512',
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
---
|
|
140
|
-
|
|
141
|
-
## Examples
|
|
142
|
-
|
|
143
|
-
### Webhook with a prefixed hex signature (GitHub style)
|
|
144
|
-
|
|
145
|
-
```typescript
|
|
146
|
-
new HMACAuthorization({
|
|
147
|
-
secret: environments.HMAC_SECRET,
|
|
148
|
-
signature: request.headers['x-hub-signature-256'], // 'sha256=9f86d0…'
|
|
149
|
-
rawPayload: rawRequestBody, // the untouched body string
|
|
150
|
-
signaturePrefix: 'sha256=',
|
|
151
|
-
})
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### Timestamped payload with replay protection (Stripe style)
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
new HMACAuthorization({
|
|
158
|
-
secret: environments.HMAC_SECRET,
|
|
159
|
-
signature: parsedSignatureHeader.v1,
|
|
160
|
-
rawPayload: `${timestamp}.${rawRequestBody}`, // timestamp bound INTO the signature
|
|
161
|
-
timestamp,
|
|
49
|
+
auth: {
|
|
50
|
+
type: 'hmac',
|
|
51
|
+
secret: environments.PARTNER_SECRET,
|
|
52
|
+
header: 'x-slack-signature',
|
|
53
|
+
signaturePrefix: 'v0=',
|
|
54
|
+
timestampHeader: 'x-slack-request-timestamp',
|
|
162
55
|
toleranceInSeconds: 300,
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
`toleranceInSeconds` only proves the timestamp is *recent*. Including the timestamp in `rawPayload` is what stops an attacker from editing it — do both.
|
|
167
|
-
|
|
168
|
-
### Base64 SHA512
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
new HMACAuthorization({
|
|
172
|
-
secret: environments.HMAC_SECRET,
|
|
173
|
-
signature: base64Signature,
|
|
174
|
-
rawPayload: canonicalPayloadString,
|
|
175
|
-
algorithm: 'SHA512',
|
|
176
|
-
signatureFormat: 'base64',
|
|
177
|
-
})
|
|
56
|
+
signedPayload: 'v0:{timestamp}:{body}',
|
|
57
|
+
}
|
|
178
58
|
```
|
|
179
59
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
60
|
+
`signedPayload` is a template with `{body}` (raw body) and `{timestamp}`. It
|
|
61
|
+
defaults to `'{body}'`.
|
|
62
|
+
|
|
63
|
+
## Traps
|
|
64
|
+
|
|
65
|
+
- `algorithm` is an enum KEY in upper case: `'SHA256'`, not `'sha256'`. Other
|
|
66
|
+
values throw. Defaults: `'SHA256'` and `'hex'`.
|
|
67
|
+
- `rawPayload` is the exact signed bytes. Do not parse JSON and stringify it
|
|
68
|
+
again: key order and spaces change and the digest fails.
|
|
69
|
+
- `secret` is used as UTF-8 bytes. Decode a hex or base64 secret first.
|
|
70
|
+
- A literal string in `secret` fails the lint rule
|
|
71
|
+
`no-literal-secret-in-authorization` of `@t4h.framework/core`.
|
|
72
|
+
- An all-digit `timestamp` is Unix SECONDS. A millisecond value is read as
|
|
73
|
+
seconds and fails the tolerance check. Other strings go through
|
|
74
|
+
`Date.parse`.
|
|
75
|
+
- The tolerance check only proves the timestamp is recent. Put the timestamp in
|
|
76
|
+
the signed bytes too (`rawPayload` or `signedPayload`), or an attacker can
|
|
77
|
+
change it.
|
|
78
|
+
- On a callback, `toleranceInSeconds` without `timestampHeader` makes each
|
|
79
|
+
callback fail with an error.
|
|
80
|
+
- A denied caller gives `false`. A configuration mistake (empty `secret`,
|
|
81
|
+
unknown `algorithm` or `signatureFormat`, tolerance without timestamp)
|
|
82
|
+
throws.
|
|
83
|
+
- `signatureFormat` `'binary'` exists only on `HMACAuthorization`, not on the
|
|
84
|
+
callback `auth`.
|
|
85
|
+
|
|
86
|
+
## Test
|
|
87
|
+
|
|
88
|
+
Sign with `node:crypto` and call the default export of
|
|
89
|
+
`@t4h.framework/hmac/authorize`. No mock is necessary:
|
|
200
90
|
|
|
201
91
|
```typescript
|
|
202
92
|
import crypto from 'node:crypto'
|
|
93
|
+
|
|
203
94
|
import hmac from '@t4h.framework/hmac/authorize'
|
|
204
95
|
|
|
205
|
-
const payload = '{"event":"order.created"}'
|
|
206
96
|
const signature = crypto
|
|
207
|
-
.createHmac('sha256', '
|
|
97
|
+
.createHmac('sha256', 'test-secret')
|
|
208
98
|
.update(payload)
|
|
209
99
|
.digest('hex')
|
|
210
100
|
|
|
211
101
|
await expect(
|
|
212
|
-
hmac({ secret: '
|
|
102
|
+
hmac({ secret: 'test-secret', signature, rawPayload: payload }),
|
|
213
103
|
).resolves.toBe(true)
|
|
214
104
|
```
|
|
215
105
|
|
|
216
|
-
|
|
106
|
+
For a tolerance test, set the clock with `vi.useFakeTimers()` and
|
|
107
|
+
`vi.setSystemTime(...)`.
|
|
217
108
|
|
|
218
|
-
|
|
109
|
+
## Related skills
|
|
219
110
|
|
|
220
|
-
|
|
111
|
+
- **framework-jwks**: public-key signatures and JWTs.
|
|
112
|
+
- **framework-webhook**: wait for a partner callback that uses this `auth`.
|
|
221
113
|
|
|
222
|
-
##
|
|
223
|
-
|
|
224
|
-
- No HTTP header / query / env extraction for signatures or secrets.
|
|
225
|
-
- No provider presets (no built-in GitHub/Stripe/Shopify header parsing) — you assemble `rawPayload` and pass the header value in.
|
|
226
|
-
- No public-key signatures — for those use **framework-jwks**.
|
|
227
|
-
- No secret decoding: `secret` is used as UTF-8 bytes as given.
|
|
228
|
-
- Does not define **Claims** or **Activities** — only **Authorization** manifests for core `Workflow` / `App`.
|
|
229
|
-
|
|
230
|
-
For workflow orchestration and claims use **framework-core**; for JWKS/JWT verification use **framework-jwks**; for OIDC introspection use **framework-oidc**.
|
|
231
|
-
|
|
232
|
-
---
|
|
114
|
+
## Reference
|
|
233
115
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
3. Attach `new HMACAuthorization(context)` to `Workflow` and/or `App` `authorization` option.
|
|
239
|
-
4. Keep `rawPayload` byte-exact — do not parse and re-stringify JSON.
|
|
240
|
-
5. Set `signaturePrefix` when the provider prefixes the header (`sha256=`).
|
|
241
|
-
6. Set `algorithm` / `signatureFormat` only when they differ from `SHA256` / `hex`.
|
|
242
|
-
7. Add `timestamp` + `toleranceInSeconds` for replay protection, and bind the timestamp into `rawPayload`.
|
|
243
|
-
8. `@t4h.framework/core/no-literal-secret-in-authorization` rejects a literal `secret`. Read it from the `EnvDesign` in `src/constants/environments.ts`.
|
|
244
|
-
9. Expect `false` for deny, throws for misconfiguration.
|
|
116
|
+
- Guide: <https://tech4humans-brasil.github.io/framework/hmac/>
|
|
117
|
+
- Types: `node_modules/@t4h.framework/hmac/dist/hmac.d.ts`
|
|
118
|
+
- Callback `auth` type: `AsyncActivityCallbackAuthHmac` in
|
|
119
|
+
`node_modules/@t4h.framework/core/dist/`
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @t4h.framework/hmac
|
|
2
2
|
|
|
3
|
+
## 0.3.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#276](https://github.com/tech4humans-brasil/framework/pull/276) [`7b38934`](https://github.com/tech4humans-brasil/framework/commit/7b38934e8f9207c98c33afcd76e3a7a4da7e8b14) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The install command gives the `@t4h.framework/core` peer, and the samples import `HMACAuthorization`. The docs tell that `./callback` reads the headers and the secret.
|
|
8
|
+
|
|
9
|
+
## 0.3.10
|
|
10
|
+
|
|
11
|
+
## 0.3.9
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [#257](https://github.com/tech4humans-brasil/framework/pull/257) [`586bfb4`](https://github.com/tech4humans-brasil/framework/commit/586bfb4663f4a478527ee0c8db360603f034d615) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The hmac README is now short and links to the site. The site page now describes the callback `auth`, the results and errors, and how to test the handler.
|
|
16
|
+
|
|
3
17
|
## 0.3.8
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,61 +1,16 @@
|
|
|
1
1
|
# @t4h.framework/hmac
|
|
2
2
|
|
|
3
|
-
HMAC
|
|
3
|
+
HMAC authorization for the T4H Framework. It verifies a shared-secret signature
|
|
4
|
+
over a raw payload, for a workflow, an app or the callbacks of an async
|
|
5
|
+
activity.
|
|
4
6
|
|
|
5
|
-
##
|
|
7
|
+
## Install
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## Exports
|
|
10
|
-
|
|
11
|
-
| Entry | Purpose |
|
|
12
|
-
|------------------|---------|
|
|
13
|
-
| `@t4h.framework/hmac` | `HMACAuthorization`, `HMACAuthorizationContext`, `HMACAlgorithm`, and related types |
|
|
14
|
-
| `@t4h.framework/hmac/authorize` | Default async handler invoked by the runtime with the manifest context |
|
|
15
|
-
| `@t4h.framework/hmac/callback` | Default export `hmacCallback`: `hmac` callback authorizer for the core `AsyncActivityCallback.authorize` (`pending({ auth: { type: 'hmac' } })`) |
|
|
16
|
-
|
|
17
|
-
## `HMACAuthorization`
|
|
18
|
-
|
|
19
|
-
Subclass of [`Authorization`](https://github.com/tech4humans-brasil/framework/tree/main/packages/core) from core. `toManifest()` points the runtime at `@t4h.framework/hmac/authorize` and passes an `HMACAuthorizationContext`.
|
|
20
|
-
|
|
21
|
-
Construct it with a full `HMACAuthorizationContext`. How you obtain `signature`, `rawPayload` and `timestamp` — headers, query, body, env — is up to your app and deployment pipeline; this package does not read HTTP or env by itself.
|
|
22
|
-
|
|
23
|
-
## Context: `HMACAuthorizationContext`
|
|
24
|
-
|
|
25
|
-
| Field | Required | Description |
|
|
26
|
-
|-------|----------|-------------|
|
|
27
|
-
| `secret` | Yes | Shared secret, used as UTF-8 bytes for the HMAC key. Empty string **throws**. |
|
|
28
|
-
| `signature` | Yes | The signature the caller presented. Empty string yields `false`. |
|
|
29
|
-
| `rawPayload` | Yes | The exact bytes that were signed. |
|
|
30
|
-
| `algorithm` | No | A **key** of `HMACAlgorithm` (`'SHA1'`, `'SHA256'`, `'SHA384'`, `'SHA512'`). Defaults to `'SHA256'`. |
|
|
31
|
-
| `signatureFormat` | No | Encoding of `signature`: `'hex'`, `'base64'`, `'base64url'` or `'binary'`. Defaults to `'hex'`. |
|
|
32
|
-
| `signaturePrefix` | No | Prefix to strip before comparing, e.g. `'sha256='` for GitHub-style headers. |
|
|
33
|
-
| `timestamp` | Conditional | When the payload was signed. Required if `toleranceInSeconds` is set. |
|
|
34
|
-
| `toleranceInSeconds` | No | Enables replay protection: rejects signatures whose `timestamp` drifts further than this from now. |
|
|
35
|
-
|
|
36
|
-
## Runtime handler (`hmac/authorize`)
|
|
37
|
-
|
|
38
|
-
The default export is an async function:
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
(options: HMACAuthorizationContext) => Promise<boolean>
|
|
9
|
+
```bash
|
|
10
|
+
npm install @t4h.framework/hmac @t4h.framework/core
|
|
42
11
|
```
|
|
43
12
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
- Returns `false` if `signature` is missing, the digests differ, the expected prefix is absent, or the timestamp is outside the tolerance window.
|
|
47
|
-
- Returns `true` if the digests match.
|
|
48
|
-
- **Throws** on configuration errors: empty `secret`, an `algorithm` that is not a `HMACAlgorithm` key, an unsupported `signatureFormat`, or a `toleranceInSeconds` set without a `timestamp`.
|
|
49
|
-
|
|
50
|
-
The signature is decoded from `signatureFormat` before the comparison, so hex casing and base64 padding differences never cause a false negative.
|
|
51
|
-
|
|
52
|
-
### Replay protection
|
|
53
|
-
|
|
54
|
-
Set `toleranceInSeconds` together with `timestamp` to reject stale signatures. `timestamp` is read as Unix time in seconds when it is all digits, otherwise via `Date.parse` (ISO 8601, for example). The window is symmetric — a sender whose clock runs slightly fast is accepted the same way a slightly slow one is.
|
|
55
|
-
|
|
56
|
-
Note that the timestamp is only checked for freshness. If you want it *bound* to the signature (so an attacker cannot change it), include it in `rawPayload` the same way the sender did — for example `` `${timestamp}.${body}` ``.
|
|
57
|
-
|
|
58
|
-
## Example
|
|
13
|
+
## Usage
|
|
59
14
|
|
|
60
15
|
```typescript
|
|
61
16
|
import { HMACAuthorization } from '@t4h.framework/hmac'
|
|
@@ -67,20 +22,13 @@ const authorization = new HMACAuthorization({
|
|
|
67
22
|
signature: '<signature-from-the-request-header>',
|
|
68
23
|
rawPayload: '<the-exact-request-body>',
|
|
69
24
|
signaturePrefix: 'sha256=',
|
|
70
|
-
timestamp: '<timestamp-from-the-request-header>',
|
|
71
|
-
toleranceInSeconds: 300,
|
|
72
25
|
})
|
|
73
26
|
```
|
|
74
27
|
|
|
75
|
-
Declare `HMAC_SECRET`
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// src/constants/environments.ts
|
|
79
|
-
import { EnvDesign } from '@t4h.framework/core'
|
|
28
|
+
Declare `HMAC_SECRET` with `type: String` in the `EnvDesign` in
|
|
29
|
+
`src/constants/environments.ts`, and attach `authorization` to a `Workflow` or
|
|
30
|
+
an `App`.
|
|
80
31
|
|
|
81
|
-
|
|
82
|
-
HMAC_SECRET: { type: String },
|
|
83
|
-
})
|
|
84
|
-
```
|
|
32
|
+
## Documentation
|
|
85
33
|
|
|
86
|
-
|
|
34
|
+
<https://tech4humans-brasil.github.io/framework/hmac/>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t4h.framework/hmac",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"description": "HMAC module for the T4H Framework authorization",
|
|
5
5
|
"homepage": "https://github.com/tech4humans-brasil/framework/tree/main/packages/hmac",
|
|
6
6
|
"bugs": "https://github.com/tech4humans-brasil/framework/issues",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"test:watch": "vitest"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@t4h.framework/core": "^0.
|
|
46
|
+
"@t4h.framework/core": "^0.21.2",
|
|
47
47
|
"typescript": "^5.9.3",
|
|
48
48
|
"vitest": "^4.0.18"
|
|
49
49
|
},
|