diff --git a/security-case-types/oidc-browser-relying-party/README.md b/security-case-types/oidc-browser-relying-party/README.md index f5bae4f..040e7e7 100644 --- a/security-case-types/oidc-browser-relying-party/README.md +++ b/security-case-types/oidc-browser-relying-party/README.md @@ -1,4 +1,4 @@ -# oidc-browser-relying-party 1.0 +# oidc-browser-relying-party 1.1 An internal web service signs its human users in through NetKingdom (KeyCape) with an OIDC authorization-code client. This type covers the client @@ -126,3 +126,16 @@ Rollback is the exact inverse, in reverse order. The hand-over is removed first, then the registration, then the secret and its policies, then the case's group memberships. Groups are deleted only if this case created them and no other case uses them. + +## History + +- **1.1** (2026-10-10, minor): verification `consumer-enforces-groups`, and + hand-over requires the consumer to enforce `access_groups`, because KeyCape has + no per-client group gate (key-cape type review R2, key-cape 6038483). No new + acknowledgment: the risk class is unchanged. The Forgejo case was re-pinned + before any apply, on the founder's go. +- **1.0 patch** (2026-10-10): R1 redirect URIs refuse `?` and `#`; R2 `groups` + scope always required; R3/R4 documentation (net-kingdom 61ab6bc). + `{client_id:env}` adopted in `bind-env` and its rollback (9a9b8e3). +- **1.0** (2026-10-09): acknowledged via informed-decision memo r4 (hub decision + `e418263e`). diff --git a/security-case-types/oidc-browser-relying-party/examples/example-public-portal.yaml b/security-case-types/oidc-browser-relying-party/examples/example-public-portal.yaml index c76ddb3..5f22610 100644 --- a/security-case-types/oidc-browser-relying-party/examples/example-public-portal.yaml +++ b/security-case-types/oidc-browser-relying-party/examples/example-public-portal.yaml @@ -3,7 +3,7 @@ apiVersion: netkingdom.io/security-case/v0.1 kind: SecurityCase spec: id: example-public-portal - type: oidc-browser-relying-party@1.0 + type: oidc-browser-relying-party@1.1 status: proposed owner: repo: net-kingdom diff --git a/security-case-types/oidc-browser-relying-party/examples/forgejo.yaml b/security-case-types/oidc-browser-relying-party/examples/forgejo.yaml index 79fdb39..45c4f00 100644 --- a/security-case-types/oidc-browser-relying-party/examples/forgejo.yaml +++ b/security-case-types/oidc-browser-relying-party/examples/forgejo.yaml @@ -6,7 +6,7 @@ apiVersion: netkingdom.io/security-case/v0.1 kind: SecurityCase spec: id: forgejo-netkingdom-oidc - type: oidc-browser-relying-party@1.0 + type: oidc-browser-relying-party@1.1 status: proposed owner: repo: railiance-apps diff --git a/security-case-types/oidc-browser-relying-party/tests/test_oidc_browser_relying_party.py b/security-case-types/oidc-browser-relying-party/tests/test_oidc_browser_relying_party.py index 431a3bf..9fac10b 100644 --- a/security-case-types/oidc-browser-relying-party/tests/test_oidc_browser_relying_party.py +++ b/security-case-types/oidc-browser-relying-party/tests/test_oidc_browser_relying_party.py @@ -199,3 +199,13 @@ def test_unknown_format_spec_is_rejected(): t = copy.deepcopy(TYPE) t["spec"]["rollback"][0]["action"] += " {client_id:shout}" assert any("unknown format spec" in m for m in msgs(v.validate_type(t))) + + +def test_consumer_enforces_access_groups(): + # 1.1: KeyCape has no per-client group gate, so the consumer must (key-cape R2). + issues, r = v.render_case(FORGEJO, as_implemented(), DECLS) + assert msgs(issues) == [] + checks = {c["id"]: c for c in r["verification"]} + assert "forge-admins,forge-coulomb-owners,forge-coulomb-readers" in checks["consumer-enforces-groups"]["check"] + hand_over = [s for s in r["follow_through"] if s["id"] == "hand-over"][0] + assert "groups claim" in hand_over["entry_point"] diff --git a/security-case-types/oidc-browser-relying-party/type.yaml b/security-case-types/oidc-browser-relying-party/type.yaml index ea471f6..dac9e5b 100644 --- a/security-case-types/oidc-browser-relying-party/type.yaml +++ b/security-case-types/oidc-browser-relying-party/type.yaml @@ -2,7 +2,7 @@ apiVersion: netkingdom.io/security-case-type/v0.1 kind: SecurityCaseType spec: id: oidc-browser-relying-party - version: "1.0" + version: "1.1" status: acknowledged title: "Browser OIDC relying party for an internal service" purpose: >- @@ -238,7 +238,7 @@ spec: stops the single replica and every client loses sign-in (R1). - id: hand-over owner: "{consumer_owner_repo}" - entry_point: "configure {consuming_service} with issuer {issuer}, client {client_id} and custody bao:platform/workloads/keycape/{client_id}" + entry_point: "configure {consuming_service} with issuer {issuer}, client {client_id} and custody bao:platform/workloads/keycape/{client_id}, and make it require one of {access_groups} in the groups claim before sign-in completes" auth: "the consumer owner's own deployment path" warden_executes: false attended: false @@ -267,6 +267,12 @@ spec: check: "LLDAP groups {access_groups} exist, with exactly the memberships of the accepted case added" expect: present reads_secret_value: false + - id: consumer-enforces-groups + # KeyCape has no per-client group gate; only the consumer can enforce + # access_groups (key-cape type review R2, type 1.1). + check: "{consuming_service}'s OIDC source for {client_id} (owner {consumer_owner_repo}) requires one of {access_groups} in the groups claim (configuration read, no secret value)" + expect: present + reads_secret_value: false - id: secret-delivered when: "client_type == confidential" check: "the delivery for sso/keycape-{client_id}-client reports Ready=True (status only)" diff --git a/security-cases/forgejo-netkingdom-oidc.yaml b/security-cases/forgejo-netkingdom-oidc.yaml index a52629d..b080a25 100644 --- a/security-cases/forgejo-netkingdom-oidc.yaml +++ b/security-cases/forgejo-netkingdom-oidc.yaml @@ -1,15 +1,17 @@ -# First KnownSecurityCase of oidc-browser-relying-party 1.0 (NK-WP-0048-T06). +# First KnownSecurityCase of oidc-browser-relying-party 1.1 (NK-WP-0048-T06). # Parameters are the request the founder accepted on 2026-10-08 # (key-cape docs/forgejo-oidc-client-request.yaml, KEY-WP-0038-T01); that # acceptance carries over as this case's acceptance. One difference: custody is # derived by the type (platform/workloads/keycape/forgejo), not the request's # proposed platform/workloads/forgejo/netkingdom-oidc-client. The founder # acknowledged that derivation with the type (memo r4, 2026-10-09). +# Re-pinned from 1.0 to 1.1 on 2026-10-10 on the founder's go, before any +# apply, so no 1.0 run overlaps (minor change: consumer-enforces-groups). apiVersion: netkingdom.io/security-case/v0.1 kind: SecurityCase spec: id: forgejo-netkingdom-oidc - type: oidc-browser-relying-party@1.0 + type: oidc-browser-relying-party@1.1 status: accepted acceptance: at: "2026-10-08" diff --git a/workplans/NK-WP-0048-security-case-types.md b/workplans/NK-WP-0048-security-case-types.md index ff0802f..bcaa487 100644 --- a/workplans/NK-WP-0048-security-case-types.md +++ b/workplans/NK-WP-0048-security-case-types.md @@ -407,6 +407,15 @@ re-pinning. Also open with key-cape: a KeyCape entry point that validates a rendered client registry without starting the server; until then `keycape-client.py` mirrors the redirect rule. +2026-10-10: founder go for 1.1, so no 1.0 run overlaps. The type adds +verification `consumer-enforces-groups`, and hand-over now requires the consumer +to enforce `access_groups` (minor, canon section 6; risk class unchanged, no new +acknowledgment; history in the type README). The accepted Forgejo case and both +examples are re-pinned to `oidc-browser-relying-party@1.1` before any apply; the +acknowledgment intake keeps `@1.0` as the record of what was acknowledged. +ops-mason's build and ops-warden's case-ref digest predate this and need a +re-render. + ```task id: NK-WP-0048-T07 status: wait