Se la tua scheda di Microsoft Teams (React + Fluent UI + @microsoft/teamsfx) fallisce l’SSO con l’errore “App resource defined in manifest and iframe origin do not match”, il problema è quasi sempre una mancata corrispondenza tra dominio dell’iframe e Application ID URI. Ecco come risolvere in modo definitivo.
Panoramica del problema
ErrorWithCode.InternalError: Get SSO token failed with error:
App resource defined in manifest and iframe origin do not match
Questo errore indica che, durante l’hand‑off SSO, Teams ha confrontato tre elementi che devono combaciare perfettamente e ha riscontrato una discrepanza:
- Resource URI nel manifest della Teams app (
webApplicationInfo.resource
). - Application ID URI registrato nell’app in Microsoft Entra ID (ex Azure AD) nella sezione Expose an API.
- Origin (protocollo+dominio+porta) della pagina che gira nell’iframe di Teams.
Cosa | Esempio | Dove si imposta |
---|---|---|
Resource URI (manifest) | api://contoso.onmicrosoft.com/12345678-90ab-cdef-1234-567890abcdef | manifest.json → webApplicationInfo.resource |
Application ID URI (Entra ID) | api://contoso.onmicrosoft.com/12345678-90ab-cdef-1234-567890abcdef | Microsoft Entra ID → App registrations → Expose an API |
Iframe origin | https://contoso.onmicrosoft.com (o il tuo FQDN) | È l’URL reale che carica la scheda dentro Teams |
Come funziona l’SSO nelle schede Teams (in breve)
- La scheda viene caricata in un iframe all’interno del client Teams (web o desktop). L’origin dell’iframe è quello del tuo host (
https://<dominio>[:porta]
). - Il codice client chiede a Teams un token SSO (Teams/TeamsFx SDK), indicando implicitamente la resource dal manifest.
- Teams verifica che la resource del manifest coincida con l’Application ID URI di Entra ID e che il dominio dell’iframe sia lo stesso dominio presente nell’URI (
api://<dominio>/<AppID>
). - Se tutto combacia, rilascia il token. In caso contrario, solleva l’errore in oggetto.
Soluzione operativa passo‑passo
Qui sotto trovi il percorso più sicuro per eliminare l’errore e rimettere in linea l’SSO:
Imposta un Application ID URI “fully qualified” in Entra ID
Nel portale di Microsoft Entra ID, apri l’applicazione → Expose an API, e imposta (o aggiungi) un Application ID URI nel formato:
api://<dominio-completo>/<AppID>
Esempio:
api://contoso.onmicrosoft.com/12345678-90ab-cdef-1234-567890abcdef
- Usa un FQDN realmente raggiungibile dalla tua scheda in Teams (es. dominio aziendale, dev‑tunnel, sottodominio pubblico).
- Evita percorsi aggiuntivi, querystring o porte: l’Application ID URI non le supporta. Il controllo avviene sul dominio.
- Per ambienti differenti (dev/test/prod) prevedi un Application ID URI per ciascun FQDN che utilizzi (vedi tabella “Strategia multi‑ambiente”).
Allinea il manifest dell’app Teams
Nel manifest.json
della tua app, aggiorna il blocco webApplicationInfo
in modo che id
sia il Client ID dell’app Entra ID e resource
corrisponda esattamente all’Application ID URI definito sopra.
{
"webApplicationInfo": {
"id": "12345678-90ab-cdef-1234-567890abcdef",
"resource": "api://contoso.onmicrosoft.com/12345678-90ab-cdef-1234-567890abcdef"
}
}
Nota: se usi template o pipeline che generano il manifest (Teams Toolkit/TeamsFx), assicurati che i valori per ambiente siano sostituiti correttamente.
Verifica gli URI di reindirizzamento
In Authentication dell’app Entra ID, aggiungi tutti gli Redirect URI utilizzati dalla tua app, ad esempio:
https://localhost:53000
(debug locale)https://dev.contoso.com
(ambiente dev)https://app.contoso.com
(produzione)
I domini devono appartenere allo stesso dominio logico che hai usato nell’Application ID URI relativo a quell’ambiente (vedi la sezione multi‑ambiente). Non mischiare localhost
con un Application ID URI basato su contoso.com
senza prevedere una variante dedicata per il locale.
Rigenera e reinstalla il pacchetto Teams
- Disinstalla la versione precedente della tua app dal client Teams (caching del manifest).
- Rigenera il pacchetto manifest (ZIP) e fai nuovamente sideload o pubblica la nuova versione.
- Riavvia il client Teams (desktop e/o web) per evitare cache residue.
Perché questa correzione funziona
Teams emette il token SSO solo quando i seguenti tre elementi hanno lo stesso dominio:
- Resource nel manifest (
api://<dominio>/<AppID>
) - Application ID URI di Entra ID
- Origin dell’iframe (l’host che serve la tua scheda)
Qualsiasi differenza (anche minima) fa fallire l’hand‑off. Uniformando il dominio completo (fully qualified domain name) in tutti i punti, la discrepanza scompare e il token viene emesso correttamente.
Strategia multi‑ambiente e multi‑tenant
Se lavori con ambienti paralleli o più tenant, pianifica fin da subito la codifica degli URI:
Ambiente | FQDN (iframe origin) | Application ID URI | Resource nel manifest | Redirect URI |
---|---|---|---|---|
Locale | https://localhost:53000 | api://localhost/12345678-90ab-cdef-1234-567890abcdef | Uguale all’Application ID URI | https://localhost:53000 |
Dev | https://dev.contoso.com | api://dev.contoso.com/12345678-90ab-cdef-1234-567890abcdef | Uguale all’Application ID URI | https://dev.contoso.com |
Prod | https://app.contoso.com | api://app.contoso.com/12345678-90ab-cdef-1234-567890abcdef | Uguale all’Application ID URI | https://app.contoso.com |
Multi‑tenant: in ciascun tenant l’app può (e in molti casi deve) avere un Application ID URI coerente con il dominio usato in quel contesto. Tieni traccia degli URI per tenant ed evita collisioni tra domini simili.
Esempi di codice utili
Recupero del token SSO con TeamsFx
import { TeamsFx, createMicrosoftGraphClient } from "@microsoft/teamsfx";
export async function getGraphClient(): Promise {
const teamsfx = new TeamsFx();
// Opzionale: garantisci che l’utente sia loggato
await teamsfx.login(["User.Read"]); // ambiti Graph minimi
// Ottieni un client Graph autenticato con SSO
const graph = await createMicrosoftGraphClient(teamsfx, ["User.Read"]);
return graph;
}
export async function loadProfile() {
const graph = await getGraphClient();
const me = await graph.api("/me").get();
console.log("User:", me.displayName);
}
Se la risorsa/URI non combacia con l’origin, il metodo interno che ottiene il token genererà l’errore visto sopra.
Recupero esplicito del token con Microsoft Teams JS SDK
import * as microsoftTeams from "@microsoft/teams-js";
export async function getSsoTokenRaw(): Promise {
await microsoftTeams.app.initialize();
return new Promise((resolve, reject) => {
microsoftTeams.authentication.getAuthToken({
successCallback: resolve,
failureCallback: (error) => reject(new Error(error))
});
});
}
Questo token è emesso per la resource dichiarata nel manifest: se i domini non coincidono, non verrà emesso.
Validazione automatica del manifest (script di check)
const fs = require("fs");
const url = require("url");
function validateResource(manifestPath, expectedHost) {
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
const resource = manifest.webApplicationInfo?.resource;
if (!resource) throw new Error("webApplicationInfo.resource mancante");
const parsed = new url.URL(resource.replace("api://", "https://"));
if (parsed.host !== expectedHost) {
throw new Error(
`Dominio resource non valido. Atteso: ${expectedHost}, trovato: ${parsed.host}`
);
}
console.log("OK: resource combacia con l'host previsto");
}
// Esempio: validateResource("./appPackage/manifest.json", "dev.contoso.com");
Diagnostica rapida
- Controlla i domini: il dominio nel tuo
Application ID URI
deve essere identico all’host della scheda (senza porta) e identico allaresource
nel manifest. - Verifica il manifest installato: quello in Teams potrebbe essere una versione cache. Disinstalla, reinstalla e riavvia.
- Desktop vs Web: prova su Teams web e desktop. A volte l’una delle due cache differisce.
- Dev‑tunnel: se usi ngrok/DevTunnel, aggiorna Application ID URI, manifest e redirect URIs con il nuovo sottodominio.
- Porte: l’origin dell’iframe include la porta, ma l’Application ID URI non la include; il controllo avviene sul dominio. Non aggiungere
:53000
nell’Application ID URI. - Tenant: assicurati di usare la registrazione applicativa giusta nel tenant in cui stai testando.
Domande frequenti
Posso usare api://<AppID>
senza dominio?
No. Per le schede Teams con SSO, il formato richiesto è api://<dominio>/<AppID>
. L’errore in oggetto nasce proprio quando il dominio non coincide con l’origin dell’iframe.
Posso avere più Application ID URI?
Sì, quando lavori con più ambienti/tenant è normale definire più URI compatibili con i rispettivi domini. Ricordati di allineare anche il manifest.json
per ogni ambiente.
Serve inserire le porte negli URI?
No. Le porte appartengono agli Redirect URI (Authentication), non all’Application ID URI. Nel controllo di corrispondenza, il dominio deve combaciare; la porta non si indica nell’Application ID URI.
Ho aggiornato tutto ma l’errore persiste. Cosa controllo ancora?
- Cancella e reinstalla l’app nel client Teams; chiudi e riapri Teams.
- Controlla di non aver due pacchetti con lo stesso developer/manifest ID installati.
- Apri gli strumenti sviluppatore e ispeziona l’origin effettiva della scheda.
- Verifica che il Client ID (
webApplicationInfo.id
) corrisponda all’app registrata corretta.
Checklist finale
- Expose an API → Application ID URI in Entra ID:
api://<dominio>/<AppID>
. - Manifest:
webApplicationInfo.id
= Client ID;webApplicationInfo.resource
= Application ID URI identico. - Redirect URIs: aggiunti per ogni ambiente (incluso
https://localhost:<porta>
). - Re‑pack & sideload: pacchetto rigenerato, app precedente disinstallata, Teams riavviato.
- Verifica: la scheda recupera il token SSO e può chiamare Microsoft Graph.
Esempio completo in manifest e configurazione
Ecco un estratto tipico del manifest.json
per produzione:
{
"$schema": "https://developer.microsoft.com/json-schemas/teams/v1.16/MicrosoftTeams.schema.json",
"manifestVersion": "1.16",
"version": "1.0.3",
"id": "a1b2c3d4-e5f6-7890-abcd-ef0123456789",
"packageName": "com.contoso.teams.ssoapp",
"developer": {
"name": "Contoso",
"websiteUrl": "https://app.contoso.com",
"privacyUrl": "https://app.contoso.com/privacy",
"termsOfUseUrl": "https://app.contoso.com/terms"
},
"name": { "short": "Contoso SSO Tab" },
"description": { "short": "Tab SSO con Microsoft Graph", "full": "..." },
"accentColor": "#FFFFFF",
"staticTabs": [
{
"entityId": "home",
"name": "Home",
"contentUrl": "https://app.contoso.com/tab",
"websiteUrl": "https://app.contoso.com",
"scopes": ["personal"]
}
],
"webApplicationInfo": {
"id": "12345678-90ab-cdef-1234-567890abcdef",
"resource": "api://app.contoso.com/12345678-90ab-cdef-1234-567890abcdef"
}
}
Suggerimenti aggiuntivi (da tenere a portata di mano)
Controllo rapido | Spiegazione |
---|---|
Caching | Teams memorizza il manifest; dopo modifiche critiche, rimuovi l’app e riavvia Teams. |
Tenant multipli | Se provi l’app in più tenant, registra un Application ID URI diverso per ciascuno. |
Ambienti paralleli | Mantieni nomi coerenti (dev , test , prod ) nell’URI per evitare conflitti. |
Conclusioni
L’errore “App resource defined in manifest and iframe origin do not match” è un classico problema di coerenza tra configurazioni in manifest, Entra ID e hosting. La soluzione consiste nel definire un Application ID URI “fully qualified” del tipo api://<dominio>/<AppID>
, riportarlo identico in webApplicationInfo.resource
e verificare che l’origin della scheda sia servita dallo stesso dominio. Una volta riallineati questi tre tasselli, il token SSO viene emesso e la tua app può invocare Microsoft Graph (o altre API) senza ulteriori errori.
Riepilogo operativo
- Imposta in Entra ID:
api://<dominio>/<AppID>
. - Aggiorna il manifest:
webApplicationInfo.id
ewebApplicationInfo.resource
. - Allinea i Redirect URI per ogni ambiente.
- Rigenera il pacchetto e reinstalla la Teams app.
Con questo assetto, l’SSO torna a funzionare in modo affidabile in tutti gli ambienti.
In sintesi: usa sempre lo stesso dominio completo in Application ID URI, resource del manifest e origin della scheda. È il singolo accorgimento che risolve la quasi totalità dei casi.