Il modello OAuth2 in Microsoft Entra ID

In AZ-204 l’autenticazione delle applicazioni ruota attorno a Microsoft Entra ID come identity provider e a MSAL (Microsoft Authentication Library) come libreria client. OAuth2 non serve a verificare l’identità (quello è OpenID Connect), ma a delegare l’autorizzazione: un’app ottiene un access token con cui chiamare una risorsa protetta come Microsoft Graph. La domanda chiave dell’esame è sempre la stessa: quale flusso (grant) usare in questo scenario, e con quali permessi?

La risposta dipende da due variabili: c’è un utente interattivo? e chi è il “soggetto” del token (l’utente o l’applicazione stessa).

I tre flussi da conoscere

Authorization code flow (con utente)

È il flusso da usare quando un utente reale interagisce con l’app: web app server-side, SPA, app mobile/desktop. L’utente si autentica, dà consenso, e Entra ID restituisce un authorization code che l’app scambia per un access token + refresh token.

  • Per SPA e app pubbliche va abbinato a PKCE (Proof Key for Code Exchange): non esiste un client secret custodibile in sicurezza nel browser.
  • Il token porta con sé il contesto utente: le chiamate a Graph rispetteranno i permessi di quell’utente.

Client credentials flow (senza utente)

È il flusso per scenari daemon / service-to-service: job in background, timer-triggered Azure Function, servizio che gira di notte senza nessuno loggato. L’app si autentica con le proprie credenziali — un client secret o, meglio in produzione, un certificato (o una managed identity su Azure, che elimina del tutto i segreti). Il token rappresenta l’applicazione stessa, non un utente.

On-behalf-of (OBO) flow (API intermedie)

Serve quando una Web API riceve un token utente e deve chiamare un’altra API a valle (tipicamente Microsoft Graph) mantenendo l’identità dell’utente originale. L’API “scambia” il token in ingresso con un nuovo token per il servizio downstream. È la risposta corretta ogni volta che nello scenario compare una catena di API (client → API1 → API2) e serve preservare chi è l’utente.

Permessi delegati vs applicativi

Questa distinzione è il cuore del dominio:

  • Permessi delegati (Delegated): l’app agisce per conto di un utente. Il permesso effettivo è l’intersezione tra ciò che l’app ha richiesto e ciò che l’utente può effettivamente fare. Usati con authorization code e OBO. Esempio: User.Read, Mail.Send.
  • Permessi applicativi (Application): l’app agisce come se stessa, senza utente, spesso a livello di tutta la tenant. Usati con client credentials. Esempio: User.Read.All, Mail.Read.

I permessi applicativi richiedono sempre admin consent e sono potenti: applicano least privilege con attenzione, perché un Application a livello tenant può leggere/scrivere dati di tutti gli utenti.

Consenso e richiesta degli scope

Il modello di consenso determina chi autorizza:

  • User consent: l’utente approva permessi delegati a basso privilegio.
  • Admin consent: obbligatorio per i permessi applicativi e per i permessi delegati “high-privilege”. Si concede una volta a livello di tenant (portale Entra o endpoint /adminconsent).

Per Graph, gli scope si richiedono con la forma https://graph.microsoft.com/User.Read. Attenzione alla differenza tra i due flussi:

  • Delegati (auth code / OBO): passi gli scope specifici nella richiesta (scopes: ["User.Read", "Mail.Send"]).
  • Applicativi (client credentials): richiedi lo scope .default (https://graph.microsoft.com/.default), che significa “tutti i permessi applicativi già consentiti per questa app”. Non si elencano i singoli permessi runtime.
Delegato  → scope = "User.Read"                → token con contesto utente
Applicativo → scope = "https://graph.microsoft.com/.default" → token app

Trappole tipiche d’esame

  • Daemon/Function schedulata senza utente loggato → client credentials + permessi applicativi + .default. Chi risponde “authorization code” sbaglia: non c’è nessun utente che possa fare login.
  • Web API che chiama Graph mantenendo l’identità del chiamante → on-behalf-of. Se scegli client credentials perdi il contesto utente e ottieni permessi errati (o eccessivi).
  • App che deve leggere solo il profilo dell’utente corrente → permesso delegato User.Read, non User.Read.All applicativo. Richiedere .All viola il least privilege e richiede admin consent inutilmente.
  • “Errore di consenso / need admin approval” su un permesso applicativo → serve admin consent a livello di tenant, non un semplice user consent.
  • SPA/browser che chiede un client secret → sbagliato: usa authorization code con PKCE. Il secret non è custodibile lato client. Su Azure, preferisci managed identity al posto dei secret ovunque possibile.