L'oggetto applicazione client è stato compilato. A questo momento, è possibile usarlo per acquisire un token per chiamare un'API Web. In ASP.NET o ASP.NET Core, la chiamata a un'API Web viene eseguita nel controller:
Microsoft.Identity.Web aggiunge metodi di estensione che forniscono servizi pratici per chiamare Microsoft Graph o un'API Web downstream. Questi metodi sono illustrati in dettaglio in Un'app Web che chiama LE API Web: Chiamare un'API. Con questi metodi helper non è necessario acquisire manualmente un token.
Se, tuttavia, si vuole acquisire manualmente un token, il codice seguente mostra un esempio di utilizzo di Microsoft.Identity.Web per farlo in un controller home. Chiama Microsoft Graph usando l'API REST (anziché Microsoft Graph SDK). In genere, non è necessario ottenere un token, è necessario compilare un'intestazione di autorizzazione aggiunta alla richiesta. Per ottenere un'intestazione di autorizzazione, inserire il IAuthorizationHeaderProvider
servizio tramite l'inserimento delle dipendenze nel costruttore del controller (o il costruttore di pagina se si usa Blazor) e lo si usa nelle azioni del controller. Questa interfaccia include metodi che producono una stringa contenente il protocollo (Bearer, Pop, ...) e un token. Per ottenere un'intestazione di autorizzazione per chiamare un'API per conto dell'utente, usare (CreateAuthorizationHeaderForUserAsync
). Per ottenere un'intestazione di autorizzazione per chiamare un'API downstream per conto dell'applicazione stessa, usare (CreateAuthorizationHeaderForAppAsync
in uno scenario daemon).
I metodi controller sono protetti da un [Authorize]
attributo che garantisce che solo gli utenti autenticati possano usare l'app Web.
[Authorize]
public class HomeController : Controller
{
readonly IAuthorizationHeaderProvider authorizationHeaderProvider;
public HomeController(IAuthorizationHeaderProvider authorizationHeaderProvider)
{
this.authorizationHeaderProvider = authorizationHeaderProvider;
}
// Code for the controller actions (see code below)
}
ASP.NET Core rende IAuthorizationHeaderProvider
disponibile l'inserimento delle dipendenze.
Ecco il codice semplificato per l'azione di HomeController
, che ottiene un token per chiamare Microsoft Graph:
[AuthorizeForScopes(Scopes = new[] { "user.read" })]
public async Task<IActionResult> Profile()
{
// Acquire the access token.
string[] scopes = new string[]{"user.read"};
string accessToken = await authorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync(scopes);
// Use the access token to call a protected web API.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", accessToken);
string json = await client.GetStringAsync(url);
}
Per comprendere meglio il codice necessario per questo scenario, vedere il passaggio fase 2 (2-1-Web app Calls Microsoft Graph) dell'esercitazione ms-identity-aspnetcore-webapp-tutorial .
L'attributo AuthorizeForScopes
sopra l'azione del controller (o della pagina Razor se si usa un modello Razor) viene fornito da Microsoft.Identity.Web. Garantisce che all'utente venga richiesto il consenso, se necessario, e in modo incrementale.
Esistono altre varianti complesse, ad esempio:
- Chiamata di diverse API.
- Elaborazione del consenso incrementale e dell'accesso condizionale.
Questi passaggi avanzati sono illustrati nel capitolo 3 dell'esercitazione 3-WebApp-multi-API.
Il codice per ASP.NET è simile al codice illustrato per ASP.NET Core:
- Un'azione del controller, protetta da un
[Authorize]
attributo, estrae l'ID tenant e l'ID utente del ClaimsPrincipal
membro del controller (ASP.NET usa HttpContext.User
). Ciò garantisce che solo gli utenti autenticati possano usare l'app.
Microsoft.Identity.Web aggiunge metodi di estensione al controller che forniscono servizi pratici per chiamare Microsoft Graph o un'API Web downstream oppure per ottenere un'intestazione di autorizzazione o anche un token. I metodi usati per chiamare direttamente un'API sono illustrati in dettaglio in Un'app Web che chiama le API Web: Chiamare un'API. Con questi metodi helper non è necessario acquisire manualmente un token.
Se, tuttavia, si vuole acquisire manualmente un token o compilare un'intestazione di autorizzazione, il codice seguente illustra come usare Microsoft.Identity.Web per farlo in un controller. Chiama un'API (Microsoft Graph) usando l'API REST anziché Microsoft Graph SDK.
Per ottenere un'intestazione di autorizzazione, si ottiene un IAuthorizationHeaderProvider
servizio dal controller usando un metodo GetAuthorizationHeaderProvider
di estensione . Per ottenere un'intestazione di autorizzazione per chiamare un'API per conto dell'utente, usare CreateAuthorizationHeaderForUserAsync
. Per ottenere un'intestazione di autorizzazione per chiamare un'API downstream per conto dell'applicazione stessa, in uno scenario daemon, usare CreateAuthorizationHeaderForAppAsync
.
Il frammento di codice seguente illustra l'azione di , che ottiene un'intestazione di HomeController
autorizzazione per chiamare Microsoft Graph come API REST:
[Authorize]
public class HomeController : Controller
{
[AuthorizeForScopes(Scopes = new[] { "user.read" })]
public async Task<IActionResult> Profile()
{
// Get an authorization header.
IAuthorizationHeaderProvider authorizationHeaderProvider = this.GetAuthorizationHeaderProvider();
string[] scopes = new string[]{"user.read"};
string authorizationHeader = await authorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync(scopes);
// Use the access token to call a protected web API.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", authorizationHeader);
string json = await client.GetStringAsync(url);
}
}
Il frammento di codice seguente illustra l'azione di HomeController
, che ottiene un token di accesso per chiamare Microsoft Graph come API REST:
[Authorize]
public class HomeController : Controller
{
[AuthorizeForScopes(Scopes = new[] { "user.read" })]
public async Task<IActionResult> Profile()
{
// Get an authorization header.
ITokenAcquirer tokenAcquirer = TokenAcquirerFactory.GetDefaultInstance().GetTokenAcquirer();
string[] scopes = new string[]{"user.read"};
string token = await tokenAcquirer.GetTokenForUserAsync(scopes);
// Use the access token to call a protected web API.
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
string json = await client.GetStringAsync(url);
}
}
Nell'esempio Java il codice che chiama un'API si trova nel getUsersFromGraph
metodo in AuthPageController.java#L62.
Il metodo tenta di chiamare getAuthResultBySilentFlow
. Se l'utente deve fornire il consenso a più ambiti, il codice elabora l'oggetto MsalInteractionRequiredException
per verificare l'utente.
@RequestMapping("/msal4jsample/graph/me")
public ModelAndView getUserFromGraph(HttpServletRequest httpRequest, HttpServletResponse response)
throws Throwable {
IAuthenticationResult result;
ModelAndView mav;
try {
result = authHelper.getAuthResultBySilentFlow(httpRequest, response);
} catch (ExecutionException e) {
if (e.getCause() instanceof MsalInteractionRequiredException) {
// If the silent call returns MsalInteractionRequired, redirect to authorization endpoint
// so user can consent to new scopes.
String state = UUID.randomUUID().toString();
String nonce = UUID.randomUUID().toString();
SessionManagementHelper.storeStateAndNonceInSession(httpRequest.getSession(), state, nonce);
String authorizationCodeUrl = authHelper.getAuthorizationCodeUrl(
httpRequest.getParameter("claims"),
"User.Read",
authHelper.getRedirectUriGraph(),
state,
nonce);
return new ModelAndView("redirect:" + authorizationCodeUrl);
} else {
mav = new ModelAndView("error");
mav.addObject("error", e);
return mav;
}
}
if (result == null) {
mav = new ModelAndView("error");
mav.addObject("error", new Exception("AuthenticationResult not found in session."));
} else {
mav = new ModelAndView("auth_page");
setAccountInfo(mav, httpRequest);
try {
mav.addObject("userInfo", getUserInfoFromGraph(result.accessToken()));
return mav;
} catch (Exception e) {
mav = new ModelAndView("error");
mav.addObject("error", e);
}
}
return mav;
}
// Code omitted here
Nell'esempio di Node.js il codice che acquisisce un token si trova nel acquireToken
metodo della AuthProvider
classe .
acquireToken(options = {}) {
return async (req, res, next) => {
try {
const msalInstance = this.getMsalInstance(this.msalConfig);
/**
* If a token cache exists in the session, deserialize it and set it as the
* cache for the new MSAL CCA instance. For more, see:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/caching.md
*/
if (req.session.tokenCache) {
msalInstance.getTokenCache().deserialize(req.session.tokenCache);
}
const tokenResponse = await msalInstance.acquireTokenSilent({
account: req.session.account,
scopes: options.scopes || [],
});
/**
* On successful token acquisition, write the updated token
* cache back to the session. For more, see:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/caching.md
*/
req.session.tokenCache = msalInstance.getTokenCache().serialize();
req.session.accessToken = tokenResponse.accessToken;
req.session.idToken = tokenResponse.idToken;
req.session.account = tokenResponse.account;
res.redirect(options.successRedirect);
} catch (error) {
if (error instanceof msal.InteractionRequiredAuthError) {
return this.login({
scopes: options.scopes || [],
redirectUri: options.redirectUri,
successRedirect: options.successRedirect || '/',
})(req, res, next);
}
next(error);
}
};
}
Questo token di accesso viene quindi usato per gestire le richieste all'endpoint /profile
:
router.get('/profile',
isAuthenticated, // check if user is authenticated
async function (req, res, next) {
try {
const graphResponse = await fetch(GRAPH_ME_ENDPOINT, req.session.accessToken);
res.render('profile', { profile: graphResponse });
} catch (error) {
next(error);
}
}
);
Nell'esempio Python il codice che chiama l'API è in app.py.
Il codice tenta di ottenere un token dalla cache dei token. Se non riesce a ottenere un token, reindirizza l'utente alla route di accesso. In caso contrario, può continuare a chiamare l'API.
@app.route("/call_downstream_api")
def call_downstream_api():
token = auth.get_token_for_user(app_config.SCOPE)
if "error" in token:
return redirect(url_for("login"))
# Use access token to call downstream api
api_result = requests.get(
app_config.ENDPOINT,
headers={'Authorization': 'Bearer ' + token['access_token']},
timeout=30,
).json()
return render_template('display.html', result=api_result)