Skip to content

feat(cli): migrate oauth providers + apps + shared to thin IPC wrappers#30246

Merged
noanflaherty merged 1 commit into
mainfrom
cli-ipc-migration/pr-9-oauth-providers-apps
May 11, 2026
Merged

feat(cli): migrate oauth providers + apps + shared to thin IPC wrappers#30246
noanflaherty merged 1 commit into
mainfrom
cli-ipc-migration/pr-9-oauth-providers-apps

Conversation

@noanflaherty
Copy link
Copy Markdown
Contributor

@noanflaherty noanflaherty commented May 11, 2026

Part of plan: cli-ipc-migration.md (PR 9 of 11)


Open in Devin Review

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@noanflaherty noanflaherty merged commit 8899458 into main May 11, 2026
8 of 12 checks passed
@noanflaherty noanflaherty deleted the cli-ipc-migration/pr-9-oauth-providers-apps branch May 11, 2026 02:37
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 07e42c7653

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +344 to +349
policyKey: "oauth/providers.register",
summary: "Register OAuth provider",
description: "Register a new OAuth provider configuration.",
tags: ["oauth"],
requirePolicyEnforcement: true,
responseStatus: "201",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Register route policies for provider write endpoints

These new provider mutation routes rely on policy keys (oauth/providers.register, oauth/providers.update, oauth/providers.delete) that are not registered in assistant/src/runtime/auth/route-policy.ts (which currently only registers oauth/providers for this area). In assistant/src/runtime/http-router.ts, missing policy registrations make enforcePolicy return allow, so these writes bypass the intended scope gate instead of requiring settings.write.

Useful? React with 👍 / 👎.

Comment on lines +351 to +355
operationId: "oauth_apps_upsert",
endpoint: "oauth/apps/upsert",
method: "POST",
policyKey: "oauth/apps.upsert",
summary: "Upsert OAuth app",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add policy coverage for new OAuth app endpoints

The newly added app routes introduce new policy keys/endpoints (oauth/apps.upsert and derived oauth/apps/lookup) but there are no matching entries in assistant/src/runtime/auth/route-policy.ts. Because the router treats missing policies as unprotected, these routes skip normal scope enforcement and can be called without the expected OAuth-settings read/write permissions.

Useful? React with 👍 / 👎.

Comment on lines +284 to +296
let rows: SerializedProvider[] = (r.result?.providers ?? []).map(
(p: Record<string, unknown>) => ({
providerKey: p.provider_key as string,
displayName: p.display_name as string | null,
description: p.description as string | null,
supportsManagedMode: p.supports_managed_mode as boolean,
managedServiceIsPaid: p.managed_service_is_paid as boolean,
requiresClientSecret: p.requires_client_secret as boolean,
logoUrl: p.logo_url as string | null,
dashboardUrl: p.dashboard_url as string | null,
clientIdPlaceholder: p.client_id_placeholder as string | null,
featureFlag: p.feature_flag as string | null,
}),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve full provider fields in oauth providers list

This migration changes assistant oauth providers list --json from full provider objects to a reduced summary shape by mapping only a small subset of fields from oauth_providers_get. The previous implementation returned full serialized providers (including fields like auth/token URLs, scopes, and timestamps), and the help text here still advertises those fields, so existing scripts consuming the old JSON contract will silently break.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment on lines +284 to 297
let rows: SerializedProvider[] = (r.result?.providers ?? []).map(
(p: Record<string, unknown>) => ({
providerKey: p.provider_key as string,
displayName: p.display_name as string | null,
description: p.description as string | null,
supportsManagedMode: p.supports_managed_mode as boolean,
managedServiceIsPaid: p.managed_service_is_paid as boolean,
requiresClientSecret: p.requires_client_secret as boolean,
logoUrl: p.logo_url as string | null,
dashboardUrl: p.dashboard_url as string | null,
clientIdPlaceholder: p.client_id_placeholder as string | null,
featureFlag: p.feature_flag as string | null,
}),
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 defaultScopes omitted from providers list CLI mapping, scopes never display

The providers list CLI command maps the IPC response from snake_case to camelCase at providers.ts:284-297, but the mapping does not include defaultScopes. The server-side serializeProviderSummary (provider-serializer.ts:134-149) also does not include a default_scopes field. As a result, formatProviderSummary (providers.ts:63-65) always sees p.defaultScopes as undefined, and the Scopes: ... line is never rendered in text output. In JSON mode, the defaultScopes field is also absent. The old code used serializeProvider (full serialization) which included defaultScopes, so this is a behavioral regression for both text and JSON consumers of assistant oauth providers list.

Prompt for agents
The providers list CLI mapping at providers.ts:284-297 is missing the defaultScopes field. The server route handleListProviders in oauth-providers.ts uses serializeProviderSummary which returns a snake_case summary that does NOT include default_scopes. Two changes are needed:

1. In assistant/src/runtime/routes/oauth-providers.ts, function handleListProviders: either switch from serializeProviderSummary to serializeProviderFull (which includes defaultScopes), or add a default_scopes field to the SerializedProviderSummary type and serializeProviderSummary function in assistant/src/oauth/provider-serializer.ts.

2. In assistant/src/cli/commands/oauth/providers.ts, the snake_case-to-camelCase mapper at line 284 must also include defaultScopes mapped from whatever snake_case key the server provides (e.g. default_scopes).

Without both changes, formatProviderSummary at line 63 will never render the Scopes line because p.defaultScopes is always undefined.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

}

return { provider: serializeProviderSummary(row) };
return { provider: serializeProviderFull(row) };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 providers get route changed from summary to full serialization — API contract change

The handleGetProvider route at assistant/src/runtime/routes/oauth-providers.ts:93 was changed from returning serializeProviderSummary(row) (snake_case summary with ~10 fields) to serializeProviderFull(row) (camelCase full object with ~30+ fields). This is an intentional improvement for the CLI's providers get command (which uses formatProviderDetail expecting camelCase), but it also changes the HTTP API response shape for GET /oauth/providers/:providerKey. Existing HTTP consumers that parsed the snake_case summary will now receive camelCase full data. The CLI is the primary consumer, so this is likely acceptable, but worth confirming no other HTTP callers depend on the old shape.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

noanflaherty pushed a commit that referenced this pull request May 11, 2026
)

