Skip to content

feat(platform): declarative kernel OAuth broker (descriptor engine) — spec 005 core#325

Merged
Weegy merged 7 commits into
byte5ai:mainfrom
SecurID:feat/declarative-oauth-broker
Jun 22, 2026
Merged

feat(platform): declarative kernel OAuth broker (descriptor engine) — spec 005 core#325
Weegy merged 7 commits into
byte5ai:mainfrom
SecurID:feat/declarative-oauth-broker

Conversation

@SecurID

@SecurID SecurID commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Finishes spec 004's deferred layer 3 (the kernel OAuth broker, Task C) as a declarative descriptor engine rather than hardcoded provider classes — so any future "connect X" plugin acquires standard authorization-code credentials through pure manifest data, with no bespoke core PR.

Spec: specs/005-declarative-oauth-broker-atlassian/spec.md.

What's in this PR (core only)

Task Change
T21 Top-level oauth_providers[] descriptor schema + type:oauth field provider/scopes parsing; permissions_summary.acquires_oauth signal
T22 Generic engine (RFC-6749 / PKCE / refresh) driven by descriptor data — token_auth_style (body_form|body_json|basic), {field} URL interpolation. Retires the bespoke MicrosoftGraphProvider class — it collapses into a body_form + {tenant_id} descriptor regression fixture
T23 pendingFlows + signed state carry pluginId so the store-detail re-connect path works without an install job
T24 GET /api/v1/install/oauth/{start,callback} — start is operator-authed, callback is public + signed-state-verified (publicPaths). Tokens persist to the plugin's own vault namespace under oauth.<fieldKey>
T25 ctx.oauthTokens.get(fieldKey) — valid access token, lazy refresh under a 5-min margin, refresh-token rotation, never leaked; typed errors (not_connected / refresh_failed)
T26 web-ui: type:oauth fields render a Connect button (store-detail re-connect); store page surfaces the ?connected=ok|error broker result

Security posture

  • No plugin code runs in the OAuth dance — descriptors are inert manifest data; the kernel engine performs the exchange/refresh, so client secrets + refresh tokens stay kernel-side.
  • Callback is unauthenticated but self-secures via a single-use, plugin-bound, 10-min signed state token (consumed on callback).
  • Tokens are namespace-locked to the plugin; extra_authorize_params cannot override engine-owned params (redirect_uri, PKCE, etc.).

Tests

Engine units (Microsoft fixture + Atlassian body_json dialect, error handling, reserved-param guard, interpolation), stub-IdP broker integration (round-trip, user-deny, bad-state, single-use replay, exchange-fail, missing-creds), and the accessor's lazy-refresh / rotation / failure-mode tests. tsc + eslint clean.

The Atlassian consumer plugin (integration + agent, separate Hub repo) validates this end-to-end and follows in a separate change (spec 005 T28–T31).

🤖 Generated with Claude Code

SecurID and others added 7 commits June 16, 2026 17:26
… spec 005 core

Finishes spec 004's deferred layer 3 (the kernel OAuth broker, Task C) as a
DECLARATIVE descriptor engine rather than hardcoded provider classes — so any
future "connect X" plugin acquires standard authorization-code credentials
through pure manifest data, with no bespoke core PR.

- Manifest: top-level `oauth_providers[]` descriptors + `type:oauth` field
  provider/scopes parsing; `permissions_summary.acquires_oauth` signal (T21).
- Engine: generic RFC-6749 / PKCE / refresh from descriptor data
  (`token_auth_style` body_form|body_json|basic, `{field}` URL interpolation).
  Retires the bespoke MicrosoftGraphProvider class — it collapses into a
  `body_form` + `{tenant_id}` descriptor regression fixture. No plugin code
  runs in the OAuth dance; refresh tokens stay kernel-side (T22).
- pendingFlows + signed state gain a `pluginId` path for store-detail
  re-connect without an install job (T23).
- Broker routes GET /api/v1/install/oauth/{start,callback}; start is
  operator-authed, callback is public + state-verified (publicPaths). Tokens
  persist to the plugin's own vault namespace under `oauth.<fieldKey>` (T24).
- ctx.oauthTokens.get(fieldKey): valid access token, lazy refresh under a
  5-min margin, refresh-token rotation, never leaked; typed errors
  (not_connected / refresh_failed) (T25).
- web-ui: `type:oauth` fields render a Connect button (store-detail
  re-connect); store page shows the `?connected=ok|error` broker result (T26).

Spec: specs/005-declarative-oauth-broker-atlassian/spec.md. Validated by
engine units (MS fixture + Atlassian dialect), stub-IdP broker integration
tests, and the accessor's lazy-refresh tests. The Atlassian consumer plugin
(integration + agent, new repo) follows in T28–T31.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`validateValues` still carried the spec-004 stub that hard-rejected any
`type:oauth` field with `unsupported_type` ("erst in Slice 1.2c"). Slice 1.2c
is the broker shipped in this branch — so every broker plugin (e.g.
integration-atlassian) failed to install: configure rejected the declared
oauth field even though its value is supplied AFTER install by the broker
(Connect flow → token in the vault under `oauth.<key>`), never at the flyout.

configure now skips `type:oauth` fields entirely. Regression test:
configure with an oauth field present activates, and the oauth field is never
flagged (while a genuinely missing required non-oauth field still is).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(spec 005)

The integration sets its operator-facing status + resolves derived config
(e.g. Atlassian cloud_id) only in activate() → resolveConnection(). But the
broker callback persists the tokens AFTER activate, with nothing to re-run
that resolution — so post-connect the plugin's ctx.status stayed
needs_action and its derived config stayed empty until the next restart.

The callback now calls an injected reactivatePlugin(pluginId) hook after the
tokens persist (best-effort; wired to installService.reactivate, the same
deactivate→activate the install flow uses). The plugin re-reads the stored
token, resolves derived config, and clears its status — so the badge clears
without a restart. Broker test asserts reactivation fires on success and not
on deny.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Weegy Weegy enabled auto-merge (squash) June 22, 2026 12:47
@Weegy Weegy merged commit 334d4dc into byte5ai:main Jun 22, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants