Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Building Microsoft Teams Tabs Apps with Java Part 2: Creating a Channel or Group Tab with SSO

0.00/5 (No votes)
15 Nov 2021 1  
In this article we'll explore creating a channel or group tab with SSO.
This article demonstrates how to create a Spring Boot web app in Java that replicates the functionality of the Channel and group tab with SSO quick-start sample app on GitHub.

This is the second article in a three-part series demonstrating how the Microsoft Teams sample library can be used to jump-start building your own Java-based Teams applications. In this article, I’ll show how to make a channel (or group) tab secured with a single sign-on (SSO). However, most of the setup and installation operations for this application are the same as the personal tab application, so you’ll want to review the first article in this series before proceeding.

Microsoft provides a great Teams apps overview. They discuss the difference between personal tab apps and channel tab apps, which is that channel tab apps deliver content to a group of Teams users instead of the single user. For this reason, the application detailed below is an extension of the application in my previous article.

Microsoft also points out that channel tab applications require a configuration page to control what group users can do within the app. They provide an excellent article on creating configuration pages.

The application I’ll build here provides a simple configuration page as an example of communicating with Teams to control group settings. You'll need Java experience to follow along, but I’ll explain how to use Teams.

Implementing SSO in a Teams Application

To implement the single sign-on (SSO) in a Teams application, we first register the application with Azure Active Directory in the same tenant that hosts Teams. The Teams Tab SSO Authentication README page covers this registration process. For additional details, review the "Implementing SSO in a Teams Application" section of the first article.

Creating the Application Using Spring Initializr

After you have completed the application registration, you are ready to build it. Spring provides the Spring Boot Initializr tool, which creates a Maven package that we can import into Eclipse.

Run the tool by opening a browser and going to https://start.spring.io/. Then, fill out the project details and metadata. Include the following dependencies:

  • Spring Web
  • Thymeleaf
  • Azure Active Directory
  • OAuth2 Client
  • Spring Boot DevTools

Click Generate to create a ZIP file that provides the complete Maven infrastructure and the initial runtime class. Unzip the resources into your project directory and import them into Eclipse as an existing Maven project.

In Eclipse, select File > Import to display this dialog:

Select Existing Maven Projects and click Next to display this dialog:

Select the directory to which you unzipped the file downloaded from Spring Initializr, check the box next to your project and click Finish.

After you import the project, enable Bootstrap CSS by adding this dependency to the pom.xml file:

XML
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>bootstrap</artifactId>
    <version>4.0.0-2</version>
</dependency>

We need to make additional changes to the web pages, but I’ll cover that later.

This application’s complete source is available on GitHub.

Preparing the Core Java Code

As in the first article, I removed as much code as possible from the sample application to highlight the code we need to implement SSO. The following Java classes derive from the corresponding C# files.

Java Class File C# Class Source File
AppConfiguration.java Startup.cs
ChannelTabSsoApplication.java Program.cs
HomeController.java HomeController.cs
AuthController.java AuthController.cs
SSOAuthHelper.java SSOAuthHelper.cs

When Spring Boot starts our application, it runs main in the ChannelTabSsoApplication class. This class uses the default class implementation that the Spring Boot starter provides.

The AppConfiguration class configures the Spring Boot engine. We must remember that Teams launches our application in an iframe, and Azure App Service’s web application server enforces cross-frame scripting security. So, we need to add some code to allow our application to run:

Java
package com.contentlab.teams.java.channeltabsso;
 
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 
@Configuration
@Order(value = 0)
public class AppConfiguration extends WebSecurityConfigurerAdapter
{
// @Value("${spring.security.oauth2.resourceserver.jwt.key-value}")
// RSAPublicKey key;
 
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers()
.contentSecurityPolicy(
"frame-ancestors 'self' https://*.microsoftonline.com  https://*.microsoft.com;"
);
}
}

Mozilla states that the content-security-policy header obsoletes the X-FRAME-OPTIONS header, so I’ve implemented the newer method in this application.

The HomeController provides mappings and handlers for the index page, the configure page, and the /Auth/Start and Auth/End URLs that the SSO process needs. I annotate this class with @Controller because the response contains web pages.

