Why SSO Matters in Teams Apps
A Teams app runs inside an iframe or webview hosted by the Teams client, and without SSO the user would have to complete a separate sign-in popup the first time they open any tab, bot, or personal app. Teams SSO lets the app silently obtain an identity token for the already-signed-in Teams user through the Teams JavaScript SDK's authentication.getAuthToken (v1) or app.authentication.getAuthToken (v2) call, avoiding an extra consent screen for basic profile access and making the app feel native rather than bolted on.
Cricket analogy: This is like a player who's already been through ground-entry security for the stadium not needing to show ID again to walk from the dressing room to the nets — Teams SSO reuses the same trusted session instead of re-checking credentials at every door.
The getAuthToken Flow and On-Behalf-Of Exchange
When a tab calls app.authentication.getAuthToken(), the Teams client returns a short-lived Entra ID token scoped to the app's own Azure AD app registration — this token proves who the user is but typically cannot call Graph directly. The app's backend exchanges this token for a Graph-scoped access token using the OAuth 2.0 On-Behalf-Of (OBO) flow: it sends the original token, its client ID, and client secret to Entra ID's token endpoint and receives back a new token with the permissions the backend actually needs, such as Mail.Read or Chat.ReadWrite.
Cricket analogy: This is like a fielder passing the ball to the wicketkeeper who then relays it to the bowler for the run-out appeal — the initial token is the first throw, and the OBO exchange is the relay that gets it into the form actually needed.
// Frontend: request an SSO token inside the Teams tab
import { app, authentication } from "@microsoft/teams-js";
await app.initialize();
const clientToken = await authentication.getAuthToken();
// Send it to your backend for the OBO exchange
const res = await fetch("/api/graph/profile", {
headers: { Authorization: `Bearer ${clientToken}` }
});
// Backend (Node.js, using msal-node ConfidentialClientApplication)
const oboRequest = {
oboAssertion: clientToken,
scopes: ["https://graph.microsoft.com/User.Read"]
};
const oboResponse = await cca.acquireTokenOnBehalfOf(oboRequest);
// oboResponse.accessToken can now call Graph directlyConfiguring the App Registration and Manifest
SSO requires the Teams app manifest to declare a webApplicationInfo block containing the Entra ID application (client) ID and a resource URI matching api://{fully-qualified-domain}/{client-id}. The Entra ID app registration must expose an API with that same identifier URI, define a scope such as access_as_user, and pre-authorize the Teams client's own well-known application IDs so users aren't prompted with an unexpected consent screen the first time SSO fires.
Cricket analogy: This is like a ground's accreditation office pre-registering the broadcaster's specific camera crew IDs so they walk through the media gate without a manual check every match day — the manifest's webApplicationInfo is that pre-registration.
The identifier URI in your Entra ID app registration must exactly match the resource field in the manifest's webApplicationInfo, including the api:// scheme and domain — a mismatch is the single most common cause of SSO silently failing in production.
getAuthToken can fail with error 'resourceRequiresConsent' or similar when a user hasn't yet granted consent, especially the very first time or after scope changes. Always have a fallback path that opens a standard OAuth popup via authentication.authenticate() so the app degrades gracefully instead of breaking.
- Teams SSO uses app.authentication.getAuthToken() to silently obtain a client-scoped Entra ID token for the signed-in user.
- The On-Behalf-Of (OAuth 2.0) flow exchanges that client token for a Graph-scoped access token on the backend.
- The app manifest's webApplicationInfo block must declare the Entra ID client ID and a matching api:// resource URI.
- The Entra ID app registration exposes an API scope (commonly access_as_user) and pre-authorizes Teams' known client IDs.
- A mismatched identifier URI between the manifest and app registration is a top cause of SSO failures.
- Always implement a fallback OAuth popup for cases where silent SSO fails or consent hasn't been granted.
- SSO removes the friction of a separate login popup, making Teams apps feel native rather than bolted on.
Practice what you learned
1. What does app.authentication.getAuthToken() return in a Teams SSO flow?
2. Which OAuth 2.0 flow lets a backend exchange a client-scoped token for a Graph-scoped one?
3. Which manifest field must match the Entra ID app registration's identifier URI for SSO to work?
4. What should an app do if getAuthToken fails due to missing consent?
5. What scope is commonly defined on the Entra ID app registration to support Teams SSO?
Was this page helpful?
You May Also Like
Microsoft Graph API for Teams
Learn how to read and write Teams data — teams, channels, messages, and members — through the unified Microsoft Graph REST API.
Webhooks and Connectors
Learn how incoming webhooks, Office 365 Connectors, and Graph change notifications push external events into Teams channels.
Teams Meeting Apps
Build apps that extend Teams meetings with in-meeting tabs, real-time events, and meeting-stage experiences using the Teams JS SDK.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesMFC (Microsoft Foundation Classes) Study Notes
C++ · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics