Authentication in MS Teams tab on mobile

Viewed 398

I've stuck with a problem of microsoftTeams.authentication.authenticate() on mobile devices. By calling the function, a new tab is opened in my browser window and not within Teams itself (on desktop devices it is opened as a popup and all works fine). Thus, the app is opened in the browser environment and auth process is broken. Is there any possibility to get me back into Teams after authorization has succeeded? I'm pretty confused. Any help would be great! Thank you!

async teamsLogin () {
  return new Promise((resolve, reject) => {
    microsoftTeams.initialize(() => {
      microsoftTeams.authentication.authenticate({
        url: process.env.VUE_APP_ORIGIN + '/auth/teams-auth',
        width: 600,
        height: 535,
        successCallback: res => this.afterLoginProcess(res.accessToken, res.expiresIn),
        failureCallback: e => {
          console.log('failure callback', e)
          reject(e)
        }
      })
    })
})

. TeamsAuth.vue

mounted () {
  microsoftTeams.initialize(() => {
    microsoftTeams.getContext(function (context: any) {
      const state = _guid()
      localStorage.setItem('simple.state', state)
      localStorage.removeItem('simple.error')
      // Go to the Azure AD authorization endpoint
      const queryParams = {
        client_id: process.env.VUE_APP_CLIENT_ID,
        response_type: 'token',
        response_mode: 'fragment',
        scope: `api://${process.env.VUE_APP_WEB_API_CLIENT_ID}/access_as_user`,
        redirect_uri: process.env.VUE_APP_ORIGIN + '/auth/teams-auth-end',
        nonce: _guid(),
        state: state,
        login_hint: context.loginHint
      }

    const authorizeEndpoint = `https://login.microsoftonline.com/${context.tid}/oauth2/v2.0/authorize?${toQueryString(queryParams)}`
    window.location.assign(authorizeEndpoint)
  })
})}

. TeamsAuthEnd.vue

  mounted () {
microsoftTeams.initialize(() => {
  const hashParams = window.location.hash.split('#/')[1]
    .split('&')
    .map(p => p.split('='))
    .reduce((acc, [k, v]) => {
      acc[k.replace('/', '')] = v
      return acc
    }, {} as any)

  if (hashParams.error) {
    microsoftTeams.authentication.notifyFailure(hashParams.error)
  } else if (hashParams.access_token) {
    const expectedState = localStorage.getItem('simple.state')
    if (expectedState !== hashParams.state) {
      microsoftTeams.authentication.notifyFailure('StateDoesNotMatch')
    } else {
      // Success: return token information to the tab
      microsoftTeams.authentication.notifySuccess({
        idToken: hashParams.id_token,
        accessToken: hashParams.access_token,
        tokenType: hashParams.token_type,
        expiresIn: hashParams.expires_in
      } as any)
    }
  } else {
    // Unexpected condition: hash does not contain error or access_token parameter
    microsoftTeams.authentication.notifyFailure({
      reason: 'UnexpectedFailure',
      str: window.location.hash,
      data: hashParams
    } as any as string)
  }
  window.close()
})

}

3 Answers

I'm also looking more into this for one of my apps, and I think there might even be cases when it loads it inside Teams and times when it loads it in a standalone web page. For example, in this doc it says:

The default behavior is currently to use your websiteUrl to launch your tab in a browser window. However, they can be loaded on a mobile client by clicking the ... overflow menu next to the tab and choosing Open, which will use your contentUrl to load the tab inside the Teams mobile client.

And I think that might differ based on the type of client too (mobile phone vs. tablet). For instance, I've tested the same app (which has ContentUrl set but websiteUrl NOT set) on an Android tablet, which shows fine, and on an Android phone, and it's not available). As per this blog (Bob German is from Microsoft and works closely with Teams):

This is all well and good for the Teams desktop and web clients, but the mobile clients currently don’t support the Teams pop-up. In the mobile clients, the tab is just a regular web page; hence the MSAL calls can be done directly. The sample handles this by providing a second version of the tab that calls MSAL directly.

You can see how this works in the app.js component; it first checks for a redirect and then to see if it’s in an IFrame or not. If it is in an IFrame, it renders Tab.js, which uses the Teams popup; if not, it renders Web.js, which does not.

I'm still looking into this myself, but maybe it can help you in the meantime.

In my experience, it depends on what kind of login methods you support. If you also plan to use some SSO where you need to redirect to IPs page, you will end up using 2 different flows for mobile clients and desktop. The problem is that the Teams tab implementation is different in terms of being rendered in an iFrame. The mobile clients use native views while desktop web app and native clients use iFrame to render your Tab's content. Showing your login page in an iFrame is not very secure and IMO this should be avoided. This is why you should do a redirect flow on native mobile apps (tab -> login page -> tab) and a popup flow on desktop (web client, native client and detached Tab view in standard browser).

Thank you very much guys for your replies! We will probably authorize users using MS services on backend to avoid any login popups or redirects :)

Related