Skip to content

feat(services): use_default_connection opt-out + atomic pin_service_ids#431

Merged
angel-manuel merged 2 commits into
devfrom
feat/service-oauth-atomic-pinning
Jul 6, 2026
Merged

feat(services): use_default_connection opt-out + atomic pin_service_ids#431
angel-manuel merged 2 commits into
devfrom
feat/service-oauth-atomic-pinning

Conversation

@angel-manuel

Copy link
Copy Markdown
Contributor

Context

White-label integrations struggle to associate a new service with a new connection atomically. Two gaps drove this:

  1. Silent default grabbing. An unbound service instance falls through to the identity's default connection for the provider at execution time — partners who mint a dedicated connection per service couldn't opt out.
  2. Non-atomic, single-target pinning. The only way to bind a new connection to a service was the OAuth callback's post-hoc, single-instance, best-effort bind. The white-label token-import path had no pinning at all.

What changed

A. Per-instance use_default_connection (migration 090, default true)

A service-instance column. When false, an unbound OAuth instance never borrows the identity's default connection — it reports needs_authentication until one is pinned. Honored consistently in all three resolution sites so the dashboard badge can't disagree with execution:

  • resolve_instance_auth (execution)
  • check_required_scopes (scope pre-gate)
  • resolve_effective_scopes / list classifier

Plumbed through CreateServiceInput/UpdateServiceInput, the summary/detail responses, types.ts, and a toggle on the service create + detail pages.

B. Atomic pin_service_ids on connection creation

  • Import path (POST /v1/connections/import, the white-label case): fully atomic via new OrgScope::create_connection_and_pin — connection insert + instance binds in one transaction. A bad / foreign-owned / org-level id rolls the whole import back, leaving no orphan connection.
  • OAuth-orchestrated flow (POST /v1/connections + callback): plural ids persisted on the flow row (migration 091, UUID[]), bound on callback. Kept best-effort here (keeps the exchanged tokens, surfaces service_instance_bind_error) to preserve the existing spoofed-id security contract.
  • Singular service_instance_id retained as a back-compat alias on both paths.

Tests

tests/service_pin_connection.rs (3 new): the fallback gate, atomic bind on import, and rollback-on-bad-id. Full local runs pass across connection_import, services_auto_connect, oauth_return_url, multi_org, connection_credential_source, oauth_connections_ux, connections_admin_view, instance_visibility_consistency. Clippy clean; sqlx offline cache regenerated; dashboard svelte-check + strict build pass; vet clean.

🤖 Generated with Claude Code

…ds on connection creation

White-label integrations couldn't atomically associate a new service with a
new connection. Two additions close the gap:

- Per-instance `use_default_connection` flag (migration 090, default true).
  When false, an unbound OAuth instance no longer falls back to the identity's
  default connection — it reports needs_authentication until one is pinned.
  Honored in resolve_instance_auth, the scope pre-gate, and the credentials
  classifier so the dashboard badge matches execution. Plumbed through the
  service create/update API, responses, and a dashboard toggle.

- `pin_service_ids` on connection creation. The import path
  (POST /v1/connections/import) binds atomically via
  OrgScope::create_connection_and_pin — a bad id rolls the whole import back
  so no orphan connection is left behind. The OAuth-orchestrated flow carries
  the ids on the flow row (migration 091, UUID[]) and binds them on callback,
  staying best-effort to preserve the exchanged tokens and the existing
  spoofed-id security contract. Singular service_instance_id kept as an alias.

Adds tests/service_pin_connection.rs covering the fallback gate, atomic bind,
and rollback-on-bad-id.

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

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
overslash Ready Ready Preview, Comment Jul 6, 2026 11:05am

Request Review

Comment thread crates/overslash-api/src/routes/connections.rs
@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

… design

Addresses the Sentry review on the OAuth callback: the browser success
redirect deliberately does not enumerate bound_service_instance_ids in the
query string. A user-controllable, browser-visible URL is the wrong transport
for authoritative binding state and can't losslessly carry a list. The
authoritative set is the DB — a partner reads it back via
GET /v1/connections/{connection_id} (its `used_by` list), keyed off the
connection_id already present in the redirect. The JSON branch keeps the full
list as a convenience for programmatic callers (authenticated response body,
not a URL).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@angel-manuel angel-manuel merged commit 493cbb8 into dev Jul 6, 2026
15 checks passed
@angel-manuel angel-manuel deleted the feat/service-oauth-atomic-pinning branch July 6, 2026 11:27
angel-manuel added a commit that referenced this pull request Jul 6, 2026
…ion tab (#432)

PR #431 added the per-service use_default_connection opt-out toggle but
placed it on the overview tab, not the credentials (Connection) tab where
the provider connection is actually managed. Users looking at connection
settings could neither see nor change the fallback behavior.

Relocate the toggle into the credentials tab's OAuth branch, directly below
the Connection picker and above the Save/Connect actions. The field was
already wired end-to-end (types, updateService, save() diff); the same
credentials-tab Save button persists it, so no other changes are needed.

Co-authored-by: Factory <factory@overslash.dev>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.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