The AuthController class provides mapping and a handler for the GetUserAccessToken URL. I annotate this controller with @RestController, which provides a text response to the caller. Code in auth.js fetches the URL when the SSO sequence exchanges the user ID token for the user access token.

The SsoAuthHelper class provides the GetAccessTokenOnBehalfUser, which the AuthController.GetUserAccessToken calls. The GetAccessTokenOnBehalfUser method handles accessing Teams resources on behalf of the authenticated user. This application uses the same process that we covered in detail in the previous article.

Preparing the Web Resources

I implement the client code in three HTML files:

Java Application HTML File C# HTML Source File
index.html Home/index.cshtml
/Auth/Start.html Auth/Start.cshtml
/Auth/End.html Auth/End.cshtml

The index.html web page has a link to auth.js. Auth.js contains a ready event handler that triggers as soon as possible after the file is loaded. The code for the handler is shown below:

JavaScript
$(document).ready(function () {
    microsoftTeams.initialize();
  
    getClientSideToken()
        .then((clientSideToken) => {
            console.log("clientSideToken: " + clientSideToken);
            return getServerSideToken(clientSideToken);
        })
        .catch((error) => {
            console.log(error);
            if (error === "invalid_grant") {
                // Display in-line button so user can consent
                $("#divError").text("Error while exchanging for Server token - invalid_grant - User or admin consent is required.");
                $("#divError").show();
                $("#consent").show();
            }
        });
});

The Microsoft Teams API is initialized and getClientSideToken, which is implemented in auth.js, is called to authenticate the user. If that succeeds, getServerSideToken is called to exchange the client token for a Teams token.

For this application, the page displays information to verify that the user is authenticated and that the application has access to Teams resources. If authentication fails — because the current access token times out, perhaps — it provides a button enabling the user to trigger the authentication process.

The application also has supporting image files: IconRed.png, IconGray.png, and TeamsIcon.png.

Adding the Supporting JavaScript

As in the previous article, I lifted one JavaScript file, auth.js, intact from the Microsoft sample application. I covered the functions in this file that initiate and handle the OAuth 2 flow in detail in the previous article.

Deploying the Application

Now that we have all the application’s parts, we need to build and deploy it. Maven provides the tools to create an Azure App Service plan where we can develop and deploy the application. To use these tools, first, open a command-line window. Next, change the directory (cd) to the project directory containing the pom.xml file. Then, execute these commands:

  • az login --tenant << your tenant ID >>
  • mvn com.microsoft.azure:azure-webapp-maven-plugin:2.2.0:config
  • mvn package azure-webapp:deploy

Where << your tenant ID >> is, the ID Azure Active Directory assigns to your tenant. You can find it on the Overview page of your Default Directory when you open the portal page for the Azure Active Directory service.

These functions, which I covered in detail in the previous article, do the following. First, the az login command logs you into Azure so that Maven can create resources. The first mvn command captures the information to make the Azure App Service plan, and the second mvn command deploys the application to that plan.

Fixing up the App Registration and application.properties File

Now that you have deployed the application, you need to update the information in the Azure Active Directory App Registration portal. There are four parts to this process: capturing the App Service URL, updating the App Registration information, revising the application.properties file, and redeploying the application.

You can capture the new Azure App Service URL from the deployment process’ text response:

[INFO] Successfully deployed the artifact to https://channeltabsso-1634231313958.azurewebsites.net

In the previous article, I pointed out that you need to update the hostname in the Redirect URL and the Application ID URL to match the hostname in the URL from the Azure App Service. Screenshots are available in that article to guide you if you need help.

The application.properties file format is below. You’ll need to replace << Your Tenant ID >> and other placeholders with the IDs you used when registering your application.

# Specifies your Active Directory ID:
azure.activedirectory.tenant-id=<< Your Tenant ID >>
# Specifies your App Registration's Application ID:
azure.activedirectory.client-id=<< application ID >>
# Specifies your App Registration's secret key:
azure.activedirectory.client-secret=<< your app secret >>
azure.auth.url=/oauth2/v2.0/token
azure.instance=https://login.microsoftonline.com
azure.api=api://<< Registered hostname >>/<< your client ID >>
azure.scopes=https://graph.microsoft.com/User.Read
azure.valid.issuers="https://login.microsoftonline.com/<< Your tenant ID >>/v2.0,https://sts.windows.net/<< your tenant ID >>/"
 
