Identity & Auth
User IDs
| Aspect | Slack | Teams |
|---|---|---|
| Format | Prefixed strings: U... (user), C... (channel), T... (team), B... (bot) | GUIDs: AAD object IDs, opaque conversation IDs |
| User identity | message.user → Slack user ID | activity.from.id → AAD object ID |
| Cross-reference | users.info(\{ user \}) → email, display name | userGraph.me() → email, display name |
| Channel identity | channel_id (flat namespace) | conversation.id (scoped to team) |
Rating: RED — IDs are completely incompatible. No conversion formula exists.
Impact
Any stored data keyed by Slack user/channel IDs (preferences, history, permissions) cannot be directly used with Teams IDs. A mapping layer is required.
Mitigation Strategy
Use email as the common identity attribute:
- Build a mapping table:
Slack user ID → email → AAD Object ID - Populate from Slack's
users.info()and Teams' Graph APIusers/\{id\} - Re-key stored data during migration
- For new dual-platform bots, key data by email from the start
Effort: 8–16 hrs depending on data volume.
Authentication & Signing
| Aspect | Slack | Teams |
|---|---|---|
| Request verification | signingSecret — HMAC-SHA256 of v0:\{timestamp\}:\{body\} | Bot Framework JWT — automatic validation by SDK |
| Manual verification | Required if using raw HTTP | Required only without SDK (REST-only integration) |
| Bot credentials | SLACK_BOT_TOKEN (xoxb-...) | CLIENT_ID + CLIENT_SECRET + TENANT_ID |
| App-level token | SLACK_APP_TOKEN (xapp-..., Socket Mode only) | Not applicable |
Rating: GREEN — both SDKs handle verification automatically.
Mitigation: No code changes needed when using SDKs. Both handle signing/verification internally. For REST-only integrations, see the verification patterns below.
REST-Only Verification
Slack (manual HMAC):
signature = HMAC-SHA256(signingSecret, "v0:{timestamp}:{rawBody}")
compare with X-Slack-Signature header
reject if timestamp > 5 minutes oldTeams (manual JWT):
fetch OpenID config from https://login.botframework.com/v1/.well-known/openidconfiguration
validate JWT from Authorization header
verify audience = your bot's CLIENT_ID
verify issuer = https://api.botframework.comOAuth & Tokens
| Aspect | Slack | Teams |
|---|---|---|
| User OAuth | users:read, chat:write, etc. scopes | Azure AD Graph permissions (delegated) |
| Bot token | xoxb-... (per-workspace) | Bot Framework token (per-tenant, auto-managed) |
| Token storage | InstallationStore (per-workspace bot+user tokens) | Not needed — SDK handles token lifecycle |
| SSO | Not native — redirect flow | Built-in with oauth: \{ defaultConnectionName \} |
| Sign-in flow | OAuth redirect to Slack authorize URL | ctx.signin() sends OAuth card in chat |
| Sign-out | Revoke token via API | ctx.signout() |
| Multi-tenant | InstallationStore with per-workspace tokens | signInAudience: "AzureADMultipleOrgs" in Azure AD |
Rating: YELLOW — both support OAuth but the flows and storage models differ significantly.
Key Difference
Slack requires per-workspace token management via InstallationStore. Teams SDK manages tokens automatically — you just configure clientId, clientSecret, tenantId, and oauth.defaultConnectionName.
Mitigation (Slack → Teams)
- Remove
InstallationStore(not needed) - Register an OAuth connection in Azure Bot resource settings
- Add
oauth: \{ defaultConnectionName: "graph" \}to App constructor - Guard handlers with
ctx.isSignedIncheck - Call
ctx.signin()when authentication is needed
Mitigation (Teams → Slack)
- Implement
InstallationStorefor per-workspace token storage - Configure OAuth scopes in Slack app settings
- Set up
InstallProviderfor the OAuth install flow - Store bot and user tokens per workspace
- Use stored tokens for API calls via
WebClient
Slack OAuth Scopes → Teams Graph Permissions
| Slack Scope | Teams Graph Permission | Notes |
|---|---|---|
users:read | User.Read (delegated) | |
users:read.email | User.Read (delegated) | Email included by default |
chat:write | Bot sends via SDK (no permission) | |
channels:read | Channel.ReadBasic.All | |
channels:history | ChannelMessage.Read.All | |
files:read | Files.Read | |
files:write | Files.ReadWrite |