Deploy KeyCape client MFA override for coulomb-social
Register coulomb-social with mfaRequired: false, roll key-cape image that honors client policy, and track NK-WP-0025 public registration orchestration.
This commit is contained in:
parent
74faee98c4
commit
0e3a24d888
6 changed files with 278 additions and 12 deletions
|
|
@ -13,9 +13,10 @@
|
||||||
| workplan | NK-WP-0009 | backlog | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md |
|
| workplan | NK-WP-0009 | backlog | — | workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md |
|
||||||
| workplan | NK-WP-0011 | backlog | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
| workplan | NK-WP-0011 | backlog | — | workplans/NK-WP-0011-enterprise-federation-saml.md |
|
||||||
| workplan | NK-WP-0021 | finished | — | workplans/NK-WP-0021-activity-core-ops-sso-operators.md |
|
| workplan | NK-WP-0021 | finished | — | workplans/NK-WP-0021-activity-core-ops-sso-operators.md |
|
||||||
| workplan | NK-WP-0022 | active | — | workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md |
|
| workplan | NK-WP-0022 | blocked | — | workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md |
|
||||||
| workplan | NK-WP-0023 | finished | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md |
|
| workplan | NK-WP-0023 | finished | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md |
|
||||||
| workplan | NK-WP-0024 | backlog | — | workplans/NK-WP-0024-user-engine-portal-integration-expansion.md |
|
| workplan | NK-WP-0024 | active | — | workplans/NK-WP-0024-user-engine-portal-integration-expansion.md |
|
||||||
|
| workplan | NK-WP-0025 | active | — | workplans/NK-WP-0025-public-self-registration-and-application-jit.md |
|
||||||
| task | ADHOC-2026-07-02-T01 | done | — | workplans/ADHOC-2026-07-02.md |
|
| task | ADHOC-2026-07-02-T01 | done | — | workplans/ADHOC-2026-07-02.md |
|
||||||
| task | ADHOC-2026-07-02-T02 | done | — | workplans/ADHOC-2026-07-02.md |
|
| task | ADHOC-2026-07-02-T02 | done | — | workplans/ADHOC-2026-07-02.md |
|
||||||
| task | NET-WP-0020-T01 | done | — | workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md |
|
| task | NET-WP-0020-T01 | done | — | workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md |
|
||||||
|
|
@ -58,9 +59,14 @@
|
||||||
| task | NK-WP-0023-T06 | done | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md |
|
| task | NK-WP-0023-T06 | done | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md |
|
||||||
| task | NK-WP-0023-T07 | done | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md |
|
| task | NK-WP-0023-T07 | done | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md |
|
||||||
| task | NK-WP-0023-T08 | done | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md |
|
| task | NK-WP-0023-T08 | done | — | workplans/NK-WP-0023-user-engine-portal-platform-integration.md |
|
||||||
| task | NK-WP-0024-T01 | todo | — | workplans/NK-WP-0024-user-engine-portal-integration-expansion.md |
|
| task | NK-WP-0024-T01 | progress | — | workplans/NK-WP-0024-user-engine-portal-integration-expansion.md |
|
||||||
| task | NK-WP-0024-T02 | todo | — | workplans/NK-WP-0024-user-engine-portal-integration-expansion.md |
|
| task | NK-WP-0024-T02 | progress | — | workplans/NK-WP-0024-user-engine-portal-integration-expansion.md |
|
||||||
| task | NK-WP-0024-T03 | todo | — | workplans/NK-WP-0024-user-engine-portal-integration-expansion.md |
|
| task | NK-WP-0024-T03 | progress | — | workplans/NK-WP-0024-user-engine-portal-integration-expansion.md |
|
||||||
| task | NK-WP-0024-T04 | todo | — | workplans/NK-WP-0024-user-engine-portal-integration-expansion.md |
|
| task | NK-WP-0024-T04 | progress | — | workplans/NK-WP-0024-user-engine-portal-integration-expansion.md |
|
||||||
|
| task | NK-WP-0025-T01 | done | — | workplans/NK-WP-0025-public-self-registration-and-application-jit.md |
|
||||||
|
| task | NK-WP-0025-T02 | progress | — | workplans/NK-WP-0025-public-self-registration-and-application-jit.md |
|
||||||
|
| task | NK-WP-0025-T03 | progress | — | workplans/NK-WP-0025-public-self-registration-and-application-jit.md |
|
||||||
|
| task | NK-WP-0025-T04 | progress | — | workplans/NK-WP-0025-public-self-registration-and-application-jit.md |
|
||||||
|
| task | NK-WP-0025-T05 | todo | — | workplans/NK-WP-0025-public-self-registration-and-application-jit.md |
|
||||||
| intake | NK-IN-0001 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md |
|
| intake | NK-IN-0001 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md |
|
||||||
| intake | NK-IN-0002 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md |
|
| intake | NK-IN-0002 | closed | blue | docs/intakes/activity-core-ops-sso-operators.md |
|
||||||
|
|
|
||||||
106
docs/public-registration-orchestration-contract.md
Normal file
106
docs/public-registration-orchestration-contract.md
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
# Public registration orchestration contract
|
||||||
|
|
||||||
|
Status: implementation contract for NK-WP-0025-T02 and USER-WP-0022-T01/T02.
|
||||||
|
|
||||||
|
## Boundary
|
||||||
|
|
||||||
|
`user-engine` owns the public registration state machine. It never accepts an
|
||||||
|
LLDAP password or an LLDAP administrator credential. A verification service
|
||||||
|
proves mailbox control; the existing identity-provisioner creates the ordinary
|
||||||
|
LLDAP identity and returns a provider-owned, single-use password-setup URL.
|
||||||
|
|
||||||
|
The public browser is not an authenticated user-engine actor. It receives only
|
||||||
|
opaque registration and verification handles. A verified applicant actor is
|
||||||
|
constructed from consumed verification evidence and is authorized only for
|
||||||
|
the registration instance named by that evidence.
|
||||||
|
|
||||||
|
## Public flow
|
||||||
|
|
||||||
|
1. `POST /api/v1/public/registrations`
|
||||||
|
accepts `username`, `email`, `display_name`, `client_id`, and optional
|
||||||
|
`tenant_hint`. Client and tenant values must be registered allow-list
|
||||||
|
entries; browser-provided return URLs are rejected.
|
||||||
|
2. The response is always the same `202 Accepted` envelope for syntactically
|
||||||
|
valid requests, whether the username/email is new, duplicated, throttled,
|
||||||
|
or already pending. It contains no existence result.
|
||||||
|
3. user-engine records an expiring registration intent and emits
|
||||||
|
`registration.verification_requested` through its transactional outbox.
|
||||||
|
The mail receiver obtains the recipient and an opaque verification handle;
|
||||||
|
the general event receiver gets only `recipient_present=true`.
|
||||||
|
4. `POST /api/v1/public/registrations/verify` consumes the opaque handle.
|
||||||
|
Verification is single-use, expiry checked, purpose bound, and returns
|
||||||
|
normalized mailbox evidence bound to the registration ID and submitted
|
||||||
|
username.
|
||||||
|
5. user-engine attaches the verified email factor, completes registration,
|
||||||
|
and calls identity-provisioner using the registration ID as the stable
|
||||||
|
idempotency key. Only the baseline ordinary-user group may be requested.
|
||||||
|
6. The provisioner creates or resumes exactly one LLDAP identity, links the
|
||||||
|
provider subject to the user-engine user, and returns its single-use
|
||||||
|
password-setup URL. Provider collision is a reconciliation result, never a
|
||||||
|
second privileged identity.
|
||||||
|
7. After provider password setup, the browser starts a fresh KeyCape
|
||||||
|
authorization-code/PKCE flow for the allow-listed client. Registration
|
||||||
|
state is never accepted as an application session.
|
||||||
|
|
||||||
|
## Required state
|
||||||
|
|
||||||
|
Registration intent persistence must include:
|
||||||
|
|
||||||
|
- opaque registration ID and lifecycle status;
|
||||||
|
- normalized username and email hash, with display values separately
|
||||||
|
protected from general event/audit output;
|
||||||
|
- registered client ID and tenant;
|
||||||
|
- verification handle digest, issued/expiry/consumed timestamps, attempt
|
||||||
|
counters, and purpose;
|
||||||
|
- provisioning idempotency key and external provider result;
|
||||||
|
- allow-listed post-setup authorization target identifier, not a raw URL;
|
||||||
|
- correlation ID and redacted failure/compensation state.
|
||||||
|
|
||||||
|
No password, verification token plaintext, LLDAP admin credential, MFA seed,
|
||||||
|
or provider bearer token may enter registration state, logs, audit, State Hub,
|
||||||
|
or the general event lane.
|
||||||
|
|
||||||
|
## Validation and abuse controls
|
||||||
|
|
||||||
|
- Usernames use the LLDAP-compatible canonical grammar, length limit, and
|
||||||
|
reserved-name list; case folding happens before uniqueness checks.
|
||||||
|
- Email is normalized for comparison without provider-specific alias
|
||||||
|
rewriting. Mailbox verification proves control but does not authorize
|
||||||
|
linking to an existing user.
|
||||||
|
- Rate limits apply per source network bucket, normalized email digest, and
|
||||||
|
username digest. Responses and timing remain non-enumerating.
|
||||||
|
- CSRF applies to browser form submission; JSON callers additionally require
|
||||||
|
an allow-listed client registration and idempotency key.
|
||||||
|
- Verification handles are random, high entropy, stored only as a digest,
|
||||||
|
purpose-bound, expiring, and atomically consumed.
|
||||||
|
- Duplicate email/username, expired/replayed verification, and provider
|
||||||
|
conflict enter explicit reconciliation or terminal states. They never fall
|
||||||
|
back to auto-linking by email.
|
||||||
|
|
||||||
|
## Provider contract
|
||||||
|
|
||||||
|
The identity-provisioner request is the existing `ProvisioningRequest` plus a
|
||||||
|
validated canonical username. The response remains `ProvisioningResult` with
|
||||||
|
`external_subject`, `status`, and optional `password_setup_url`.
|
||||||
|
|
||||||
|
For public registration the adapter must guarantee:
|
||||||
|
|
||||||
|
- the same idempotency key returns the same external subject;
|
||||||
|
- baseline membership only; no tenant-admin or platform groups;
|
||||||
|
- password setup is provider-owned, single-use, and expiring;
|
||||||
|
- partial creation is resumable/reconcilable and observable without exposing
|
||||||
|
credentials;
|
||||||
|
- the external subject is linked before registration reports completion.
|
||||||
|
|
||||||
|
## Activation gates
|
||||||
|
|
||||||
|
Public registration stays disabled until all of the following pass together:
|
||||||
|
|
||||||
|
1. verification issuer and mail receiver conformance, including replay and
|
||||||
|
expiry;
|
||||||
|
2. user-engine persistence migration and anonymous-route abuse controls;
|
||||||
|
3. flex-auth policy limiting the verified applicant actor to its registration;
|
||||||
|
4. identity-provisioner canonical-username/idempotency conformance;
|
||||||
|
5. KeyCape fresh-login return flow and client allow-list checks;
|
||||||
|
6. disposable-user live tests and cleanup on railiance01.
|
||||||
|
|
||||||
|
|
@ -54,7 +54,7 @@ spec:
|
||||||
# 2026-05-24: direct-imported into railiance01 k3s for the
|
# 2026-05-24: direct-imported into railiance01 k3s for the
|
||||||
# bootstrap-console OIDC/MFA rollout. Use IfNotPresent while the
|
# bootstrap-console OIDC/MFA rollout. Use IfNotPresent while the
|
||||||
# HTTP registry push/pull path is being cleaned up.
|
# HTTP registry push/pull path is being cleaned up.
|
||||||
image: key-cape:90a2078
|
image: key-cape:mfa-client-20260809
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
|
|
||||||
ports:
|
ports:
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,10 @@ CLIENT = {
|
||||||
"allowedScopes": ["openid", "profile", "email", "groups"],
|
"allowedScopes": ["openid", "profile", "email", "groups"],
|
||||||
"grantTypes": ["authorization_code"],
|
"grantTypes": ["authorization_code"],
|
||||||
"clientType": "public",
|
"clientType": "public",
|
||||||
|
# Ordinary coulomb.social sessions are AAL1 unless the client explicitly
|
||||||
|
# requests AAL2 through acr_values. Platform/admin clients keep their
|
||||||
|
# existing provider-driven mandatory MFA behavior.
|
||||||
|
"mfaRequired": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,11 @@ type: workplan
|
||||||
title: "Cut over NetKingdom identity to railiance01 and retire CoulombCore"
|
title: "Cut over NetKingdom identity to railiance01 and retire CoulombCore"
|
||||||
domain: infotech
|
domain: infotech
|
||||||
repo: net-kingdom
|
repo: net-kingdom
|
||||||
status: active
|
status: blocked
|
||||||
owner: codex
|
owner: codex
|
||||||
topic_slug: netkingdom
|
topic_slug: netkingdom
|
||||||
created: "2026-07-27"
|
created: "2026-07-27"
|
||||||
updated: "2026-07-28"
|
updated: "2026-08-08"
|
||||||
depends_on:
|
depends_on:
|
||||||
- USER-WP-0020
|
- USER-WP-0020
|
||||||
- NK-WP-0023
|
- NK-WP-0023
|
||||||
|
|
@ -313,9 +313,10 @@ Done when railiance01 is the sole authoritative identity stack, all evidence
|
||||||
is reconciled, and the workplan is marked finished.
|
is reconciled, and the workplan is marked finished.
|
||||||
|
|
||||||
Retention gate: keep the reversible CoulombCore identity resources through at
|
Retention gate: keep the reversible CoulombCore identity resources through at
|
||||||
least 2026-08-29. T08 then requires a successful railiance01 restore/restart
|
least 2026-08-29. The workplan is blocked until that review date, when T08 can
|
||||||
drill and a new explicit approval for destructive deletion. Until both occur,
|
be checked for readiness to finish. Reaching the date does not authorize
|
||||||
the workplan remains active with only T08 waiting.
|
deletion or completion: T08 still requires a successful railiance01
|
||||||
|
restore/restart drill and new explicit approval for destructive deletion.
|
||||||
|
|
||||||
## Safety gates
|
## Safety gates
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
---
|
||||||
|
id: NK-WP-0025
|
||||||
|
type: workplan
|
||||||
|
title: "Provide NetKingdom self-registration and application first-login provisioning"
|
||||||
|
domain: infotech
|
||||||
|
repo: net-kingdom
|
||||||
|
status: active
|
||||||
|
owner: codex
|
||||||
|
topic_slug: netkingdom
|
||||||
|
created: "2026-08-09"
|
||||||
|
updated: "2026-08-09"
|
||||||
|
depends_on:
|
||||||
|
- NK-WP-0024
|
||||||
|
- USER-WP-0022
|
||||||
|
- KEY-WP-0008
|
||||||
|
- CSOC-WP-0003
|
||||||
|
state_hub_workstream_id: "1638183b-e2cb-4da2-92b8-d39592d62e86"
|
||||||
|
---
|
||||||
|
|
||||||
|
# NK-WP-0025 - public registration and application JIT provisioning
|
||||||
|
|
||||||
|
Provide two safe entry paths for ordinary users of NetKingdom-enabled
|
||||||
|
applications, beginning with coulomb.social:
|
||||||
|
|
||||||
|
1. an existing LLDAP user signs in through KeyCape and receives an
|
||||||
|
application-local profile on first successful login; and
|
||||||
|
2. a new person self-registers through a NetKingdom-owned flow, verifies the
|
||||||
|
address, establishes an LLDAP login, then continues through the same
|
||||||
|
application first-login path.
|
||||||
|
|
||||||
|
The design is additive to coulomb.social's local-account model. NetKingdom
|
||||||
|
owns authentication and identity lifecycle; coulomb.social/helix-forge owns
|
||||||
|
its application profile, preferences, content, and application authorization.
|
||||||
|
An OIDC subject is linked to an application profile, not merged with an
|
||||||
|
unverified email-only local account.
|
||||||
|
|
||||||
|
## T01 - Ratify identity and account-linking semantics
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: NK-WP-0025-T01
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
state_hub_task_id: "523d5894-6ab6-4097-a28c-0f77880744b1"
|
||||||
|
```
|
||||||
|
|
||||||
|
Record the source-of-truth split, stable key `(issuer, subject)`, optional
|
||||||
|
verified-email matching rules, explicit-link requirements for pre-existing
|
||||||
|
local accounts, unlink/recovery behavior, username/display-name handling, and
|
||||||
|
collision policy for `tegwick` and `bernd.worsch@gmail.com`.
|
||||||
|
|
||||||
|
Done when email alone cannot silently take over an existing application
|
||||||
|
account and the standalone application-account decision remains intact.
|
||||||
|
|
||||||
|
Implemented by coulomb.social's unique `(issuer, subject)` Member binding and
|
||||||
|
idempotent first-login creation. Email and username remain mutable profile
|
||||||
|
attributes and are not account-link keys.
|
||||||
|
|
||||||
|
## T02 - Provide secure LLDAP self-registration orchestration
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: NK-WP-0025-T02
|
||||||
|
status: progress
|
||||||
|
priority: high
|
||||||
|
state_hub_task_id: "bc220e2b-4cd1-4dcf-b107-12ef3595fe14"
|
||||||
|
```
|
||||||
|
|
||||||
|
Expose a NetKingdom registration service behind user-engine. Validate and
|
||||||
|
normalize username/email, verify mailbox control, create the LLDAP identity
|
||||||
|
through the existing privileged provisioning adapter, establish password via
|
||||||
|
a one-time provider-owned handoff, assign only the ordinary baseline group,
|
||||||
|
and support idempotent resume/expiry/cleanup. Browsers and user-engine must
|
||||||
|
never receive LLDAP admin credentials.
|
||||||
|
|
||||||
|
Done when an unknown user can reach a normal KeyCape login without operator
|
||||||
|
provisioning and without acquiring tenant-admin or platform authority.
|
||||||
|
|
||||||
|
Implementation resumed with
|
||||||
|
`docs/public-registration-orchestration-contract.md`. The contract fixes the
|
||||||
|
anonymous/verified actor boundary, non-enumerating start response, durable
|
||||||
|
verification request, digest-only single-use verification state, baseline
|
||||||
|
group provisioning, provider-owned password setup, and fresh OIDC handoff.
|
||||||
|
The user-engine registration domain already requires verified factors and
|
||||||
|
uses the privileged provisioning adapter; the next implementation slice is
|
||||||
|
the persisted intent and verification-port API. Source implementation now
|
||||||
|
includes allow-listed, non-enumerating anonymous start and purpose-bound
|
||||||
|
verification consumption. Verification attaches an email factor only after
|
||||||
|
client/tenant/username binding checks and deliberately stops before user or
|
||||||
|
LLDAP creation. The remaining slice is resumable completion, baseline
|
||||||
|
provisioning, provider-subject linking, and password-setup handoff.
|
||||||
|
|
||||||
|
## T03 - Integrate registration entry points and return flow
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: NK-WP-0025-T03
|
||||||
|
status: progress
|
||||||
|
priority: high
|
||||||
|
state_hub_task_id: "3ec4a9a0-cbce-4c63-8ed8-db36f4cae0fb"
|
||||||
|
```
|
||||||
|
|
||||||
|
Publish a NetKingdom registration URL and allow applications to provide
|
||||||
|
`return_to`, client, and tenant hints through a signed/allow-listed state
|
||||||
|
envelope. Add "Create account" beside "Sign in" on the coulomb.social landing
|
||||||
|
page. After registration, start a fresh OIDC authorization-code/PKCE flow;
|
||||||
|
never treat the registration browser session as application authentication.
|
||||||
|
|
||||||
|
Done when direct NetKingdom registration and coulomb.social-initiated
|
||||||
|
registration converge on the same verified identity flow without open
|
||||||
|
redirects or client confusion.
|
||||||
|
|
||||||
|
## T04 - Apply optional MFA policy safely
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: NK-WP-0025-T04
|
||||||
|
status: progress
|
||||||
|
priority: high
|
||||||
|
state_hub_task_id: "cf2e4d05-fd07-4bc3-92ff-b2a5ade97846"
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace global mandatory MFA for this consumer with the KeyCape policy from
|
||||||
|
KEY-WP-0008. Ordinary coulomb.social login is password-only unless the
|
||||||
|
application profile, requested action, tenant policy, or risk/assurance rule
|
||||||
|
requires step-up. Platform administration and other existing high-assurance
|
||||||
|
clients remain MFA protected.
|
||||||
|
|
||||||
|
Done when lowering assurance for this application cannot lower assurance for
|
||||||
|
OpenBao, platform administration, or another client.
|
||||||
|
|
||||||
|
Source implementation adds a per-client minimum override and honors explicit
|
||||||
|
`acr_values=aal2`; only the coulomb-social static registration selects AAL1.
|
||||||
|
Other clients retain provider-driven mandatory MFA. Live rollout remains.
|
||||||
|
|
||||||
|
## T05 - Prove both cases end to end
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: NK-WP-0025-T05
|
||||||
|
status: todo
|
||||||
|
priority: high
|
||||||
|
state_hub_task_id: "5d13f9f6-16b1-44c9-8e1d-4630cfe60a7b"
|
||||||
|
```
|
||||||
|
|
||||||
|
Test an existing LLDAP identity with no coulomb.social profile, a completely
|
||||||
|
new registrant, repeated callback/JIT, existing-email collision, expired and
|
||||||
|
replayed verification, duplicate username, provider outage and compensation,
|
||||||
|
cross-tenant attempts, optional MFA, profile-requested step-up, unlink, and
|
||||||
|
offboarding. Use `tegwick` only for the attended positive acceptance test;
|
||||||
|
automated tests use disposable identities.
|
||||||
|
|
||||||
|
Done when both user journeys work on railiance01 and all negative cases leave
|
||||||
|
no orphaned privileged identity or duplicate application profile.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue