Consuming Google drive API from MS Teams App

It’s not uncommon for companies to have multi cloud setup and use both M365 and GSuite. MS Teams supports a data provider for Google drive but, the search experience there is quite poor. In such setup it can bring a great value to implement search across both M365 and Google Drive indexes and expose it as a personal app in MS Teams.

As usual, the biggest issue is authentication. In web browser we can use Google SDK to handle token acquisition but in teams it will not work because https://accounts.google.com/o/oauth2/v2/auth cannot be embedded in iFrame and window.open will open new browser window and we will have no control over it in Teams Desktop or Teams Mobile.

Preparations

Before we start to integrate anything, we need to go through app registration in Google. To do that navigate to Google cloud platform. Create an app there and add oAuth2 client Id. For developement purposes add localhost:3000 to both Authorized JavaScript origins and Authorized redirect URIs. If You are using different port than 3000, update accordingly.

With this app configured we can continue to implementation.

Implementation

We want to open a window using authentication namespace in teams SDK, use that window to login, redirect back (still in auth window) to our app and finally call notifySuccess to send the token to the main window.

First let’s build the url to google auth service:

1	const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${this.clientId}&redirect_uri=${replyUrl}&response_type=token&scope=${this.scope}&include_granted_scopes=true&state=pass-through value`;

Where clientId is client id we generated in Google cloud platform, scope can be found here and for reply url I use window.location.origin.

Once our url is build let’s use teams SDK:

this.token = await new Promise<string>((resolve, reject) => {
    microsoftTeams.authentication.authenticate({
        url: authUrl
    }).then(token => {
        this.token = token;
        resolve(token);
    }).catch(err => {
        resolve(localStorage.getItem("googleAccessToken") || "");
    });
});

You may find it confusing, that we are trying to get the token from localStorage in catch clause. This is a work around for known teams SKD issue which sometimes returns CancelledByUser exception instead of successful response.

This piece of code will only handle opening and closing our authentication window. We still need to handle the redirection in that window. As we will be redirected back to our app we need to look for the token in query string parameter somewhere during page lifecycle. Personally, I recommend adding the scanning for query parameter somewhere in index.ts in root folder, right before rendering the main component. If You are using SPFx, do it in onInit web part method.

Our redirect handler can look somehow like this:

export class oAuthHandlerService {
    public static async handleOAuthResponse(): Promise<void> {
        const url = new URL(window.location.href.replace("#state=pass-through%20value", ""));
        const params = new URLSearchParams(url.search);
        const token = params.get("access_token");
        if (token) {
            localStorage.setItem("googleAccessToken", token);
            try {
                await teamsjs.app.initialize();
                teamsjs.authentication.notifySuccess(token);
            }
            catch (err) {
 
            }
            window.location.href = window.location.origin;
        }
    }

Another surprise here. You may wonder why we replace hash value. If You will redirect to a page with a query parameter (https://localhost:3000?app=google for example), google with redirect You back with hash, this can break the constuctor of URLSearchParams making parameters appearing in url after hash empty. You can also notice we are setting item in localStorage, this is the second part of the work around I mentioned before.

Testing

Before You run this solution in teams, remeber to add all required validDomains to teams app manifest (accounts.google.com and your localhost in particular) and bear in mind teams doesn’t support http protocol (only https) so You need to host Your app in secure place before running it in teams. If You want to use ngrok for tunneling, remember to add the tunneling url to google app registration as well.

Thanks for reading, have a lovely day.

SPFx Web Part sample is available here

This blog is a part of Teams Week!

About the Author

Coding enthusiast, TDD evangelist, OOP champion and SOLID proponent. Likes to code as well as talking about the code.
For fun, besides software development, plays guitar, basketball and video games. Enjoys cooking and learning.

Reference

Wojciechowski, M., 2022, Consuming Google drive API from MS Teams App, Available at: https://mgwdevcom.wordpress.com/2022/12/07/consuming-google-drive-api-from-ms-teams-app/ [Accessed on 16 March 2023]

Share this on...

Rate this Post:

Share: