Migrate API Keys
Why Migrate
Embedding org API keys in client applications is a significant security risk. If any device is compromised, jailbroken, or reverse-engineered, the attacker obtains a credential that grants full org-level access. This key cannot be revoked without breaking every other device using it, and there is no way to determine which device made a particular API call.
Specific risks of embedded API keys:
- No per-device revocation: Revoking the key cuts off every device in the org simultaneously.
- No attribution: API calls made with the same key are indistinguishable. You cannot tell which device submitted a training update or identify a compromised device.
- Extraction is trivial: On Android, APK decompilation reveals hardcoded strings in minutes. On iOS, runtime inspection tools can dump memory. Obfuscation does not provide real security.
- Compliance failures: SOC 2, HIPAA, and PCI-DSS auditors flag long-lived credentials embedded in client applications as a finding.
The device token model eliminates all of these issues. Each device gets its own short-lived token, issued by your backend, with full audit trail and individual revocation capability.
Before
- Client app embeds long-lived org API key
- Key leakage risk and difficult revocation semantics
After
- Backend keeps org API key secret
- Client receives short-lived device tokens via bootstrap exchange
Prerequisites
Before starting the migration, verify:
- Your backend service can securely store the org API key (secret manager, not source code)
- You have a way to deliver bootstrap tokens to devices (app provisioning flow, push notification, or MDM)
- The Octomil device auth endpoints are enabled for your org (check Dashboard -> Settings -> Device Auth)
- Your SDK version supports device auth: Python SDK >= 0.5.0, iOS SDK >= 0.4.0, Android SDK >= 0.4.0
- You have monitoring in place to track auth failures (see Monitoring Dashboard)
- You have identified all client applications and app versions that currently embed the API key
Migration Steps
1. Keep existing org API key only in backend service
Move the API key from your client application code to your backend environment. The key should be stored in your deployment secret manager (AWS Secrets Manager, Vault, Railway secrets, etc.) and injected as an environment variable.
Before (insecure): The org API key is hardcoded in the client app and ships in the binary.
After (secure): The API key lives in your backend's secret manager (os.environ["OCTOMIL_API_KEY"]). Client apps never see it — they receive short-lived device tokens instead.
2. Add backend endpoint that returns bootstrap bearer token to trusted app session
Create an endpoint in your backend that your client app calls (after authenticating the user) to obtain a bootstrap token. Your backend uses the org API key to request the bootstrap token from Octomil.
# backend/routes/device_auth.py
import httpx
from fastapi import APIRouter, Depends
from your_app.auth import get_current_user
router = APIRouter()
@router.post("/device/bootstrap")
async def get_bootstrap_token(
device_id: str,
user = Depends(get_current_user), # Your app's user auth
):
"""
Authenticated endpoint that returns a bootstrap token for the device.
The org API key never leaves the backend.
"""
async with httpx.AsyncClient() as client:
response = await client.post(
"https://api.octomil.com/api/v1/device-auth/token",
headers={
"X-API-Key": os.environ["OCTOMIL_API_KEY"],
},
json={
"device_id": device_id,
"ttl": 900, # 15 minutes
},
)
response.raise_for_status()
return {"bootstrap_token": response.json()["bootstrap_token"]}
3. Integrate SDK device auth manager (bootstrap, refresh, revoke)
Update your client application to use the SDK's device auth flow instead of passing the API key directly.
Python SDK:
from octomil import DeviceClient
# Get bootstrap token from YOUR backend, then initialize
bootstrap_token = requests.post(
"https://your-backend.com/device/bootstrap",
json={"device_id": device_id},
headers={"Authorization": f"Bearer {user_session_token}"},
).json()["bootstrap_token"]
client = DeviceClient(bootstrap_token=bootstrap_token)
# SDK handles token refresh automatically from here
result = client.submit_training_update(weights=updated_weights)
iOS SDK (Swift):
let deviceClient = try await Octomil.DeviceClient(
bootstrapToken: bootstrapToken,
deviceId: UIDevice.current.identifierForVendor?.uuidString ?? ""
)
try await deviceClient.submitTrainingUpdate(weights: updatedWeights)
Android SDK (Kotlin):
val deviceClient = Octomil.DeviceClient(
context = applicationContext,
bootstrapToken = bootstrapToken,
deviceId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
)
deviceClient.submitTrainingUpdate(weights = updatedWeights)
4. Remove org API key from all client bundles
Search your codebase for any remaining references to the API key in client-side code:
# Find hardcoded API keys in your codebase
grep -r "sk_live_\|sk_test_\|OCTOMIL_API_KEY" \
--include="*.swift" --include="*.kt" --include="*.py" --include="*.ts" \
--exclude-dir="backend" --exclude-dir="server" .
Remove every occurrence. If any are found in build artifacts (APK, IPA), the key must be rotated after migration is complete.
5. Roll out gradually by app version and monitor auth failures
Do not migrate all devices simultaneously. Use a phased approach.
Phased Rollout Strategy
| Phase | Duration | Scope | Gate Criteria |
|---|---|---|---|
| 1. Internal testing | 1 week | 5-10 internal devices (iOS, Android, Python) | Zero auth failures; bootstrap, refresh, and revocation validated |
| 2. Canary | 1-2 weeks | 5% of production devices (feature flag) | < 0.5% failure rate on new flow; pause if > 1% |
| 3. Broad rollout | 2-4 weeks | 25% -> 50% -> 100%, hold 48h at each stage | Error rates stable at each stage; legacy flow still active as fallback |
| 4. Deprecation | 2 weeks after 100% | Announce cutoff, then disable legacy key path | All devices migrated; cutoff date communicated |
Monitoring Migration Progress
Track the migration using the following metrics and dashboard views:
Key Metrics
| Metric | Expected Trend |
|---|---|
| Device token adoption rate | Rising to 1.0 |
| Legacy API key usage | Falling to zero |
| Device token auth successes | Rising |
| Auth failure rate by method | Comparable or improving |
In the Monitoring Dashboard, create panels for migration progress (daily adoption %), failure rate comparison by auth method, and app version breakdown showing which versions still use the legacy flow.
Rollback
- Temporarily permit old flow for a limited window
- Keep strict telemetry on legacy calls
- Set cutoff date and disable legacy key path
Detailed Rollback Procedure
If the device token flow causes unacceptable error rates during rollout:
- Immediate: Re-enable the legacy API key flow for affected devices. This requires no client update if the app has fallback logic:
# Client-side fallback (build this in during migration)
try:
client = DeviceClient(bootstrap_token=get_bootstrap_token())
except BootstrapError:
# Fallback to legacy flow (temporary)
client = LegacyClient(api_key=get_legacy_key())
report_fallback_event()
-
Within 24 hours: Identify the root cause. Common issues:
- Backend bootstrap endpoint returning errors (check backend logs)
- Network timeout during bootstrap exchange (increase TTL or retry count)
- SDK version incompatibility (verify minimum SDK versions)
-
Fix and re-deploy: Address the root cause, test in staging, and resume the phased rollout from the last successful phase.
Success Criteria
- 0 client bundles containing org API keys
-
99% of devices using short-lived token flow
- Revocation path validated in incident drills
Validation Checklist
After migration is complete, verify:
-
grepfor API key patterns in all client repositories returns zero results - The legacy API key has been rotated (old key is no longer valid)
- The
octomil_api_key_auth_total{source="client"}metric is at zero for 7 consecutive days - A test revocation of a device token successfully blocks the device from API access
- A test deprovisioning via SCIM successfully revokes all associated device tokens
- The incident runbook includes device token revocation steps
Post-Migration Cleanup
After the migration is validated and the cutoff date has passed:
- Rotate the org API key. The old key may have been exposed in client bundles. Generate a new key in Dashboard -> Settings -> API Keys and update your backend service.
- Remove fallback code from client applications. The legacy client initialization path is dead code after migration.
- Update documentation and onboarding guides to reference the device token flow exclusively.
- Archive migration monitoring dashboards or repurpose them for ongoing device auth health monitoring.
Related Docs
- Device Token Lifecycle -- detailed token flow, storage, and refresh behavior
- Security Architecture -- how device auth fits into the security model
- Admin Onboarding: SSO + SCIM -- how user deprovisioning triggers token revocation
- Python SDK -- Python SDK device auth reference
- iOS SDK -- iOS SDK device auth reference
- Android SDK -- Android SDK device auth reference