Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 181 additions & 0 deletions docs/plans/2026-06-02-auth-provider-admin-apply-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# Auth Provider Admin Apply Design

Date: 2026-06-02

## Goal

Close the gap between the existing admin/auth/authz proof and a usable auth
administration path:

- A protected admin operator can validate and apply auth configuration changes.
- Secret values submitted through admin are written through a configured
Workflow secrets module, not persisted or echoed by the auth plugin.
- Applied non-secret config state is persisted by the host application.
- Scenario 90 proves the contract with the real Workflow server, real admin
shell, real auth/authz gates, and real Workflow pipeline steps.

## Current State

Workflow already provides the reload/config mechanisms this work must not
duplicate:

- `POST /api/workflow/reload` reloads the current config.
- The server supports file watch reload through `config.NewConfigWatcher`.
- The server has database config polling when a DB config store is configured.

Workflow also already provides secret storage primitives:

- `secrets.Provider` supports `Get`, `Set`, `Delete`, and `List`.
- `step.secret_set` writes submitted values through a named secrets module.
- Secret modules expose providers through the service registry.

Auth/provider plugins already expose management descriptors or concrete remote
CRUD steps:

- `workflow-plugin-auth` exposes auth admin contribution, config describe,
config validate, and provider catalog steps.
- Provider plugins own provider-specific management operations:
Auth0 clients/users/roles, Okta apps/users/groups/policies, Entra users/groups
and applications, Ory Kratos identities, Ory Hydra OAuth clients/JWKs/trusted
issuers, Ory Polis SSO/directory resources, and Scalekit SSO/directory
resources.

`workflow-plugin-infra` and Workflow IaC own infrastructure resource planning
and lifecycle. Auth provider tenant/client/user provisioning is not infra unless
it is explicitly modeled as an IaC resource later.

Scenario 90 already proves:

- Primary app and admin app are served by Workflow.
- Admin is protected by authentication and authorization.
- Admin nav is contribution-driven.
- Auth config describe/validate and provider catalog are live.
- Authz UI is functional through the admin nav.

## Design

### Ownership Boundary

Auth plugin:

- Defines descriptor and validation contracts.
- Redacts secret-bearing fields from describe/validate output.
- Does not persist host config.
- Does not own Workflow reload.
- Does not own secret storage.
- Does not own generic remote provisioning orchestration.

Admin plugin:

- Renders generic contribution UI.
- Adds an optional apply action when contribution metadata includes
`apply_path`.
- Sends the same `desired_config` draft used for validation.
- Does not assume auth-specific fields or semantics.

Workflow host/scenario:

- Owns authenticated and authorized apply endpoints.
- Runs validation before persistence.
- Writes submitted secret values through `step.secret_set`.
- Persists accepted non-secret config state plus secret refs/status.
- Can trigger Workflow reload or rely on existing hot reload/config-store
behavior in production deployments.

Provider plugins:

- Own provider-specific remote CRUD/provisioning calls.
- Surface actions through provider-specific Workflow steps.
- Are invoked by host endpoints with explicit scopes, audit, idempotency, and
rollback behavior.

Infra plugin:

- Owns infrastructure resources, DNS/compute/storage/network provider bindings,
and wfctl/IaC flows.
- Does not become the default place for Auth0/Okta/Entra/Hydra client CRUD.

### Scenario 90 Extension

Scenario 90 will add:

- `apply_path: /api/admin/auth/config/apply` to the auth contribution metadata.
- A Vault dev sidecar and `secrets.vault` module used only for scenario proof.
- A persisted auth admin state table.
- `POST /api/admin/auth/config/apply`:
- authenticates the bearer token with `auth.jwt`;
- enforces `admin:auth.config:update` from persisted admin role grants;
- validates `desired_config` with `step.auth_admin_config_validate`;
- rejects invalid config without persistence;
- writes `auth0_client_secret`, when provided, through `step.secret_set`;
- persists only sanitized accepted config JSON, secret refs, actor, and time;
- returns `applied`, `valid`, `accepted_config`, `secret_fields`,
`secret_refs`, `warnings`, and no submitted secret values.
- `GET /api/admin/auth/config/applied`:
- authenticates and enforces `admin:auth.config:read`;
- returns the last applied state for verification and UI observability.

The scenario intentionally does not provision a real Auth0/Okta/Entra tenant in
public CI. Live remote provisioning requires provider credentials, external API
side effects, quotas, and cleanup; that belongs in provider-specific tests or a
credentialed/private scenario. Scenario 90 proves the cross-component admin
apply path and secret-provider wiring. Provider plugins continue to prove their
SDK-backed CRUD steps directly.

### Adversarial Review Adjustment

The initial sketch proposed a file-backed secrets module. That was wrong for
runtime Workflow: `secrets.file` exists in wfctl secret-store resolution, but
the Workflow server module registry currently exposes `secrets.vault`,
`secrets.aws`, and `secrets.keychain`. Scenario 90 therefore uses a Vault dev
container and configures a real `secrets.vault` runtime module. This keeps the
proof honest: `step.secret_set` crosses the real module/provider boundary.

### Use Cases

Solo developer:

- Boots Scenario 101/bootstrap auth or local auth.
- Uses admin to configure passkey/local auth settings.
- Stores local provider secrets through file/keychain/env-backed Workflow
secrets modules.
- Relies on Workflow hot reload or an explicit reload endpoint after config
persistence.

Development team:

- Configures staging identity providers from the admin UI.
- Validates config before applying.
- Stores client secrets through the environment's configured secrets provider.
- Uses provider-plugin CRUD routes for staging OIDC clients or SSO connections,
guarded by `admin:auth.providers.write`.

SRE/platform team:

