Request groups scope for whynot OIDC role
This commit is contained in:
@@ -58,6 +58,12 @@ openbao:
|
||||
allowed_redirect_uris:
|
||||
- https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback
|
||||
- http://localhost:8250/oidc/callback
|
||||
- http://127.0.0.1:8250/oidc/callback
|
||||
oidc_scopes:
|
||||
- openid
|
||||
- profile
|
||||
- email
|
||||
- groups
|
||||
user_claim: sub
|
||||
groups_claim: groups
|
||||
bound_claims:
|
||||
@@ -104,6 +110,14 @@ verification:
|
||||
- OIDC role read showed the whynot-design bound claim, read policy, and callback URIs.
|
||||
- Metadata read showed catalog-id whynot-design-npm-publish.
|
||||
- Secret field presence check found NPM_AUTH_TOKEN without printing or recording the value.
|
||||
- at: '2026-06-28T11:20:06+00:00'
|
||||
actor: codex
|
||||
kind: non_secret_oidc_role_correction
|
||||
result: applied
|
||||
details:
|
||||
- Positive login reported missing groups claim because the role did not request the groups scope.
|
||||
- Updated auth/netkingdom/role/whynot-design-workload-kv-read with oidc_scopes openid/profile/email/groups.
|
||||
- Added the 127.0.0.1 local CLI callback URI alongside localhost and browser callbacks.
|
||||
lifecycle:
|
||||
deactivate: Disable ops-warden catalog entry and remove or detach auth role policy.
|
||||
rotate: Replace NPM_AUTH_TOKEN value directly in OpenBao and record non-secret rotation
|
||||
|
||||
@@ -98,7 +98,14 @@ Role payload:
|
||||
"role_type": "oidc",
|
||||
"allowed_redirect_uris": [
|
||||
"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback",
|
||||
"http://localhost:8250/oidc/callback"
|
||||
"http://localhost:8250/oidc/callback",
|
||||
"http://127.0.0.1:8250/oidc/callback"
|
||||
],
|
||||
"oidc_scopes": [
|
||||
"openid",
|
||||
"profile",
|
||||
"email",
|
||||
"groups"
|
||||
],
|
||||
"user_claim": "sub",
|
||||
"groups_claim": "groups",
|
||||
@@ -119,7 +126,14 @@ cat >"$role_payload_file" <<'JSON'
|
||||
{
|
||||
"allowed_redirect_uris": [
|
||||
"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback",
|
||||
"http://localhost:8250/oidc/callback"
|
||||
"http://localhost:8250/oidc/callback",
|
||||
"http://127.0.0.1:8250/oidc/callback"
|
||||
],
|
||||
"oidc_scopes": [
|
||||
"openid",
|
||||
"profile",
|
||||
"email",
|
||||
"groups"
|
||||
],
|
||||
"bound_claims": {
|
||||
"groups": [
|
||||
@@ -192,7 +206,7 @@ Only after these are true:
|
||||
- secret metadata confirmed;
|
||||
- policy exists and is scoped to the corrected `coulomb/whynot-design` path;
|
||||
- OIDC role exists and binds only `groups=["whynot-design"]` with approved
|
||||
browser and local CLI callback URIs;
|
||||
browser/local CLI callback URIs and `groups` OIDC scope;
|
||||
- positive verification passed;
|
||||
- negative verification passed;
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ Ops-warden batch follow-up:
|
||||
| Policy file | `openbao/policies/workload-kv-read-whynot-design-npm-publish.hcl` |
|
||||
| OIDC auth mount | `netkingdom` |
|
||||
| OIDC role | `whynot-design-workload-kv-read` |
|
||||
| OIDC callback URIs | `https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback`, `http://localhost:8250/oidc/callback` |
|
||||
| OIDC callback URIs | `https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback`, `http://localhost:8250/oidc/callback`, `http://127.0.0.1:8250/oidc/callback` |
|
||||
| Kubernetes auth role | `whynot-design-workload-kv-read` if an in-cluster service account consumes this lane |
|
||||
| flex-auth ref | `secret.read:whynot-design` if tenant policy requires pre-approval |
|
||||
|
||||
@@ -116,6 +116,17 @@ OpenBao:
|
||||
```text
|
||||
https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback
|
||||
http://localhost:8250/oidc/callback
|
||||
http://127.0.0.1:8250/oidc/callback
|
||||
```
|
||||
|
||||
The role must request these OIDC scopes so KeyCape emits the group claim OpenBao
|
||||
checks:
|
||||
|
||||
```text
|
||||
openid
|
||||
profile
|
||||
email
|
||||
groups
|
||||
```
|
||||
|
||||
The whynot-design pilot claim is confirmed as `groups=whynot-design`. Before
|
||||
@@ -180,7 +191,9 @@ warden access "npm token" \
|
||||
```
|
||||
|
||||
Use `--no-policy` only while the local ops-warden config reports
|
||||
`policy.enabled=false`; remove it once the flex-auth gate is enforced.
|
||||
`policy.enabled=false`; remove it once the flex-auth gate is enforced. If login
|
||||
fails with `groups claim not found`, the OpenBao role is missing the `groups`
|
||||
OIDC scope and must be corrected before retrying.
|
||||
|
||||
Negative verification:
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ workload_kv_read:
|
||||
required:
|
||||
- allowed_redirect_uris
|
||||
allowed_redirect_uris: non-empty list of OpenBao callback URIs accepted by the role
|
||||
groups_claim: requires openbao.auth.oidc_scopes to include groups
|
||||
|
||||
access_frontdoor_readiness:
|
||||
allowed:
|
||||
|
||||
@@ -183,6 +183,19 @@ def validate_workload_kv_read(ccr: dict[str, Any], errors: list[str], warnings:
|
||||
errors.append(
|
||||
f"openbao.auth.allowed_redirect_uris[{index}] must be a non-empty string"
|
||||
)
|
||||
if auth.get("groups_claim"):
|
||||
oidc_scopes = require_list(
|
||||
auth.get("oidc_scopes"), "openbao.auth.oidc_scopes", errors
|
||||
)
|
||||
if "groups" not in oidc_scopes:
|
||||
errors.append(
|
||||
"openbao.auth.oidc_scopes must include 'groups' when groups_claim is set"
|
||||
)
|
||||
for index, scope in enumerate(oidc_scopes):
|
||||
if not isinstance(scope, str) or not scope.strip():
|
||||
errors.append(
|
||||
f"openbao.auth.oidc_scopes[{index}] must be a non-empty string"
|
||||
)
|
||||
policies = [str(policy) for policy in require_list(auth.get("policies"), "openbao.auth.policies", errors)]
|
||||
if policies != [policy_name]:
|
||||
errors.append("openbao.auth.policies must contain exactly openbao.policy_name")
|
||||
@@ -362,6 +375,8 @@ def auth_payload(ccr: dict[str, Any]) -> dict[str, Any]:
|
||||
payload["groups_claim"] = auth["groups_claim"]
|
||||
if auth.get("allowed_redirect_uris"):
|
||||
payload["allowed_redirect_uris"] = auth["allowed_redirect_uris"]
|
||||
if auth.get("oidc_scopes"):
|
||||
payload["oidc_scopes"] = auth["oidc_scopes"]
|
||||
return payload
|
||||
|
||||
|
||||
|
||||
@@ -142,8 +142,13 @@ class CredentialChangeTests(unittest.TestCase):
|
||||
[
|
||||
"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback",
|
||||
"http://localhost:8250/oidc/callback",
|
||||
"http://127.0.0.1:8250/oidc/callback",
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
payload["oidc_scopes"],
|
||||
["openid", "profile", "email", "groups"],
|
||||
)
|
||||
|
||||
def test_apply_plan_refuses_unapproved_ccr(self) -> None:
|
||||
with self.assertRaises(SystemExit):
|
||||
@@ -170,6 +175,8 @@ class CredentialChangeTests(unittest.TestCase):
|
||||
self.assertIn('role_payload_file="$(mktemp)"', rendered)
|
||||
self.assertIn('"bound_claims": {', rendered)
|
||||
self.assertIn('"allowed_redirect_uris": [', rendered)
|
||||
self.assertIn('"oidc_scopes": [', rendered)
|
||||
self.assertIn('"groups"', rendered)
|
||||
self.assertIn(
|
||||
'"https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback"',
|
||||
rendered,
|
||||
|
||||
@@ -191,6 +191,11 @@ subject; do not create an unbounded OIDC role.
|
||||
`workload-kv-read-whynot-design-npm-publish` policy, `ttl=15m`, and the approved
|
||||
browser/local CLI callback URIs.
|
||||
|
||||
**2026-06-28:** Positive verification found the OIDC role was missing
|
||||
`oidc_scopes`, causing OpenBao login to fail with `groups claim not found`.
|
||||
Updated the live role and source CCR to request `openid`, `profile`, `email`,
|
||||
and `groups`, matching the platform-admin OIDC scope shape.
|
||||
|
||||
## T04 - Provision the KV path without exposing the token
|
||||
|
||||
```task
|
||||
|
||||
Reference in New Issue
Block a user