\u003Cfigcaption>What a question looks like once integrated\u003C/figcaption>\u003C/figure>\n\u003C!-- /wp:image -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Everyone who wants to use SO for Teams within MS Teams has to go through the install process once, no matter how many other people have already completed it. That’s because the key part of this process is linking the Microsoft Teams tenant/user with the Stack Overflow team/user. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:heading -->\n\u003Ch2>Keeping Team Data Secure\u003C/h2>\n\u003C!-- /wp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>We spent a lot of time making sure that the security was tight. We’re dealing with companies’ internal conversations and proprietary information, so it’s very important to make sure that’s locked down. The first security gate comes when we require an admin of the SO for Teams instance to authorize the app in order to allow any user to use it. That’s because a user who is part of multiple SO for Teams might paste a link into MS Teams that might not be allowed for users in that conversation (or entire MS Teams instance). \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>After consulting with our internal experts, we realized that we didn’t need to build out a full OAuth2 process—our internal auth would work fine. But for any conversation to happen, a user still has to be authorized on both sides, no matter what authentication method the organizations use for MS Teams and SO for Teams, whether it’s SAML, OAuth, or something else. They just don’t have to perform authentication between the systems. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Then, to secure every request, whether it’s to unfurl a link, perform a search, or notify a user of new questions and answers, it has to be validated against a JWT token. But because we use the REST API, we had to decompose this process from their SDK and just use the bits that we needed. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>The message auth above highlights a difference between MS Teams and our Slack integrations. In Slack, we authenticated with their app. In MS Teams, they authenticate every payload with us. It behaves as if they are using our API instead of vice versa. It was backwards to what we previously implemented, so it required a ground-up redesign of our chat integration logic. And because we were the ones doing the authorization, we had to be a little more careful about security. From the user’s perspective, though, the process looks almost exactly the same. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>We added a couple of extra security features for this integration to ensure all proprietary data stayed safe and did not leak out. The initial login screen comes from the public SO site. But every SO for Teams instance is stored on a separate, segregated server. We have no idea which SO Team a user is a part of until they login, then we can associate the Microsoft Teams user with the Stack Overflow Teams user. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Second, we want to make sure that absolutely nothing vital leaked out accidently. On all Q&A sites, public and private, you click on a tag to see all the questions associated with it. On the public sites, that tag goes in the URL, and that URL ends up in the server logs, which, for SO for Teams, could be \u003Ca href=\"https://vicki.substack.com/p/logs-were-our-lifeblood-now-theyre\">a leak of information\u003C/a>. So for SO for Teams, we encrypt all tags before they get to the URL, even though it’s internal to us. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:heading -->\n\u003Ch2>Designing Bots for Teams\u003C/h2>\n\u003C!-- /wp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>With the integration installed, a given MS Teams user has a bot added to their view. They could treat this bot as a person and request information, say, “Hey, how do I exit vim?” The bot would pass an Activity object and the search query to our endpoint, which would return the best matching question. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:image {\"id\":13915} -->\n\u003Cfigure class=\"wp-block-image\">\u003Cimg src=\"https://stackoverflow.blog/wp-content/uploads/2019/10/Frame-2-1121x630.png\" alt=\"\" class=\"wp-image-13915\"/>\u003Cfigcaption>Search results within MS Teams\u003C/figcaption>\u003C/figure>\n\u003C!-- /wp:image -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>But that only solved part of our problem. We want people to be able to monitor tags so that the questions and answers that they are interested in come to them. For the most part, MS Teams assumes conversations are two-way and initiated by the user. Which meant that we couldn’t pass information to the user unless they initiated a conversation—at that point, we would get the identifiers for the channel. Instead, we could use proactive messaging, which allowed a bot to initiate, but that required a ton of information from the bot to get started. To get this proactive messaging from the outset, we had to go back and change the installer to store that information when the user initially installed the integration. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>To create the actual links between a tag that you want to monitor and the MS Teams user, we use custom webhooks. MS Teams lets integrations embed configuration pages (known as Tabs; more on this below) into the program. We use these pages to let the user configure a webhook that will receive incoming questions and answers related to the tags that they care about. There, they could determine whether they want questions, answers, and/or comments for a tag sent to a given channel. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:heading -->\n\u003Ch2>Keeping Tabs on Teams\u003C/h2>\n\u003C!-- /wp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Tabs, while powerful tools to configure our integration within MS Teams, almost didn’t make it into our initial build because of the additional complexity they added. A Tab is essentially an iframe loaded in MS Teams, either in a channel or on an individual user, to configure something for that chat location. So the channel tab would configure notifications for a single channel, and the user tab would configure personal notifications for a single user. The Stack Overflow site serves up the content of the iframe; it was the same settings page as you would see using SO Teams. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>But we weren’t on the SO site here; we were within the MS Teams application and had to match its look and feel. Microsoft provided those \u003Ca href=\"https://github.com/OfficeDev/msteams-ui-components/tree/master/msteams-ui-styles-core\">styles as TypeScript\u003C/a> and a build script that pulled those styles out into a standard CSS file. The issue was that the script didn’t pull everything out. So we had to tweak it a little here and there. The short of it is, yes, we support dark mode within MS Teams. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Our tabs primarily create/edit/delete the webhook notifications used to send new questions and answers to channels and users. Unfortunately, tabs have even less context than bots, so integrating with those challenges was extra difficult. MS Teams walls off some of the API functionality from tabs for a very good reason—you don’t want a random webpage in your app accessing the same data as an approved and vetted integration. But our tab still needed some of this information. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:image {\"id\":13917} -->\n\u003Cfigure class=\"wp-block-image\">\u003Cimg src=\"https://stackoverflow.blog/wp-content/uploads/2019/10/Frame-4-1121x630.png\" alt=\"\" class=\"wp-image-13917\"/>\u003Cfigcaption>Notification settings in Teams\u003C/figcaption>\u003C/figure>\n\u003C!-- /wp:image -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>To pull this off, we cached some extra information in the installation, specifically the \u003Ccode>conversationUpdate \u003C/code>event. When the user installs a tab, we tie this event to them and the tab. If the user has already authorized with the bot, great, we got the info we need. If the user hasn’t done that and we don’t have the cached info, we just ask the user to sign in with the bot again and set an account cookie. It’s something of a pain for the user to sign in again, but the more deliberate gates there are to prevent leaking information, the better. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Speaking of security, because tabs iframe out to our site, we needed to add additional precautions on the iframe content. Generally, we disallow iframes of our content globally, so just to enable this, we had to set up specific response headers for specific routes. To verify the user, we couldn’t just use the identifiers within either app; technically, those could be spoofed. We couldn’t validate against the JWT like we do with bot requests, tab can’t access that. So we use the cookie we mentioned above to validate. And if that user was anonymous when they sent the request, we send them to log in again, and we get the cookie in place. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:heading -->\n\u003Ch2>We’ve Teamed Teams with Teams\u003C/h2>\n\u003C!-- /wp:heading -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Our customers had been asking for this for a while, but Microsoft ended up contacting us and providing a recommended feature set. In fact, Microsoft was incredibly helpful throughout the whole process. Initially, they gave us a weekly fifteen minute call with their engineers, but that meant sometimes we’d be blocked until the call. To fix this, they eventually gave us an entire MS Teams instance in which to ask questions of their engineers and get the project completed as quickly as possible. We’d be blocked on something for hours, sometimes for days, and within a few minutes of posting in this MS Teams instance, our Microsoft engineering contact was able to unblock us in a matter of minutes. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>On top of that, they gave us free access to MS Teams for 90 days so that we could invite anyone we wanted to test the integration thoroughly. Microsoft went above and beyond during this whole integration—we wouldn’t have been able to do it without them. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>\u003Ca href=\"https://appsource.microsoft.com/en-us/product/office/WA200000739\">Our integration with Microsoft Teams\u003C/a> continues our mission to situate your knowledge management process within your existing workflow. Chat programs are central to our work processes, so we want to be available in the tools that they use. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>Again, we couldn’t have gotten this integration completed as quickly as we did without Microsoft’s help. They gave us a ton of support and helped us get this project through the process faster than we could have alone. \u003C/p>\n\u003C!-- /wp:paragraph -->\n\n\u003C!-- wp:separator -->\n\u003Chr class=\"wp-block-separator\"/>\n\u003C!-- /wp:separator -->\n\n\u003C!-- wp:paragraph -->\n\u003Cp>\u003Cem>New to Stack Overflow for Teams? Now it's free for your first 25 users. \u003Ca href=\"https://stackoverflow.com/teams?utm_source=so-owned&utm_medium=blog&utm_campaign=microsoft-integration&utm_content=tech-blog-announcement\">Try Teams for free\u003C/a> today and make internal knowledge sharing and information discovery even easier with our new integration.\u003C/em>\u003C/p>\n\u003C!-- /wp:paragraph -->","html","2019-11-04T17:02:48.000Z",{"current":514},"a-technical-deep-dive-into-our-ms-teams-integration",[516,524,526,531,533,537,542],{"_createdAt":517,"_id":518,"_rev":519,"_type":520,"_updatedAt":517,"slug":521,"title":523},"2023-05-23T16:43:21Z","wp-tagcat-announcements","9HpbCsT2tq0xwozQfkc4ih","blogTag",{"current":522},"announcements","Announcements",{"_createdAt":517,"_id":518,"_rev":519,"_type":520,"_updatedAt":517,"slug":525,"title":523},{"current":522},{"_createdAt":517,"_id":527,"_rev":519,"_type":520,"_updatedAt":517,"slug":528,"title":530},"wp-tagcat-engineering",{"current":529},"engineering","Engineering",{"_createdAt":517,"_id":527,"_rev":519,"_type":520,"_updatedAt":517,"slug":532,"title":530},{"current":529},{"_createdAt":517,"_id":534,"_rev":519,"_type":520,"_updatedAt":517,"slug":535,"title":536},"wp-tagcat-integrations",{"current":536},"integrations",{"_createdAt":517,"_id":538,"_rev":519,"_type":520,"_updatedAt":517,"slug":539,"title":541},"wp-tagcat-microsoft-teams",{"current":540},"microsoft-teams","microsoft teams",{"_createdAt":517,"_id":543,"_rev":519,"_type":520,"_updatedAt":517,"slug":544,"title":546},"wp-tagcat-stack-overflow-for-teams",{"current":545},"stack-overflow-for-teams","stack overflow for teams","A Technical Deep Dive into Our MS Teams Integration",[549,555,560,565],{"_id":550,"publishedAt":551,"slug":552,"sponsored":12,"title":554},"1d082483-6dc6-424b-8b09-9c84b54779da","2025-09-02T17:00:00.000Z",{"_type":10,"current":553},"back-to-school-developers-at-stack-overflow-have-some-advice-for-you","Back to school? Developers at Stack Overflow have some advice for you",{"_id":556,"publishedAt":551,"slug":557,"sponsored":12,"title":559},"5cd91820-9515-4be5-87ae-e919fd443c18",{"_type":10,"current":558},"getting-started-on-stack-overflow-a-step-by-step-guide-for-students","Getting started on Stack Overflow: a step-by-step guide for students",{"_id":561,"publishedAt":551,"slug":562,"sponsored":12,"title":564},"614538a9-c352-4024-adf1-fa44a9f911b6",{"_type":10,"current":563},"stack-overflow-is-helping-you-learn-to-code-with-new-resources","Stack Overflow is helping you learn to code with new resources",{"_id":566,"publishedAt":551,"slug":567,"sponsored":12,"title":569},"763b1d36-83d8-4178-9c2d-32d705ea1d7b",{"_type":10,"current":568},"introducing-your-newest-study-buddy-stackoverflow-ai","Introducing your newest study buddy: stackoverflow.ai",{"count":571,"lastTimestamp":572},3,"2023-05-25T09:46:51Z",["Reactive",574],{"$sarticleModal":575},false,["Set"],["ShallowReactive",578],{"sanity-MRxiJznr88t9BRx0-7UeIduDmON0UWijt6rFyyzURVw":-1,"sanity-comment-wp-post-13910-1757291981828":-1},"/2019/11/04/a-technical-deep-dive-into-our-ms-teams-integration"]