Microsoft Teams SSO: risolvere l’errore “App resource defined in manifest and iframe origin do not match” (causa, fix e best practice)

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.

Indice

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.
CosaEsempioDove si imposta
Resource URI (manifest)api://contoso.onmicrosoft.com/12345678-90ab-cdef-1234-567890abcdefmanifest.jsonwebApplicationInfo.resource
Application ID URI (Entra ID)api://contoso.onmicrosoft.com/12345678-90ab-cdef-1234-567890abcdefMicrosoft Entra ID → App registrations → Expose an API
Iframe originhttps://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)

  1. 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]).
  2. Il codice client chiede a Teams un token SSO (Teams/TeamsFx SDK), indicando implicitamente la resource dal manifest.
  3. 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>).
  4. 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://&lt;dominio-completo&gt;/&lt;AppID&gt;

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:

AmbienteFQDN (iframe origin)Application ID URIResource nel manifestRedirect URI
Localehttps://localhost:53000api://localhost/12345678-90ab-cdef-1234-567890abcdefUguale all’Application ID URIhttps://localhost:53000
Devhttps://dev.contoso.comapi://dev.contoso.com/12345678-90ab-cdef-1234-567890abcdefUguale all’Application ID URIhttps://dev.contoso.com
Prodhttps://app.contoso.comapi://app.contoso.com/12345678-90ab-cdef-1234-567890abcdefUguale all’Application ID URIhttps://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 alla resource 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 rapidoSpiegazione
CachingTeams memorizza il manifest; dopo modifiche critiche, rimuovi l’app e riavvia Teams.
Tenant multipliSe provi l’app in più tenant, registra un Application ID URI diverso per ciascuno.
Ambienti paralleliMantieni 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

  1. Imposta in Entra ID: api://<dominio>/<AppID>.
  2. Aggiorna il manifest: webApplicationInfo.id e webApplicationInfo.resource.
  3. Allinea i Redirect URI per ogni ambiente.
  4. 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.

Indice