@t4h.framework/oidc 0.6.10 → 0.6.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-oidc/SKILL.md +27 -18
- package/CHANGELOG.md +25 -0
- package/README.md +19 -4
- package/dist/oidc-authorize.d.ts.map +1 -1
- package/dist/oidc-authorize.js +26 -8
- package/dist/oidc-authorize.js.map +1 -1
- package/package.json +4 -2
|
@@ -49,18 +49,32 @@ type AuthorizationManifest<TContext extends object = any> = {
|
|
|
49
49
|
|
|
50
50
|
Attach `OIDCAuthorization` on **`App`** or **`Workflow`** options; `toManifest()` embeds `authorization` in the published manifest. The deployed runtime resolves dynamic fields (especially `token`), then loads `@t4h.framework/oidc/authorize` with the manifest `context`.
|
|
51
51
|
|
|
52
|
+
Declare the values in the `EnvDesign` in `src/constants/environments.ts`. The context types `clientSecret` as `string`, so declare the entry with `type: String`, not `type: Secret`. This is an exception to the `type: Secret` rule of **framework-core**.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
// src/constants/environments.ts
|
|
56
|
+
import { EnvDesign } from '@t4h.framework/core'
|
|
57
|
+
|
|
58
|
+
export const environments = EnvDesign({
|
|
59
|
+
OIDC_DISCOVERY_URL: { type: String },
|
|
60
|
+
OIDC_CLIENT: { type: String },
|
|
61
|
+
OIDC_SECRET: { type: String },
|
|
62
|
+
})
|
|
63
|
+
```
|
|
64
|
+
|
|
52
65
|
```typescript
|
|
53
66
|
import { App } from '@t4h.framework/core'
|
|
54
67
|
import { OIDCAuthorization } from '@t4h.framework/oidc'
|
|
55
68
|
|
|
69
|
+
import { environments } from './constants/environments.js'
|
|
70
|
+
|
|
56
71
|
export default new App({
|
|
57
72
|
id: 'my-app',
|
|
58
73
|
authorization: new OIDCAuthorization({
|
|
59
|
-
discoveryUrl:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
token: 'Bearer <incoming-access-token-from-runtime>',
|
|
74
|
+
discoveryUrl: environments.OIDC_DISCOVERY_URL,
|
|
75
|
+
clientId: environments.OIDC_CLIENT,
|
|
76
|
+
clientSecret: environments.OIDC_SECRET,
|
|
77
|
+
token: '<<headers.authorization>>',
|
|
64
78
|
}),
|
|
65
79
|
workflows: [/* ... */],
|
|
66
80
|
})
|
|
@@ -94,14 +108,14 @@ Subclass of `Authorization<OIDCAuthorizationContext>`. Constructor stores contex
|
|
|
94
108
|
|
|
95
109
|
```typescript
|
|
96
110
|
new OIDCAuthorization({
|
|
97
|
-
discoveryUrl:
|
|
98
|
-
clientId:
|
|
99
|
-
clientSecret:
|
|
100
|
-
token: '
|
|
111
|
+
discoveryUrl: environments.OIDC_DISCOVERY_URL,
|
|
112
|
+
clientId: environments.OIDC_CLIENT,
|
|
113
|
+
clientSecret: environments.OIDC_SECRET,
|
|
114
|
+
token: '<<headers.authorization>>',
|
|
101
115
|
})
|
|
102
116
|
```
|
|
103
117
|
|
|
104
|
-
|
|
118
|
+
The package does not read env. `@t4h.framework/core/no-literal-secret-in-authorization` rejects a literal `clientSecret`. Pass `discoveryUrl`, `clientSecret` and the other values from the `EnvDesign` when you construct the instance.
|
|
105
119
|
|
|
106
120
|
### Runtime token placeholder
|
|
107
121
|
|
|
@@ -175,11 +189,8 @@ Always author/runtime-resolve credentials as `Bearer <access-token>` when you ex
|
|
|
175
189
|
1. Add `@t4h.framework/oidc` dependency alongside `@t4h.framework/core`.
|
|
176
190
|
2. Construct `new OIDCAuthorization({ discoveryUrl, clientId, clientSecret, token, ... })`.
|
|
177
191
|
3. Attach to `App` and/or `Workflow` `authorization` option.
|
|
178
|
-
4.
|
|
179
|
-
5.
|
|
180
|
-
6. Pass incoming caller token with `Bearer ` prefix (or use a runtime placeholder like `<<headers.authorization>>`).
|
|
181
|
-
|
|
182
|
-
Do **not** look for OIDC Activities or Claim classes in this package — they do not exist here.
|
|
192
|
+
4. Register a confidential OAuth client allowed to call **introspection** with **`client_secret_basic`**.
|
|
193
|
+
5. Pass incoming caller token with `Bearer ` prefix (or use a runtime placeholder like `<<headers.authorization>>`).
|
|
183
194
|
|
|
184
195
|
---
|
|
185
196
|
|
|
@@ -244,10 +255,8 @@ Workflow-level authorization behavior in process is owned by the **runtime** (ou
|
|
|
244
255
|
|---------|-------------|
|
|
245
256
|
| Raw JWT without `Bearer ` prefix | Denied up front; no provider request is made |
|
|
246
257
|
| Expecting env auto-wiring | Secrets/URLs must be set when constructing `OIDCAuthorization` |
|
|
247
|
-
| Missing `introspection_endpoint` on IdP | Discovery
|
|
258
|
+
| Missing `introspection_endpoint` on IdP | The handler throws `Discovery response has no introspection_endpoint` (`oidc-authorize.ts`) |
|
|
248
259
|
| Treating `false` as HTTP error | `false` means inactive token; errors are thrown separately |
|
|
249
|
-
| Adding OIDC as a Claim provider | Wrong abstraction — use `Authorization` on App/Workflow |
|
|
250
|
-
| Importing handler from main entry | Use `@t4h.framework/oidc/authorize` (default export) |
|
|
251
260
|
|
|
252
261
|
---
|
|
253
262
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @t4h.framework/oidc
|
|
2
2
|
|
|
3
|
+
## 0.6.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#241](https://github.com/tech4humans-brasil/framework/pull/241) [`f6b8454`](https://github.com/tech4humans-brasil/framework/commit/f6b845484d22e7c8e7240fe2e113f1cdea7c8ce3) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - Adds seven rules to `@t4h.framework/core/eslint` and turns them on in `configs.recommended`:
|
|
8
|
+
- `no-io-in-workflow` reports `fetch` and calls to network, file, database and mail modules in a workflow and in the `constructor`, `toInput` and `toOutput` of an activity. It also reports `zlib` and `stream` calls there, because they work on the content in memory on each replay.
|
|
9
|
+
- `no-binary-subclass` reports a class that extends `Binary`.
|
|
10
|
+
- `no-framework-activity-in-app` reports `new` and `extends` on an activity of a framework package other than the core.
|
|
11
|
+
- `no-testing-import-in-source` reports an import of a `@t4h.framework/<package>/testing` entry out of a test file.
|
|
12
|
+
- `require-workflow-id` reports a `Workflow` with no `id`.
|
|
13
|
+
- `no-secret-to-string` reports a `Secret` that becomes a string.
|
|
14
|
+
- `no-literal-secret-in-authorization` reports a string literal in the `secret` or `clientSecret` field of an `Authorization`.
|
|
15
|
+
|
|
16
|
+
The package skills now name these rules in place of the text that the rules enforce. The docs and skills give each `Workflow` an `id`, read the HMAC secret from `EnvDesign`, and do not document a custom HTTP activity.
|
|
17
|
+
|
|
18
|
+
- [#243](https://github.com/tech4humans-brasil/framework/pull/243) [`0ffd663`](https://github.com/tech4humans-brasil/framework/commit/0ffd663ce8369edd7b54206cb3216568ad1de471) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The README examples read each value from `environments`, an `EnvDesign` in `src/constants/environments.ts`, and pass each credential as a `Secret`. They no longer use `process.env`, the raw `env` from the core or a literal in a `Secret` field. The jwks README describes the real context fields (`signature`, `rawPayload`) and says that a token that `jwt.verify` rejects throws.
|
|
19
|
+
|
|
20
|
+
- [#240](https://github.com/tech4humans-brasil/framework/pull/240) [`0606115`](https://github.com/tech4humans-brasil/framework/commit/0606115e2e0baa994e9f0ed3559ee54ecac88e44) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The skills do not repeat a rule that a lint rule, `tsc` or a runtime error already enforces. Each such rule is now one line that names the gate. The examples that broke a gate (`process.env`, `as any`, an inline secret, an `EnvDesign` in a workflow file, `ref.output` before `wait()`) are correct now.
|
|
21
|
+
|
|
22
|
+
- [#228](https://github.com/tech4humans-brasil/framework/pull/228) [`d29e1ab`](https://github.com/tech4humans-brasil/framework/commit/d29e1ab29a039b13c3e4b075472bd615566ae75e) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - Uses exact types and `unknown` in place of `any`, and checks external data with guards.
|
|
23
|
+
- harness: the fix-issues prompt lists each sensor as `- checkTypes: PASS`. Before, it printed `- 0: [object Object]`.
|
|
24
|
+
- hmac: an algorithm name such as `constructor` throws `Unsupported algorithm`.
|
|
25
|
+
- oidc: a discovery response with no `introspection_endpoint`, or a null introspection body, throws a clear error.
|
|
26
|
+
- `HttpRequestActivity.toOutput` takes `HttpRequestActivityOutput` in place of `any`.
|
|
27
|
+
|
|
3
28
|
## 0.6.10
|
|
4
29
|
|
|
5
30
|
## 0.6.9
|
package/README.md
CHANGED
|
@@ -52,14 +52,29 @@ There is no built-in mapping from environment variables; all configuration comes
|
|
|
52
52
|
|
|
53
53
|
## Example
|
|
54
54
|
|
|
55
|
+
Declare the values in the `EnvDesign` in `src/constants/environments.ts`. The context types `clientSecret` as `string`, so declare the entry with `type: String`, not `type: Secret`.
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// src/constants/environments.ts
|
|
59
|
+
import { EnvDesign } from '@t4h.framework/core'
|
|
60
|
+
|
|
61
|
+
export const environments = EnvDesign({
|
|
62
|
+
OIDC_DISCOVERY_URL: { type: String },
|
|
63
|
+
OIDC_CLIENT: { type: String },
|
|
64
|
+
OIDC_SECRET: { type: String },
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
55
68
|
```typescript
|
|
56
69
|
import { OIDCAuthorization } from '@t4h.framework/oidc'
|
|
57
70
|
|
|
71
|
+
import { environments } from './constants/environments.js'
|
|
72
|
+
|
|
58
73
|
const authorization = new OIDCAuthorization({
|
|
59
|
-
discoveryUrl:
|
|
60
|
-
clientId:
|
|
61
|
-
clientSecret:
|
|
62
|
-
token: '
|
|
74
|
+
discoveryUrl: environments.OIDC_DISCOVERY_URL,
|
|
75
|
+
clientId: environments.OIDC_CLIENT,
|
|
76
|
+
clientSecret: environments.OIDC_SECRET,
|
|
77
|
+
token: '<<headers.authorization>>',
|
|
63
78
|
})
|
|
64
79
|
```
|
|
65
80
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oidc-authorize.d.ts","sourceRoot":"","sources":["../src/oidc-authorize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"oidc-authorize.d.ts","sourceRoot":"","sources":["../src/oidc-authorize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAA;AAyCzD,wBAA8B,IAAI,CAChC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,OAAO,CAAC,CA8ClB"}
|
package/dist/oidc-authorize.js
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
const BEARER_CREDENTIAL = /^Bearer +(?<token>[^ ]+) *$/i;
|
|
2
|
+
const CREDENTIAL_PREFIX = 'value=';
|
|
2
3
|
function getAccessToken(token) {
|
|
3
4
|
const match = BEARER_CREDENTIAL.exec(token);
|
|
4
5
|
return match?.groups?.token ?? null;
|
|
5
6
|
}
|
|
6
7
|
function encodeCredential(value) {
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
return new URLSearchParams({ value })
|
|
9
|
+
.toString()
|
|
10
|
+
.slice(CREDENTIAL_PREFIX.length);
|
|
11
|
+
}
|
|
12
|
+
function getIntrospectionEndpoint(discovery) {
|
|
13
|
+
if (typeof discovery === 'object' &&
|
|
14
|
+
discovery !== null &&
|
|
15
|
+
'introspection_endpoint' in discovery &&
|
|
16
|
+
typeof discovery.introspection_endpoint === 'string')
|
|
17
|
+
return discovery.introspection_endpoint;
|
|
18
|
+
throw new Error('Discovery response has no introspection_endpoint');
|
|
19
|
+
}
|
|
20
|
+
function isActive(introspection) {
|
|
21
|
+
if (introspection === null)
|
|
22
|
+
throw new Error('Introspection response body is null');
|
|
23
|
+
return (typeof introspection === 'object' &&
|
|
24
|
+
'active' in introspection &&
|
|
25
|
+
introspection.active === true);
|
|
9
26
|
}
|
|
10
27
|
export default async function oidc(context) {
|
|
11
28
|
const accessToken = getAccessToken(context.token);
|
|
@@ -18,10 +35,11 @@ export default async function oidc(context) {
|
|
|
18
35
|
},
|
|
19
36
|
});
|
|
20
37
|
if (!discoveryResponse.ok)
|
|
21
|
-
throw new Error(`Discovery request failed: ${discoveryResponse.status} ${discoveryResponse.statusText}`);
|
|
22
|
-
const discovery =
|
|
38
|
+
throw new Error(`Discovery request failed: ${String(discoveryResponse.status)} ${discoveryResponse.statusText}`);
|
|
39
|
+
const discovery = await discoveryResponse.json();
|
|
40
|
+
const introspectionEndpoint = getIntrospectionEndpoint(discovery);
|
|
23
41
|
const credentials = Buffer.from(`${encodeCredential(context.clientId)}:${encodeCredential(context.clientSecret)}`).toString('base64');
|
|
24
|
-
const introspectionResponse = await fetch(
|
|
42
|
+
const introspectionResponse = await fetch(introspectionEndpoint, {
|
|
25
43
|
body: new URLSearchParams({
|
|
26
44
|
token: accessToken,
|
|
27
45
|
token_type_hint: 'access_token',
|
|
@@ -34,8 +52,8 @@ export default async function oidc(context) {
|
|
|
34
52
|
},
|
|
35
53
|
});
|
|
36
54
|
if (!introspectionResponse.ok)
|
|
37
|
-
throw new Error(`Introspection request failed: ${introspectionResponse.status} ${introspectionResponse.statusText}`);
|
|
38
|
-
const introspection =
|
|
39
|
-
return introspection
|
|
55
|
+
throw new Error(`Introspection request failed: ${String(introspectionResponse.status)} ${introspectionResponse.statusText}`);
|
|
56
|
+
const introspection = await introspectionResponse.json();
|
|
57
|
+
return isActive(introspection);
|
|
40
58
|
}
|
|
41
59
|
//# sourceMappingURL=oidc-authorize.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oidc-authorize.js","sourceRoot":"","sources":["../src/oidc-authorize.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"oidc-authorize.js","sourceRoot":"","sources":["../src/oidc-authorize.ts"],"names":[],"mappings":"AAEA,MAAM,iBAAiB,GAAG,8BAA8B,CAAA;AAExD,MAAM,iBAAiB,GAAG,QAAQ,CAAA;AAElC,SAAS,cAAc,CAAC,KAAa;IACnC,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAE3C,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,IAAI,CAAA;AACrC,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAa;IACrC,OAAO,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,CAAC;SAClC,QAAQ,EAAE;SACV,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAA;AACpC,CAAC;AAED,SAAS,wBAAwB,CAAC,SAAkB;IAClD,IACE,OAAO,SAAS,KAAK,QAAQ;QAC7B,SAAS,KAAK,IAAI;QAClB,wBAAwB,IAAI,SAAS;QACrC,OAAO,SAAS,CAAC,sBAAsB,KAAK,QAAQ;QAEpD,OAAO,SAAS,CAAC,sBAAsB,CAAA;IAEzC,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAA;AACrE,CAAC;AAED,SAAS,QAAQ,CAAC,aAAsB;IACtC,IAAI,aAAa,KAAK,IAAI;QACxB,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;IAExD,OAAO,CACL,OAAO,aAAa,KAAK,QAAQ;QACjC,QAAQ,IAAI,aAAa;QACzB,aAAa,CAAC,MAAM,KAAK,IAAI,CAC9B,CAAA;AACH,CAAC;AAED,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,IAAI,CAChC,OAAiC;IAEjC,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAEjD,IAAI,CAAC,WAAW;QAAE,OAAO,KAAK,CAAA;IAE9B,MAAM,iBAAiB,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE;QAC1D,MAAM,EAAE,KAAK;QACb,OAAO,EAAE;YACP,MAAM,EAAE,kBAAkB;SAC3B;KACF,CAAC,CAAA;IAEF,IAAI,CAAC,iBAAiB,CAAC,EAAE;QACvB,MAAM,IAAI,KAAK,CACb,6BAA6B,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC,IAAI,iBAAiB,CAAC,UAAU,EAAE,CAChG,CAAA;IAEH,MAAM,SAAS,GAAY,MAAM,iBAAiB,CAAC,IAAI,EAAE,CAAA;IAEzD,MAAM,qBAAqB,GAAG,wBAAwB,CAAC,SAAS,CAAC,CAAA;IAEjE,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAC7B,GAAG,gBAAgB,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAClF,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAEpB,MAAM,qBAAqB,GAAG,MAAM,KAAK,CAAC,qBAAqB,EAAE;QAC/D,IAAI,EAAE,IAAI,eAAe,CAAC;YACxB,KAAK,EAAE,WAAW;YAClB,eAAe,EAAE,cAAc;SAChC,CAAC;QACF,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,mCAAmC;YACnD,MAAM,EAAE,kBAAkB;YAC1B,aAAa,EAAE,SAAS,WAAW,EAAE;SACtC;KACF,CAAC,CAAA;IAEF,IAAI,CAAC,qBAAqB,CAAC,EAAE;QAC3B,MAAM,IAAI,KAAK,CACb,iCAAiC,MAAM,CAAC,qBAAqB,CAAC,MAAM,CAAC,IAAI,qBAAqB,CAAC,UAAU,EAAE,CAC5G,CAAA;IAEH,MAAM,aAAa,GAAY,MAAM,qBAAqB,CAAC,IAAI,EAAE,CAAA;IAEjE,OAAO,QAAQ,CAAC,aAAa,CAAC,CAAA;AAChC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t4h.framework/oidc",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.11",
|
|
4
4
|
"description": "OIDC module for the T4H Framework authorization",
|
|
5
5
|
"homepage": "https://github.com/tech4humans-brasil/framework/tree/main/packages/oidc",
|
|
6
6
|
"bugs": "https://github.com/tech4humans-brasil/framework/issues",
|
|
@@ -32,12 +32,14 @@
|
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "tsc --project tsconfig.build.json",
|
|
34
34
|
"check-types": "tsc --noEmit",
|
|
35
|
+
"check-package": "publint --strict && attw --pack . --profile esm-only",
|
|
36
|
+
"lint": "eslint . --max-warnings 0",
|
|
35
37
|
"prepublishOnly": "yarn build",
|
|
36
38
|
"test": "vitest run",
|
|
37
39
|
"test:watch": "vitest"
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
40
|
-
"@t4h.framework/core": "^0.
|
|
42
|
+
"@t4h.framework/core": "^0.20.0",
|
|
41
43
|
"typescript": "^5.9.3",
|
|
42
44
|
"vitest": "^4.0.18"
|
|
43
45
|
},
|