# Microsoft Bot Framework Twin — bf.twins.la A high-fidelity digital twin of two halves of the Microsoft Bot Framework that work independently: * Channel half (default channelId="msteams") — issues signed JWTs to bots, publishes JWKS, accepts bot-to-channel activity POSTs. * Bot half — emulates a Bot Framework bot at /api/messages and validates inbound channel JWTs. ## Manual-trust requirement Real Bot Framework SDKs hardcode `login.botframework.com` as the trusted JWKS source. To use this twin's channel half from a real bot, the SDK must be configured to trust the twin's JWKS URL: * .NET: `ConfigurationBotFrameworkAuthentication` with a `ToBotFromChannelOpenIdMetadataUrl` setting pointing at `/v1/.well-known/openidconfiguration`. * Python (botbuilder-py): `AuthenticationConfiguration` with `to_bot_from_channel_open_id_metadata_url` overridden. * JS: `ConfigurationBotFrameworkAuthentication` with `ToBotFromChannelOpenIdMetadataUrl` overridden. The twin documents this URL prominently — see /_twin/settings. ## Authentication Twin Plane: HTTP Basic (tenant_id:tenant_secret) Bootstrap a tenant first: POST /_twin/tenants -> {tenant_id, tenant_secret} Twin Plane Admin: Bearer token (set by deployment owner) Authorization: Bearer Or: X-Twin-Admin-Token: Provider: Channel side: bot acquires bearer at POST /v1/.well-known/oauth2/v2.0/token using client_id (= app_id) + client_secret (= app_password). Bot side: incoming requests carry Authorization: Bearer signed by the channel; the bot validates against the channel's JWKS. ## Key Endpoints Twin Plane (no auth): GET /_twin/health GET /_twin/scenarios GET /_twin/settings GET /_twin/references POST /_twin/tenants Twin Plane (Basic tenant_id:tenant_secret): POST /_twin/accounts — kind=bot OR kind=bot_instance GET /_twin/accounts — list both kinds GET /_twin/logs POST /_twin/simulate/inbound — drive a user→channel→bot delivery GET /_twin/bots//inbox — bot-half receive queue POST /_twin/bots//reply — operator-driven bot→channel reply POST /_twin/feedback GET /_twin/feedback Channel half (no auth): GET /v1/.well-known/openidconfiguration GET /v1/.well-known/keys POST /v1/.well-known/oauth2/v2.0/token (client_credentials) Channel half (Bearer): POST /v3/conversations/{conversationId}/activities POST /v3/conversations/{conversationId}/activities/{activityId} Bot half (Bearer JWT): POST /api/messages ## Quick Start (local) 1. pip install twins-microsoft-bot-framework twins-microsoft-bot-framework-local python -m twins_microsoft_bot_framework_local 2. Bootstrap a tenant: curl -X POST http://localhost:8080/_twin/tenants \ -H "Content-Type: application/json" \ -d '{"friendly_name": "Dev"}' # -> { tenant_id, tenant_secret } 3. Register a bot the channel will deliver to: curl -X POST http://localhost:8080/_twin/accounts \ -u "TENANT_ID:TENANT_SECRET" \ -H "Content-Type: application/json" \ -d '{"kind":"bot","messaging_endpoint":"http://localhost:9000/api/messages"}' # -> { app_id, app_password, ... } 4. Drive a synthetic user→channel→bot delivery: curl -X POST http://localhost:8080/_twin/simulate/inbound \ -u "TENANT_ID:TENANT_SECRET" \ -H "Content-Type: application/json" \ -d '{"bot_app_id":"","text":"hello"}' # -> { activity, webhook: { webhook_delivered, webhook_url, reason, status_code } } 5. (Optional) point the same instance at itself to drive a round-trip without external code: register a bot_instance with trusted_openid_url=http://localhost:8080/v1/.well-known/openidconfiguration and set the bot's messaging_endpoint to /api/messages on the same process. ## Reference GitHub: https://github.com/twins-la/microsoft-bot-framework Project overview: https://twins.la All twins: https://github.com/twins-la/twins-la