- Manages approved identity-provider clients/connections with audit and
explicit scopes.
- Uses Workflow/wfctl infra for infrastructure and auth provider plugins for
identity-provider resources.
- Requires idempotent provider operations, rollback/delete paths, and no secret
material in logs or persisted config state.

## Security Requirements

- Apply endpoints must be server-side authenticated and authorized.
- Client-supplied role/scope claims are not trusted.
- Invalid config must not be persisted.
- Submitted secret values must not appear in API responses, stored config JSON,
logs, or test artifacts.
- Secret writes must target a configured Workflow secrets module.
- Direct iframe/plugin UI access remains protected by server-side endpoints; UI
hiding is not an authorization control.

## Rollback

- Admin UI apply button is feature-detected by `apply_path`; removing
`apply_path` hides it without breaking validate-only contributions.
- Scenario state is local file/SQLite data and can be reset with Docker compose
teardown.
- Production hosts should retain prior accepted config, apply new config only
after validation, and use Workflow's existing reload/config-store rollback
path rather than auth-plugin-owned reload code.
77 changes: 77 additions & 0 deletions docs/plans/2026-06-02-auth-provider-admin-apply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Auth Provider Admin Apply Plan

Date: 2026-06-02

## Scope

Implement the smallest real slice that proves auth admin save/apply without
duplicating Workflow reload, Workflow secrets, provider CRUD, or infra.

PRs:

- `workflow-plugin-admin`: generic config-form apply button.
- `workflow-scenarios`: Scenario 90 apply endpoint, secrets wiring, and tests.

No `workflow-plugin-auth` PR is required unless implementation proves the
current descriptor/validation contract cannot support the scenario.

## Tasks

1. Admin UI: Add optional apply action
- In `internal/ui_dist/index.html`, render an `Apply changes` button only
when contribution metadata has `apply_path`.
- POST `{desired_config: draft}` to `apply_path` with the same auth headers
used by validation.
- Render the JSON response in the existing config result area.
- Verify with `go test ./...`.

2. Scenario 90: Wire auth contribution metadata
- Configure `step.auth_admin_contribution_describe` with `describe_path`,
`validate_path`, and `apply_path`.
- Verify `/api/admin/contributions` exposes `apply_path` only to authorized
users who can see the auth contribution.

3. Scenario 90: Add secrets module and persisted apply state
- Add a Vault dev sidecar and `secrets.vault` module suitable for local
Docker proof.
- Add/create an auth admin applied-state table.
- Store accepted non-secret config JSON, secret refs/status, actor, and
timestamp.

4. Scenario 90: Add protected apply endpoint
- Authenticate with `auth.jwt`.
- Enforce `admin:auth.config:update` from `scenario90_role_assignments`.
- Validate with `step.auth_admin_config_validate`.
- Route invalid validation to a non-persisting response.
- Write submitted secret fields through `step.secret_set`.
- Persist sanitized accepted config and refs only.
- Return `applied:true` only after secret write and persistence.

5. Scenario 90: Add applied-state read endpoint
- Authenticate with `auth.jwt`.
- Enforce `admin:auth.config:read`.
- Return persisted state without secret values.

6. Scenario 90: Extend verification
- Shell tests cover contribution metadata, valid apply, invalid apply, secret
redaction, state persistence, delegated read-only denial, and support-user
denial.
- Playwright tests click Authentication, validate, apply, navigate to
Authorization, and confirm no console errors.
- Scenario test must run the real Workflow server through Docker.

## Non-Goals

- No new Workflow reload implementation.
- No new secret storage provider.
- No new infra/IaC resource implementation.
- No public-CI live Auth0/Okta/Entra provisioning.
- No auth-specific logic in the generic admin shell.

## Verification

- `workflow-plugin-admin`: `go test ./...`.
- `workflow-scenarios`: `go test ./...`.
- `workflow-scenarios/scenarios/90-admin-tailnet-demo/test/run.sh`.
- `workflow-scenarios/scenarios/90-admin-tailnet-demo/test/run-playwright.sh`.
- Manual/adversarial review of diffs before PR/merge.
17 changes: 17 additions & 0 deletions scenarios/90-admin-tailnet-demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ does not contain or run an application-specific Python/Node/Ruby web harness.
- Authz admin contribution: <http://localhost:18080/admin/authz/>
- Admin contribution API: <http://localhost:18080/api/admin/contributions>
- Auth provider catalog API: <http://localhost:18080/api/admin/auth/providers>
- Auth config apply API: <http://localhost:18080/api/admin/auth/config/apply>
- Auth config applied-state API: <http://localhost:18080/api/admin/auth/config/applied>

The scenario creates an admin user during tests:

Expand Down Expand Up @@ -43,6 +45,21 @@ through the admin shell's generic config-form mode; authz proof endpoints use
`workflow-plugin-authz-ui` steps. The app demonstrates frontend scope checks for
order read/update and admin scope checks for auth/authz management pages.

Auth config apply is host-owned Workflow YAML: the endpoint authenticates with
`auth.jwt`, enforces `admin:auth.config:update`, validates through
`workflow-plugin-auth`, writes the submitted Auth0 provider secret through a
real `secrets.vault` module backed by the scenario Vault sidecar, and persists
only accepted non-secret config plus `secret://` refs in SQLite. The
applied-state endpoint exposes the persisted non-secret config and secret refs
for scenario verification and admin observability, without returning secret
values.

Provider catalog and provider descriptor routes are public-CI safe. Live
remote Auth0/Okta/Entra/Ory/Scalekit provisioning remains provider-plugin-owned
and should run in credentialed/provider-specific scenarios, not through
`workflow-plugin-infra` unless an identity resource is explicitly modeled as
IaC.

## Run

```sh
Expand Down
Loading
Loading