Store API Keys Securely
Manage sensitive credentials using Azure Key Vault
Store API keys, passwords, and credentials securely in Azure Key Vault. Bifrost automatically encrypts and scopes secrets per organization.
Two Ways to Store Secrets
Section titled “Two Ways to Store Secrets”Settings → Configuration → + Add Config
- Key:
api_key - Type: Secret
- Value:
sk_live_xxxxx(your actual secret)
Stores the value encrypted in Key Vault automatically.
Settings → Secrets → + Add Secret
Create secret first, then reference it in config:
- Add secret with value
sk_live_xxxxx - Configuration → + Add Config
- Key:
api_key - Type: Secret Reference
- Value:
api_key(name of secret to reference)
- Key:
Useful for reusing secrets across multiple configs.
Use in Workflows
Section titled “Use in Workflows”from bifrost import config
@workflow(name="call_api")async def call_api(context: ExecutionContext): # Get secret (works for both Secret and Secret Reference types) api_key = await config.get("api_key")
headers = {"Authorization": f"Bearer {api_key}"} response = await make_api_call(url, headers=headers) return responseCommon Use Cases
Section titled “Common Use Cases”API Keys
Section titled “API Keys”Store third-party API credentials:
@workflow(name="external_api")async def external_api(context: ExecutionContext): api_key = await config.get("stripe_api_key")
response = requests.post( "https://api.stripe.com/v1/charges", headers={"Authorization": f"Bearer {api_key}"} ) return response.json()Database Credentials
Section titled “Database Credentials”Store database passwords securely:
@workflow(name="db_query")async def db_query(context: ExecutionContext): db_password = await config.get("postgres_password")
conn = await asyncpg.connect( host="db.example.com", user="bifrost", password=db_password, database="production" ) result = await conn.fetch("SELECT * FROM users") await conn.close() return resultWebhook Signatures
Section titled “Webhook Signatures”Verify incoming webhooks:
import hmacimport hashlib
@workflow(name="verify_webhook")async def verify_webhook(context: ExecutionContext, payload: str, signature: str): webhook_secret = await config.get("webhook_secret")
# Calculate expected signature expected = hmac.new( webhook_secret.encode(), payload.encode(), hashlib.sha256 ).hexdigest()
if not hmac.compare_digest(expected, signature): raise ValueError("Invalid webhook signature")
return {"verified": True}Organization Scoping
Section titled “Organization Scoping”Secrets are automatically isolated per organization:
- Org A stores
api_keywith valuesk_live_aaa - Org B stores
api_keywith valuesk_live_bbb - Workflows automatically use their org’s value
# Automatically uses current org's secretapi_key = await config.get("api_key")Each organization has completely separate secrets in Key Vault.
Updating Secrets
Section titled “Updating Secrets”Update Secret Config
Section titled “Update Secret Config”- Settings → Configuration
- Find config → Click Edit
- Enter new value → Save
Update Referenced Secret
Section titled “Update Referenced Secret”If using Secret Reference type:
- Settings → Secrets
- Find secret → Click Edit
- Enter new value → Save
All configs referencing this secret automatically use the new value.
Best Practices
Section titled “Best Practices”✅ Do This:
# Get from Key Vaultapi_key = await config.get("api_key")
# Log without exposing valuelogger.info("Making API call with stored credentials")
# Don't return in responsereturn {"success": True, "user_id": user.id}❌ Don’t Do This:
# Hardcoded secretapi_key = "sk_live_123abc"
# Logged secret valuelogger.info(f"API key: {api_key}")
# Returned in responsereturn {"api_key": api_key}Error Handling
Section titled “Error Handling”Always handle missing secrets gracefully:
@workflow(name="safe_api_call")async def safe_api_call(context: ExecutionContext): try: api_key = await config.get("api_key") except KeyError: return { "success": False, "error": "API key not configured", "action": "Add 'api_key' to Configuration in Settings" }
# Use api_key... return {"success": True}When to Use Each Type
Section titled “When to Use Each Type”| Type | Use When |
|---|---|
| Secret | One secret, one config. Simple and direct. |
| Secret Reference | Reusing secret across multiple configs. Easier rotation. |
Example: Secret Reference benefit
Create one secret stripe_key, reference it in multiple configs:
stripe_api_key→ referencesstripe_keypayment_processor_key→ referencesstripe_keybilling_api_key→ referencesstripe_key
Update secret once, all three configs get the new value.
Security Features
Section titled “Security Features”- Encryption: All secrets encrypted at rest in Azure Key Vault
- Access Control: RBAC managed through Azure
- Audit Logs: All secret access is logged
- Never Exposed: Secrets never returned in API responses or logs
- Org Isolation: Complete separation between organizations
Troubleshooting
Section titled “Troubleshooting”Config not found: Verify config exists in Settings → Configuration for your organization.
Permission denied: Ensure Bifrost has Key Vault access. Check Azure Key Vault access policies.
Secret not updating: If using Secret Reference, make sure the referenced secret name is correct.
Next Steps
Section titled “Next Steps”- OAuth Integration - Connect to external OAuth providers
- Microsoft Graph - Use Microsoft Graph API
- Custom APIs - Integrate any REST API