@o3co/create-auth-policy-verifier 0.8.1 → 0.9.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o3co/create-auth-policy-verifier",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Scaffold a new o3co/auth.policy-verifier project from the standalone template",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -43,7 +43,9 @@ OAUTH_JWT_SECRET=$(openssl rand -hex 32) \
43
43
  | `OAUTH_JWT_JWKS_TIMEOUT_MS` | `5000` | JWKS 取得をこの時間で打ち切る |
44
44
  | `OAUTH_JWT_JWKS_COOLDOWN_MS` | `30000` | JWKS 再取得の最小間隔 |
45
45
  | `OAUTH_JWT_JWKS_CACHE_MAX_AGE_MS` | `600000` | 取得した JWKS をキャッシュから返す期間 |
46
- | `OAUTH_JWT_TOKEN_TYPE` | `at+jwt` | 受け入れる `typ` ヘッダ。同じ鍵で署名された id/refresh/logout token を拒否する |
46
+ | `OAUTH_JWT_TOKEN_TYPE` | `at+jwt` | 受け入れる `typ` ヘッダ。同じ鍵で署名された id/refresh/logout token を拒否する。`*` で pin しない(その場合 `audience` を OIDC client id にしてはいけない) |
47
+ | `OAUTH_JWT_AUDIENCE_CLAIM` | `aud` | audience を読む claim。`azp`(Clerk)、`client_id`(Cognito)。`aud` 以外を指定すると `aud` は一切見ない |
48
+ | `OAUTH_AUTHENTICATOR` | `jwt` | subject を確立する token authenticator。他の名前は `main.mts` で module が登録したもの |
47
49
  | `OAUTH_JWT_MODE` | `verify` | `verify` はトークンを完全検証。明示的な `insecure-decode`(テスト専用)は署名検証なしでデコードする — `exp`/`nbf` は引き続き強制される |
48
50
  | `RULE_ON_EMPTY_RULE_SET` | `deny` | ルールが 1 つも集まらなかったときの決定(`deny` \| `allow`) |
49
51
  | `VERIFY_MAX_BATCH_SIZE` | `50` | `POST /verify/batch` の件数上限 |
@@ -43,7 +43,9 @@ Individual values can also be overridden with environment variables.
43
43
  | `OAUTH_JWT_JWKS_TIMEOUT_MS` | `5000` | Abort a JWKS fetch after this long |
44
44
  | `OAUTH_JWT_JWKS_COOLDOWN_MS` | `30000` | Minimum spacing between JWKS fetches |
45
45
  | `OAUTH_JWT_JWKS_CACHE_MAX_AGE_MS` | `600000` | How long a fetched JWKS is served from cache |
46
- | `OAUTH_JWT_TOKEN_TYPE` | `at+jwt` | Accepted `typ` header; rejects id/refresh/logout tokens signed with the same key |
46
+ | `OAUTH_JWT_TOKEN_TYPE` | `at+jwt` | Accepted `typ` header; rejects id/refresh/logout tokens signed with the same key. `*` pins nothing (then `audience` must not be an OIDC client id) |
47
+ | `OAUTH_JWT_AUDIENCE_CLAIM` | `aud` | Claim the audience is read from: `azp` (Clerk), `client_id` (Cognito). Anything but `aud` means `aud` is not consulted at all |
48
+ | `OAUTH_AUTHENTICATOR` | `jwt` | Which token authenticator establishes the subject; another name must be registered by a module in `main.mts` |
47
49
  | `OAUTH_JWT_MODE` | `verify` | `verify` fully verifies tokens; the explicit `insecure-decode` (test-only) decodes without signature verification — `exp`/`nbf` are still enforced |
48
50
  | `RULE_ON_EMPTY_RULE_SET` | `deny` | Decision when no rule is collected (`deny` \| `allow`) |
49
51
  | `VERIFY_MAX_BATCH_SIZE` | `50` | Cap on `POST /verify/batch` entries |
@@ -35,6 +35,15 @@ http {
35
35
  }