spring.thymeleaf.prefix=classpath:/templates/

When you finish replacing these IDs, you can redeploy the application.

Testing in Teams

The manifest file for this application is below. The critical element that makes this a group application instead of a personal application is the scopes object in the configurableTabs object, with the team and groupchat values.

JavaScript
{
    "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.11/MicrosoftTeams.schema.json",
    "manifestVersion": "1.11",
    "version": "1.0.0",
    "id": "<< any random GUID >>",
    "packageName": "com.contentlab.teams.java.channeltabsso",
    "developer": {
    "name": "Microsoft",
    "websiteUrl": "https://www.microsoft.com",
    "privacyUrl": "https://www.microsoft.com/privacy",
    "termsOfUseUrl": "https://www.microsoft.com/termsofuse"
    },
    "name": {
        "short": "Java Group Tab Teams Auth SSO",
        "full": "Java Group Tab Teams Auth SSO"
    },
    "description": {
        "short": "Java Group Tab Teams Auth SSO",
        "full": "Java Group Tab Teams Auth SSO"
    },
    "icons": {
        "color": "color.png",
        "outline": "outline.png"
    },
    "accentColor": "#0000EE",
    "configurableTabs": [
      {
        "configurationUrl": "https://channeltabsso-1634231313958.azurewebsites.net/Configure",
        "canUpdateConfiguration": true,
        "scopes": ["team", "groupchat"]
      }
    ],
    "permissions": ["identity","messageTeamMembers"],
    "validDomains": [
        "channeltabsso-1634231313958.azurewebsites.net",
        "*.onmicrosoft.com",
        "*.azurewebsites.net"
    ],
"webApplicationInfo": {
      "id": "a2f5e45f-c3a7-4ba6-8e14-543e45e1be95",
      "resource": "api://channeltabsso-1634231313958.azurewebsites.net/<< your client id >>"
  }
}

Build the manifest.zip file as I described in the first article. Then upload the file to Teams. When you add the application to Teams, you’ll see this:

Select a channel and click Set up a tab.

You may need to wait for the Azure App Service to start. Look for a status message in the lower-left corner:

When the application loads, you’ll see the Configure tab:

This sample application just shows the authentication data. You can click the red and gray buttons to change the configuration. After you select a color, the application enables the Save button. Since this is just a demonstration application, the button event handlers are just placeholders that exist to show how to enable the Save button and post a notification to Teams that the configuration has been saved.

JavaScript
    <script>
    microsoftTeams.initialize();
    let saveGray = () => {
        microsoftTeams.settings.registerOnSaveHandler((saveEvent) => {
            microsoftTeams.settings.setSettings({
                websiteUrl: window.location.origin ,
                contentUrl: window.location.origin + "",
                entityId: "Java Channel Configure",
                suggestedDisplayName: "Java Channel Configure - Grey Settings"
            });
            saveEvent.notifySuccess();
        });
    }
    let saveRed = () => {
        microsoftTeams.settings.registerOnSaveHandler((saveEvent) => {
            microsoftTeams.settings.setSettings({
                websiteUrl: window.location.origin ,
                contentUrl: window.location.origin + "",
                entityId: "Java Channel Configure",
                suggestedDisplayName: "Java 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>

Your application will need to present choices relevant to the types of interactions your application supports. This might include selecting a conversion category, setting the conversation title, or, if you implement an ad-hoc approval workflow, naming the approvers and specifying the approval order.

When you click the Save button, the application’s main page is presented. What your application displays here will depend on your application and the captured configuration data.

Next Steps

You now have the shell for a working channel tab application. I’ve covered all the details of integrating your application with Teams and securing it with SSO. You need to add the configuration details and the group application, but that’s the standard practice for building web-based single-page applications that you already know.

As you experienced, converting a Teams application from C# to Java takes minimal effort. I just changed one function in SsoAuthHelper. The rest of the HTML and JavaScript that SSO requires carries over directly. Your Java skills are relevant, and you can continue to use your familiar tools to create Teams apps.

This article has also provided a process for converting Microsoft’s C# samples. So, you can browse through the library and find other examples to give you a starting shell.

Continue to the final article of this series to explore creating a conversational tab app.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here