White-label end-users
~15 min Python TypeScriptGoal: You’re building a SaaS product on MIOSA. Your end-users create sandboxes through your UI - they don’t have MIOSA accounts. This recipe shows you how to stamp identity on every sandbox, store per-user credentials, surface a per-user audit feed in your dashboard, and make the GitHub OAuth consent screen say your brand name instead of “Authorize MIOSA.”
What you’ll use: External Attribution, Secrets (external_user scope), Audit Log, White-label OAuth
What you’ll build
Your SaaS backend
└─ creates sandbox with external_user_id + external_workspace_id
└─ accepts user's OpenAI key → stores it scoped to that user
└─ initiates GitHub OAuth → stores token scoped to that user
└─ queries audit log by external_user_id → exposes in your dashboard
└─ OAuth consent screen shows "Authorize YourApp" not "Authorize MIOSA" Prerequisites
- MIOSA API key (
msk_live_*) with admin scope - see API Keys - A GitHub OAuth App registered at
github.com/settings/applications/new(for the custom branding step)
Step 1 - Stamp identity at create time
Every sandbox you create for one of your end-users gets tagged with your IDs.
Step 2 - Store credentials scoped to the end-user
Your user pastes their own OpenAI key into your settings UI. Store it scoped to them - other users at the same clinic cannot see it.
How the proxy resolves the right credential
When Dr. Smith’s sandbox makes an outbound call, the proxy walks this priority list and uses the first match:
1. Direct binding on that specific sandbox
2. external_user scope ← matches dr_smith@clinic1.example
3. external_workspace scope
4. MIOSA workspace scope
5. Tenant-wide scope Another clinician at the same clinic resolves the clinic-wide EHR_API_KEY but not Dr. Smith’s personal OPENAI_API_KEY.
Step 3 - Initiate OAuth on behalf of your end-user
When Dr. Smith clicks “Connect GitHub” in your dashboard, your backend initiates the OAuth flow and sends the authorization URL back to her browser.
Step 4 - Display per-user audit data in your dashboard
Pull the audit log for a single user and render it in your own UI.
Step 5 - Brand the OAuth consent screen
By default, the GitHub consent screen says “Authorize MIOSA.” Replace that with your product name.
Register your OAuth app on GitHub
https://github.com/settings/applications/new
Application name: ClinicIQ Cloud
Homepage URL: https://cliniciq.example
Callback URL: https://app.cliniciq.example/oauth/callback Copy the Client ID and generate a Client Secret.
Add the override to your MIOSA tenant
In your tenant dashboard → Settings → OAuth Providers → “Add provider override”:
| Field | Value |
|---|---|
| Provider | github |
| Display name | GitHub |
| Client ID | <from GitHub> |
| Client Secret | <from GitHub> |
| Callback URL | https://app.cliniciq.example/oauth/callback |
| Button label | Connect GitHub to ClinicIQ |
From now on, when any of your end-users clicks “Connect GitHub,” they see:
Authorize ClinicIQ Cloud
ClinicIQ Cloud by cliniciq.example is requesting access to:
✓ Repositories (read and write)
✓ User profile (read) Your brand. Your callback domain. MIOSA is invisible.
Your callback route forwards to MIOSA
@app.get("/oauth/callback")
def oauth_callback():
code = request.args["code"]
state = request.args["state"]
# MIOSA exchanges the code, stores the token, resolves the pending flow
client.oauth.complete(provider="github", code=code, state=state)
return redirect("/dashboard?connected=github") Step 6 - Disable end-user-scoped credentials (optional)
Some operators want all credentials managed centrally - end-users cannot add their own keys.
In your tenant dashboard → Settings → Security → toggle “Allow end-user-scoped credentials” to Off.
After that, any SDK call with scope="external_user" returns HTTP 403. Only external_workspace and tenant scoped secrets resolve.
Troubleshooting
Q: I stored a secret for dr_smith@clinic1.example but her sandbox can’t see it. Check that the sandbox was created with external_user_id="dr_smith@clinic1.example" (exact match, case-sensitive). Use client.sandboxes.get(sbx.id) and verify external_user_id on the returned object.
Q: The OAuth callback returns invalid_state. The state token has a 10-minute TTL. If the user took longer than that to authorize, the state expired. Re-initiate secrets.connect() to get a fresh authorize_url.
Q: I see “Authorize MIOSA” instead of my brand name. You registered the provider override but used MIOSA’s default OAuth app client ID. Verify the override in Settings → OAuth Providers shows your own client_id, not MIOSA’s.
Q: client.audit.list(external_user_id=...) returns an empty list. The sandbox must actually make outbound network calls for rows to appear. Confirm with sandbox.exec("curl -s https://api.openai.com/") - even a 401 generates an audit row.
What you learned
external_user_id/external_workspace_idare the identity primitives for white-label deployments.scope="external_user"secrets resolve only for that user’s sandboxes.client.audit.list(external_user_id=...)lets you build per-user audit dashboards without any custom logging.- The OAuth branding override requires registering your own OAuth app with the provider and pointing the callback at your domain.