Migrate Provider-Key Apps to Optional OAuth with Boundary Tests
A credential-boundary matrix catches wrong-route secrets, cross-user connection reuse, and silent payer fallback before rollout.
Migrate routing without mixing credential domains
Adding optional AI Pass OAuth to a provider-key application creates two credential domains. The migration is safe only if a request uses the credential belonging to its selected route and signed-in user. A dropdown that says “AI Pass” is not sufficient evidence: the adapter, stored preferences, background work, logs, and disconnect behavior must agree.
Consider an illustrative translation desk called Captionbench. Existing users store provider credentials through its established protected backend. The product owner approves AI Pass as an additional choice for interactive subtitle translation. Existing BYOK remains available. The migration must not reinterpret a saved provider credential as a gateway credential or transfer it to another service.
Read the canonical integration skill, especially its existing-auth-and-billing and backend OAuth references, before implementing exact connection behavior. This article specifies app-level migration tests, not an alternative OAuth protocol.
Model route selection separately from credential storage
Store the user's route preference as non-secret application data. Credential references should identify protected records, never contain raw values in component props, analytics, support exports, or an agent transcript. A provider-key record belongs to the direct adapter. An OAuth connection belongs to the authorized AI Pass path. A project setup grant belongs to neither runtime adapter.
The following pure function is a local policy example. It receives booleans from trusted connection lookups, not user-submitted claims. It does not authenticate an API call.
def choose_route(preference, direct_ready, wallet_ready):
if preference == "direct_byok":
return "direct" if direct_ready else "needs_provider_setup"
if preference == "aipass_oauth":
return "wallet" if wallet_ready else "needs_wallet_connection"
return "needs_explicit_choice"
assert choose_route("direct_byok", True, True) == "direct"
assert choose_route("aipass_oauth", True, False) == "needs_wallet_connection"
assert choose_route(None, True, True) == "needs_explicit_choice"
Notice the missing fallback: a disconnected wallet does not silently invoke the stored provider key. That would change the payer and possibly the data-processing route. If fallback is a supported product feature, it needs its own explicit policy and user choice before dispatch.
Build a credential-boundary test matrix
Use synthetic credential handles and adapters that record only which handle class they received. Never seed tests with copied production credentials.
| Scenario | Expected route outcome | Forbidden observation |
|---|---|---|
| Existing user keeps BYOK | Direct adapter receives direct handle | OAuth adapter called |
| User chooses connected wallet | Wallet adapter receives wallet handle | Provider key forwarded |
| Wallet disconnected, BYOK present | Connection required, no inference | Automatic direct fallback |
| User A switches to user B | B's connection resolved afresh | A's handle reused |
| Host session expires | Host access denied | Wallet bypasses host authorization |
| Disconnect wallet | New wallet actions blocked | BYOK credential deleted |
| Queued direct job survives rollout | Original route policy retained | Silent conversion to wallet charge |
For in-flight operations, define a separate lifecycle policy. Disconnect should prevent new dispatches; it does not establish whether an already-dispatched request completed or was charged. Retain non-secret operation evidence needed to resolve that ambiguity without promising cancellation semantics the provider has not documented.
Test the places credentials escape indirectly
Assert that error serialization excludes request authorization headers. Capture fake logs and inspect structured fields, not just human-readable messages. Exercise browser hydration and state persistence to ensure credential handles are not accidentally replaced with raw credentials. Test account switching in the same browser process, where cached connection state can outlive the previous host session.
Existing billing deserves its own assertion. A wallet-routed translation must follow the product's approved host-credit policy, rather than inheriting a direct adapter's deduction side effect accidentally. Conversely, do not remove subscription access gates just because inference is user-funded.
The SDK documentation informs permitted browser behavior; the REST documentation informs a server-side integration. Neither should be treated as permission to ask a user to paste runtime OAuth tokens into chat. Exact token custody and connection mechanics come from the canonical path references.
Roll out as an additive migration
Default existing users to their established route unless a reviewed product decision says otherwise. Add new connection records without bulk deleting provider credentials. Gate the optional path, test rollback, and preserve enough operation metadata to understand work started before rollback.
The acceptance artifact is the completed matrix with test identifiers, observed fake adapter calls, and unresolved lifecycle questions. Passing it proves application routing and credential isolation under the tested conditions. It does not prove live OAuth completion, production wallet funding, or a paid translation. Report those separately and leave spending pending without specific approval.
For AI agents
Skill file
---
name: aipass-credential-boundary-migration-tests
description: Use when adding optional OAuth beside provider keys. Build and execute a credential-isolation migration matrix with fake adapters.
---
# Credential-boundary migration tests
## Scope
Test an approved additive migration from existing provider-key routing to an optional AI Pass OAuth route. Preserve BYOK. Use synthetic credential handles and network-disabled adapters; never request real credentials or execute paid inference.
## Contract prerequisite
Read the [canonical integration skill](https://aipass.one/skills/aipass-integration/SKILL.md) and its existing-auth-and-billing reference, plus backend-oauth or sdk-path for the implementation under test. Those references own exact OAuth behavior. Do not invent token exchange, login, or disconnect endpoints. See [REST docs](https://aipass.one/docs/rest) and [SDK docs](https://aipass.one/docs/sdk) for the selected runtime.
## Procedure
1. Inspect route preferences, credential stores, connection lookup, session caches, adapter construction, jobs, logging, and host billing hooks.
2. Define separate fake direct and wallet handle types. Instrument adapters to reject the wrong type and record route, synthetic subject, and operation ID only.
3. Build a matrix covering existing BYOK, connected wallet selection, disconnected wallet with a stored provider key, missing preference, account switch, expired host session, wallet disconnect, queued pre-migration work, and rollback.
4. For each row state expected adapter, expected subject, forbidden credential domain, expected host billing effect, and permitted number of dispatches.
5. Execute tests through the actual app routing boundary. Assert no automatic payer fallback, no host-auth bypass, no cross-user connection reuse, and no deletion of BYOK records on wallet disconnect.
6. Capture synthetic logs and serialized UI state. Assert that raw fake secret markers never appear outside the protected adapter boundary.
7. Test new-dispatch blocking separately from in-flight reconciliation. Disconnect does not prove cancellation or absence of charges.
## Deliverables and gate
Write `credential-boundary-matrix.md`, runnable repository tests, and `migration-test-results.md` with commands, exit codes, and failing rows. Record fixture-only execution prominently. Completion requires every matrix row accounted for, not merely a green build. Live OAuth and wallet-funded inference remain separate unverified criteria unless independently observed with required approval.