36
36
 
37
37
  oauth {
38
+ # Which token authenticator establishes the subject of a decision. "jwt"
39
+ # (the default) is the built-in bearer-JWT path the block below configures.
40
+ # A deployment whose IdP issues opaque tokens, or whose gateway already
41
+ # authenticated the caller, registers its own authenticator in main.mts and
42
+ # names it here — see docs/extending.md, "Writing a token authenticator".
43
+ # With another name the `jwt` block must be omitted — that authenticator's
44
+ # keys go under its own sub-block (oauth.<name> { … }).
45
+ authenticator = "jwt"
46
+ authenticator = ${?OAUTH_AUTHENTICATOR}
38
47
  jwt {
39
48
  algorithm = "HS256"
40
49
  algorithm = ${?OAUTH_JWT_ALGORITHM}
@@ -120,6 +129,19 @@ oauth {
120
129
  # rejects id_tokens, refresh tokens and logout tokens signed with the same key.
121
130
  tokenType = "at+jwt"
122
131
  tokenType = ${?OAUTH_JWT_TOKEN_TYPE}
132
+ # `tokenType = "*"` (exactly that) pins nothing — any typ, or none. For
133
+ # issuers whose tokens carry no typ header (Cognito). The audience is then
134
+ # the only thing telling an access token from an id_token signed with the
135
+ # same key, so `audience` must NOT be an OIDC client id under "*": use an
136
+ # API identifier, or an audienceClaim id_tokens lack (Cognito: client_id).
137
+ # The claim the audience is read from. "aud" (the default) is RFC 9068;
138
+ # "azp" binds a Clerk session token, "client_id" a Cognito access token.
139
+ # The check moves, it never goes away — `audience` above stays required.
140
+ # With anything but "aud" here, `aud` is NOT consulted at all: use it only
141
+ # for issuers that emit no aud, and never name a claim the user can write
142
+ # (Clerk unsafe_metadata, Auth0 user_metadata).
143
+ audienceClaim = "aud"
144
+ audienceClaim = ${?OAUTH_JWT_AUDIENCE_CLAIM}
123
145
 
124
146
  # Bounds on a presented token's own lifetime. Both apply in EVERY mode,
125
147
  # insecure-decode included. `exp` itself is required and has no setting:
@@ -176,6 +198,8 @@ attribute {
176
198
  # can never pass — see the note in the rule block.
177
199
  collectors = [
178
200
  # The token's `scope` claim, split into the list the scope rule reads.
201
+ # `claim = "scp"` reads Okta's scope array instead; pair it with the same
202
+ # `claim` on ResourceActionScopeRuleCollector below (#219).
179
203
  { collector = "PayloadScopeCollector" }
180
204
  # The token's `sub`, which becomes the `subject` on the decision response
181
205
  # and the `sub` field on the audit log line.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o3co/auth-policy-verifier-standalone",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "packageManager": "pnpm@10.29.3",
@@ -89,12 +89,12 @@ describe("loadAppConfig", () => {
89
89
  pathPrefix: "",
90
90
  callerAuth: { header: "x-caller-token" },
91
91
  });
92
- expect(config.oauth.jwt.algorithm).toBe("HS256");
93
- expect(config.oauth.jwt.secret).toBe(TEST_SECRET);
94
- expect(config.oauth.jwt.mode).toBe("verify");
95
- expect(config.oauth.jwt.issuer).toBe("https://issuer.test");
96
- expect(config.oauth.jwt.audience).toBe("https://api.test");
97
- expect(config.oauth.jwt.tokenType).toBe("at+jwt");
92
+ expect(config.oauth.jwt?.algorithm).toBe("HS256");
93
+ expect(config.oauth.jwt?.secret).toBe(TEST_SECRET);
94
+ expect(config.oauth.jwt?.mode).toBe("verify");
95
+ expect(config.oauth.jwt?.issuer).toBe("https://issuer.test");
96
+ expect(config.oauth.jwt?.audience).toBe("https://api.test");
97
+ expect(config.oauth.jwt?.tokenType).toBe("at+jwt");
98
98
  expect(config.verify.maxBatchSize).toBe(50);
99
99
  },
100
100
  );
@@ -170,7 +170,7 @@ describe("loadAppConfig", () => {
170
170
 
171
171
  const config = loadAppConfig(configDirPath, "development");
172
172
 
173
- expect(config.oauth.jwt.mode).toBe("insecure-decode");
173
+ expect(config.oauth.jwt?.mode).toBe("insecure-decode");
174
174
  });
175
175
 
176
176
  it("rejects a boolean-ish OAUTH_JWT_MODE left over from the removed OAUTH_JWT_VALIDATE", () => {
@@ -237,11 +237,11 @@ describe("loadAppConfig — numeric knobs through the real 3-tier resolution (#1
237
237
  const config = loadAppConfig(configDirPath, "development");
238
238
 
239
239
  expect(config.http.port).toBe(3000);
240
- expect(config.oauth.jwt.jwksTimeoutMs).toBe(5000);
241
- expect(config.oauth.jwt.jwksCooldownMs).toBe(30_000);
242
- expect(config.oauth.jwt.jwksCacheMaxAgeMs).toBe(600_000);
243
- expect(config.oauth.jwt.maxTokenAgeSeconds).toBe(86_400);
244
- expect(config.oauth.jwt.clockToleranceSeconds).toBe(0);
240
+ expect(config.oauth.jwt?.jwksTimeoutMs).toBe(5000);
241
+ expect(config.oauth.jwt?.jwksCooldownMs).toBe(30_000);
242
+ expect(config.oauth.jwt?.jwksCacheMaxAgeMs).toBe(600_000);
243
+ expect(config.oauth.jwt?.maxTokenAgeSeconds).toBe(86_400);
244
+ expect(config.oauth.jwt?.clockToleranceSeconds).toBe(0);
245
245
  expect(config.verify.maxBatchSize).toBe(50);
246
246
  // The request limits (#118) travel the same path.
247
247
  expect(config.verify.maxBodyBytes).toBe(65_536);
@@ -272,11 +272,11 @@ describe("loadAppConfig — numeric knobs through the real 3-tier resolution (#1
272
272
  const config = loadAppConfig(configDirPath, "development");
273
273
 
274
274
  expect(config.http.port).toBe(8080);
275
- expect(config.oauth.jwt.jwksTimeoutMs).toBe(2500);
276
- expect(config.oauth.jwt.jwksCooldownMs).toBe(0);
277
- expect(config.oauth.jwt.jwksCacheMaxAgeMs).toBe(120_000);
278
- expect(config.oauth.jwt.maxTokenAgeSeconds).toBe(600);
279
- expect(config.oauth.jwt.clockToleranceSeconds).toBe(60);
275
+ expect(config.oauth.jwt?.jwksTimeoutMs).toBe(2500);
276
+ expect(config.oauth.jwt?.jwksCooldownMs).toBe(0);
277
+ expect(config.oauth.jwt?.jwksCacheMaxAgeMs).toBe(120_000);
278
+ expect(config.oauth.jwt?.maxTokenAgeSeconds).toBe(600);
279
+ expect(config.oauth.jwt?.clockToleranceSeconds).toBe(60);
280
280
  expect(config.verify.maxBatchSize).toBe(25);
281
281
  expect(config.verify.maxBodyBytes).toBe(16_384);
282
282
  expect(config.verify.maxResourceLength).toBe(128);
@@ -1,5 +1,5 @@
1
1
  {
2
- "@o3co/auth.policy-verifier.core": "0.8.1",
3
- "@o3co/auth.policy-verifier.builtins": "0.8.1",
4
- "@o3co/auth.policy-verifier.server": "0.8.1"
2
+ "@o3co/auth.policy-verifier.core": "0.9.0",
3
+ "@o3co/auth.policy-verifier.builtins": "0.9.0",
4
+ "@o3co/auth.policy-verifier.server": "0.9.0"
5
5
  }