Resolve credentials
Walk the USER → ORGANIZATION → PLATFORM chain from a runtime's seat and watch each level win in turn.
The service-plane view: a translation runtime asks for the effective key and the chain answers. This scenario stages all three levels and a miss, so every branch of the walk shows itself.
Goal
Observe credential_source change from user to organization to platform as upper levels disappear, and end on the 404 CREDENTIAL_NOT_CONFIGURED miss.
Prerequisites
- A service token with the
provider-credentials.invokescope:export SERVICE_TOKEN=...(Service Plane Tokens). - Staged records for one provider: the user's personal default, the organization's default, a platform default (scenarios 02, 05, 07).
Steps
1. The user's own key wins
Call POST /internal/v1/credentials/resolve:
curl -s -X POST "$CREDS_BASE/internal/v1/credentials/resolve" \
-H "Authorization: Bearer $SERVICE_TOKEN" -H "Content-Type: application/json" \
-d '{
"user_id": "'$USER_ID'",
"org_id": "'$ORG_ID'",
"provider_code": "'$CODE'",
"purpose": "translation",
"correlation_id": "cookbook-06-a"
}'
Expected 200: credential_source: "user" and — uniquely on this plane — plaintext credentials. Handle accordingly (Resolve Chain).
2. Remove the user level — the organization steps in
Delete (or disable) the user's credential, resolve again with the same body. Expected 200 with credential_source: "organization", owner_id = the org UUID.
3. Drop the org from the request — the platform catches
Resolve without org_id. Expected 200 with credential_source: "platform" — no organization context means the level is skipped entirely, and the shared fallback serves.
4. Forbid the fallback — a controlled miss
Same request plus "allow_platform_fallback": false:
r = httpx.post(f"{CREDS_BASE}/internal/v1/credentials/resolve",
headers={"Authorization": f"Bearer {SERVICE_TOKEN}"},
json={**body, "allow_platform_fallback": False})
assert r.status_code == 404
assert r.json()["error"]["code"] == "CREDENTIAL_NOT_CONFIGURED"
Expected 404 CREDENTIAL_NOT_CONFIGURED — the provider exists, nothing admissible is configured. Every step of this scenario, hits and the miss alike, is now visible in the audit trail under your correlation ids (scenario 09).
Wrong-scope tokens are refused, not downgraded
Repeat step 1 with an admin-scoped token: expected 403 FORBIDDEN. Resolve belongs to provider-credentials.invoke alone.
Verified by the test test_s06_resolve_credentials.