The CLI IPC refactor PRs (#30238#30247) added new shared routes without
matching policy entries in route-policy.ts. The route-policy coverage
guard test fails on main with 29 missing endpoints:

  - audit, auth/info
  - conversations/cli/{list,create,export,clear}
  - inference/send
  - internal/mcp/{list,add,remove}
  - oauth/apps.upsert, oauth/apps/lookup
  - oauth/providers.{register,update,delete}
  - platform/{status,connect,disconnect,callback-routes/register,callback-routes}
  - sequences/{list,get,pause,resume,cancel-enrollment,stats,guardrails}
  - user-routes/{list,inspect}

http-router.ts treats unregistered endpoints as unprotected (enforcePolicy
no-ops on missing keys), so any authenticated principal could hit these
over HTTP without the intended scope/principal-type gates. The IPC dispatch
path bypasses enforcePolicy entirely, so the existing CLI flows are
unaffected by these additions.

Scope/principal assignments mirror sibling routes:
  - oauth/apps mutations use ACTOR_ENDPOINTS settings.write (matches
    oauth/apps.create/.delete already present)
  - oauth/providers mutations use ACTOR_ENDPOINTS settings.write
  - oauth/apps/lookup uses ACTOR_ENDPOINTS settings.read
  - auth/info uses ACTOR_ENDPOINTS settings.read
  - internal/mcp/{list,add,remove} join INTERNAL_ENDPOINTS (gateway-only,
    internal.write) alongside the existing internal/mcp/reload entry
  - CLI-driven routes (audit, conversations/cli/*, inference/send,
    platform/*, sequences/*, user-routes/*) are local-only with
    settings.read or settings.write, mirroring tasks/*, cache/*, defer/*,
    domain/*, email/* — destructive 'conversations/cli/clear' elevates
    to settings.write to match conversations/clear-all
  - inference/send uses chat.write (LLM dispatch on behalf of caller),
    aligned with tts/synthesize-cli + stt/transcribe-file

Closes route-policy coverage findings on PRs #30241 (sequences,
user-routes), #30242 (conversations/cli/clear), #30244 (inference/send),
#30245 (internal/mcp), #30246 (oauth/providers, oauth/apps), #30247
(platform/*).

Co-authored-by: credence-the-bot[bot] <credence-the-bot[bot]@users.noreply.github.com>
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.

1 participant