Here we cover integrating Microsoft Teams with Azure serverless functions and SSO authentication to create a Channel Tabs app. Then we discuss how to easily create a minimal project for a working Channel Tab app hosted in the Azure cloud, using Microsoft Teams with Python and Visual Studio Code.
In the first article of this series, we learned step-by-step how to build a Personal Tab app for Microsoft Teams in Python and backed by serverless Azure Functions. In this second article of our three-part series, we’ll create a Channel and Group Tabs app with Python and Azure Functions. Fortunately, we can reuse much of our code, and the learning curve lessens from now on.
Microsoft Teams Channel and Group apps present users with a page featuring information regarding that channel or group. This type of Teams app enables people to organize their calendars, project management worksheets, files, and more, and view other information that team or channel members share.
We base this new app on an existing sample in C# that illustrates implementing a Channel and Groups Tab with SSO authentication.
Starting from the code presented in the first article, you can implement the Channel and Group Tab app step-by-step by following this tutorial. Alternatively, you can download the source code from GitHub and jump to the configuration steps to get the app up and running. To follow along, you should know Python and have a free Azure account. We’ll show you how to do the rest.
Examining the Application Architecture
Just like the Personal Tab app we developed in the first article of this series, we’re building a serverless app with Python and Flask to control application flow and render HTML content inside Teams.
When the serverless functions first load, they read an HTML template from disk and cache it in a variable to quickly render the web page HTML.
The app’s structure is the same as in the first article. However, this time, we’re adding a new az-function-configuration
HTTP trigger to provide the Teams Tab with a configuration page for when someone adds the app to the channel or group:
This application consists of only five Azure Functions: get-user-access-token
, index
, auth-start
, auth-end
, and configuration. We borrow functions from the first project, except for configuration, which we'll build in this tutorial.
Registering the Application on Azure
Before sending an access request to Teams, we must register a new application in the Azure Active Directory (AAD) App Registrations portal and create a secret that the app uses to authenticate to Azure. We already made an app registration for a Personal Tab in the first article, and both applications are similar. However, we want to create a Channel Tab app this time, so we must make a new registration. Follow the steps below to set up your new app registration.
First, follow the instructions for this process in the Teams Tab SSO Authentication README. You can use the same steps to register your Teams Python application with Azure AD. However, we’re creating an app registration with the channel-tab-sso-function-app
name this time.
Next, change the App Registration in the Azure Portal so that the Redirect URI link points to your Function App website (for example, *.azurewebsites.net). Also, checkmark the Implicit grant and hybrid flows options.
Also, ensure that the Application ID URI points to the /api/az-function-auth-end
in the Expose an API tab:
Reusing Code from the Existing Personal Tabs App
Now let's reuse the Personal Tabs code we created in the first article to build a large part of our Channel Tab application.
First, create a new folder in the \ChannelTabSSO folder to contain your new Python project. Then, open your computer’s terminal in the new folder and type the following command to open VS Code in the selected folder:
\ChannelTabSSO>code .
Then, copy the static files from the old PersonalTabSSO
project to the new ChannelTabSSO
project as follows:
ChannelTabSSO
+-- static
+-- js
+-- auth.js
+-- templates
|-- base.html
|-- index.html
|-- auth_start.html
+-- auth_end.html
Next, copy the ssoAuthHelper.py and cacheHelper.py files from the PersonalTabSSO folder to ChannelTabSSO
:
ChannelTabSSO
|-- ssoAuthHelper.py
+-- cacheHelper.py
Then, copy the az-functions-* folders and their contained files from the PersonalTabSSO folder to ChannelTabSSO
:
ChannelTabSSO
|-- az-function-auth-end/
|-- az-function-auth-start/
|-- az-function-index/
+-- az-function-get-user-access-token/
This copy-and-paste work is the hardest part of the Channel Tabs application. Once finished, VS Code will display your project like this:
Adding Configuration Page Files
Channel Tab apps are slightly different from Personal Tab apps. Microsoft designed the Channel or Group Tab apps to prompt a configuration page that presents choices and collects information from users, then sets the contentUrl
of the content page according to the user's response.
As the Microsoft documentation creating configuration pages shows, the configuration page is a special content page that controls what group users can do within the app. Follow the steps below to implement the configuration page.
First, create a new images folder under static, then download the images from the source repository images folder and add them to the images folder:
ChannelTabSSO
+-- static
+-- images
|-- IconGray.png
|-- IconRed.png
+-- TeamsIcon.png.png
Now create the configuration.html file in the templates folder with the following contents:
{% extends "base.html" %}
{% block title %}Python Configuration Page{% endblock %}
{% block content %}
<h1>Python Configuration Channel Tab SSO Authentication</h1>
<div id="divError" style="display: none"></div>
<button onclick="requestConsent()" id="consent" style="display:none;">Authenticate</button>
<div id="divGraphProfile" style="display: none"></div>
<button onclick="(document.getElementById('icon').src = '/static/images/IconGray.png'); colorClickGray()">Select Gray</button>
<img id="icon" src="https://raw.githubusercontent.com/marcelooliveira/PythonTeamsApps/main/ChannelTabSSO/static/images/TeamsIcon.png" alt="icon" style="width:100px" />
<button onclick="(document.getElementById('icon').src = '/static/images/IconRed.png'); colorClickRed()">Select Red</button>
{% endblock %}
{% block scripts %}
<script>
microsoftTeams.initialize();
let saveGray = () => {
microsoftTeams.settings.registerOnSaveHandler((saveEvent) => {
microsoftTeams.settings.setSettings({
websiteUrl: window.location.origin ,
contentUrl: window.location.origin + "/api/az-function-index",
entityId: "Python Channel Configure",
suggestedDisplayName: "Python Channel Configure - Grey Settings"
});
saveEvent.notifySuccess();
});
}
let saveRed = () => {
microsoftTeams.settings.registerOnSaveHandler((saveEvent) => {
microsoftTeams.settings.setSettings({
websiteUrl: window.location.origin ,
contentUrl: window.location.origin + "/api/az-function-index",
entityId: "Python Channel Configure",
suggestedDisplayName: "Python Channel Configure - Red Settings"
});
saveEvent.notifySuccess();
});
}
let icon = document.getElementById("icon");
const colorClickGray = () => {
microsoftTeams.settings.setValidityState(true);
saveGray()
}
const colorClickRed = () => {
microsoftTeams.settings.setValidityState(true);
saveRed();
}
</script>
{% endblock %}
Notice how the configuration page is slightly different from the index content page. Later, we’ll discuss how the configuration page works and how Teams presents it to the users when adding the Channel Tab app to a Channel or Group.
Creating the az-function-configuration Azure Function
We have already copied four Azure functions from the first Personal Tab app. But, we must still create the Azure function to work as an HTTP trigger and render our configuration page.
First, click the Azure tab and click the Create New Project icon:
Next, select ChannelTabSSO as your local function project folder:
Then, select Python as your function app language:
Next, select HTTP trigger as the Azure Function template:
Name it az-function-configuration
:
Then, make the function Anonymous so that anybody can access its endpoint without passing a code parameter:
Replace the contents of the __init__.py file in the \az-function-configuration folder with this code:
import azure.functions as func
from flask import Flask
import sys
from cacheHelper import CacheHelper
app = Flask(__name__)
this = sys.modules[__name__]
this.cacheHelper = None
def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
if this.cacheHelper is None:
this.cacheHelper = CacheHelper(context.function_directory)
return func.WsgiMiddleware(app).handle(req, context)
@app.route("/api/az-function-configuration")
def index():
return this.cacheHelper.render_cached_page(app, "configuration.html")
The code above re-routes calls to the /api/az-function-configuration
endpoint to the Python code and renders them with the Flask engine.
Application Setup and Configuration
As in the first article, we change some configurations so that our Channel Tab app can work on our development machine and in the cloud.
First, modify requirements.txt to ensure it contains these lines:
azure-functions
requests
flask==2.0.2
msal
Then, open the terminal at the project root folder and install the requirements:
pip install -r requirements.txt
Next, click the Azure tab, select your Function app, and click Download Remote Settings:
Open the local.settings.json file next. Then, add the configuration from the app registration you created on Azure Portal. In this case, we use the settings from channel-tab-sso-function-app
.
Include the following configurations in the local.settings.json file: Instance
, TenantId
, ClientId
, AppSecret
, AuthUrl
, and CacheEnabled
:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=personaltabssofunctionap;AccountKey=*************************************;EndpointSuffix=core.windows.net",
"FUNCTIONS_WORKER_RUNTIME": "python",
"FUNCTIONS_EXTENSION_VERSION": "~4",
"WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "DefaultEndpointsProtocol=https;AccountName=personaltabssofunctionap;AccountKey=********************************************;EndpointSuffix=core.windows.net",
"WEBSITE_CONTENTSHARE": "personal-tab-sso-function-app45e3c0",
"APPINSIGHTS_INSTRUMENTATIONKEY": "********-****-****-****-************",
"Instance": "https://login.microsoftonline.com/",
"TenantId": "********-****-****-****-************",
"ClientId": "********-****-****-****-************",
"AppSecret": "***************************************************************",
"AuthUrl": "/oauth2/v2.0/token",
"CacheEnabled": "false"
}
}
On the Azure tab, click Upload Local Settings. This command sends your local configurations to your Function App in the cloud:
Finally, press F5 to run the project in debug mode:
Observe how we now have five working Azure functions:
az-function-auth-end: [GET,POST] http://localhost:7071/api/az-function-auth-end
az-function-auth-start: [GET,POST] http://localhost:7071/api/az-function-auth-start
az-function-configuration: [GET,POST] http://localhost:7071/api/az-function-configuration
az-function-get-user-access-token: [GET,POST] http://localhost:7071/api/az-function-get-user-access-token
az-function-index: [GET,POST] http://localhost:7071/api/az-function-index
Now click the Functions folder within the Local Project folder and click the Deploy to the Function App icon. This action uploads your Python-based Functions to Azure:
Finally, select your Function App in Azure for deployment:
Adding the Channel Tab App to a Team
We’ll install the Channel Tab app by uploading a .zip file containing the manifest like the Personal Tab app. First, click the Apps tab and click the Upload a Custom App link:
Then, select the manifest.zip file:
At this point, you’ve installed your new app. But, unlike Personal Tab apps, users can’t open Channel Tab apps immediately. Instead, the user can only open Channel and Group Tabs after someone adds the app to the team. Let’s explore this process.
First, click the app on the Apps tab and click the Add to a team button:
Then, select the team where you want to add the app:
After you add your app to the team, the app displays the Configuration page we created before:
This page is a great place to define parameters, behaviors, and appearances to customize how your team or channel will use your new Python app. Although our sample Configuration page just has some placeholder buttons to select colors, it hints at the real-world possibilities:
Using the Channel Tab App in a Team
After we add the Channel Tab app to a team, users can view a new Configure tab in the team’s interface:
Now Teams redirects our user to the Index page. But as with Personal Tab apps, Channel Tab apps must first request and receive authorization from the users before use. Here is the same Index content page we copied from the original Personal Tab project featuring the Authenticate button:
Teams requests the user to provide consent for your app before use:
Now your team users can enjoy their Channel Tab with SSO authentication. What your application displays here depends on your application and the options you defined on the Configuration page.
Next Steps
In this second installment of the series, we’ve covered integrating Microsoft Teams with Azure serverless functions and SSO authentication to create a Channel Tabs app. We’ve discussed how to easily create a minimal project for a working Channel Tab app hosted in the Azure cloud, using Microsoft Teams with Python and Visual Studio Code.
We created a new Channel Tab app with minimal effort from the first Personal Tab app. Flask and Jinja use HTML and JavaScript code to generate the new Configuration web page. And once again, Python code shines as the driving force behind our Azure serverless functions.
You now have a working channel tab application that you can use as a scaffolding for developing professional Microsoft Teams apps using your Python programming skills.
Sign up for a free trial of Azure to create and run your own Python-based Teams apps, or continue to the third and final part of this series to learn how to build tabs with adaptive cards.
To learn more about how to build apps for an individual, your team, your organization, or for all Microsoft Teams users everywhere, check out Develop apps for Microsoft Teams.