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, nonUser.Read.Allapplicativo. Richiedere.Allviola 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.