Vous avez généré l’objet de votre application cliente. À présent, vous devez l’utiliser pour acquérir un jeton afin d’appeler une API web. Dans ASP.NET ou ASP.NET Core, l’appel d’une API web s’effectue dans le contrôleur :
Microsoft.Identity.Web ajoute des méthodes d’extension qui fournissent des services pratiques pour appeler Microsoft Graph ou une API web en aval. Ces méthodes sont expliquées en détail dans Application web qui appelle des API web : Appeler une API. Avec ces méthodes d’assistance, vous n’avez pas besoin d’acquérir manuellement un jeton.
Toutefois, si vous souhaitez acquérir manuellement un jeton, le code suivant montre un exemple d’utilisation de Microsoft.Identity.Web pour le faire dans un contrôleur home. Il appelle Microsoft Graph à l’aide de l’API REST (au lieu du Kit de développement logiciel (SDK) Microsoft Graph). En règle générale, vous n’avez pas besoin d’obtenir un jeton, juste de créer un en-tête d’autorisation que vous ajoutez à votre requête. Pour obtenir un jeton d’autorisation, vous injectez le service IAuthorizationHeaderProvider
par injection de dépendances dans le constructeur de votre contrôleur (ou votre constructeur de page si vous utilisez Blazor), puis vous l’utilisez dans les actions de votre contrôleur. Cette interface propose des méthodes qui produisent une chaîne contenant le protocole (Porteur, Pop, ...) et un jeton. Pour obtenir un en-tête d’autorisation pour appeler une API au nom de l’utilisateur, utilisez (CreateAuthorizationHeaderForUserAsync
). Pour obtenir un en-tête d’autorisation pour appeler une API en aval pour le compte de l’application elle-même, dans un scénario démon, utilisez (CreateAuthorizationHeaderForAppAsync
).
Les méthodes de contrôleur sont protégées par un attribut [Authorize]
qui veille à ce que seuls des utilisateurs authentifiés puissent utiliser l’application 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 rend IAuthorizationHeaderProvider
disponible par injection de dépendances.
Voici le code simplifié pour l’action du HomeController
, qui obtient un jeton pour appeler 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);
}
Pour mieux comprendre le code nécessaire dans ce scénario, consultez la phase 2 (2-1-Web App Calls Microsoft Graph) [Application web appelant Microsoft Graph] du tutoriel ms-identity-aspnetcore-webapp-tutorial.
L’attribut AuthorizeForScopes
en haut de l’action du contrôleur (ou de la page Razor si vous utilisez un modèle Razor) est fourni par Microsoft.Identity.Web. Il s’assure que l’utilisateur est invité à donner son consentement, le cas échéant, et de façon incrémentielle.
Il existe d’autres variantes complexes, telles que :
- Appels de plusieurs API.
- Traitement du consentement incrémentiel et de l’accès conditionnel.
Ces étapes avancées sont abordées dans le chapitre 3 du tutoriel 3-WebApp-multi-APIs.
Le code pour ASP.NET est similaire au code présenté pour ASP.NET Core :
- Une action de contrôleur, protégée par un attribut
[Authorize]
, extrait l’ID du locataire et l’ID de l’utilisateur du membre ClaimsPrincipal
du contrôleur (ASP.NET utilise HttpContext.User
). Cela garantit que seuls les utilisateurs authentifiés peuvent utiliser l’application.
Microsoft.Identity.Web ajoute des méthodes d’extension au contrôleur qui fournissent des services pratiques pour appeler Microsoft Graph ou une API web en aval, ou pour obtenir un en-tête d’autorisation, voire un jeton. Les méthodes servant à appeler directement une API sont expliquées en détail dans l’article Application web appelant des API web : Appeler une API. Avec ces méthodes d’assistance, vous n’avez pas besoin d’acquérir manuellement un jeton.
Toutefois, si vous souhaitez acquérir manuellement un jeton ou créer un en-tête d’autorisation, le code suivant montre comment utiliser Microsoft.Identity.Web pour le faire dans un contrôleur. Il appelle une API (Microsoft Graph) à l’aide de l’API REST en lieu et place du Kit de développement logiciel (SDK) Microsoft Graph.
Pour obtenir un en-tête d’autorisation, vous obtenez un service IAuthorizationHeaderProvider
du contrôleur à l’aide d’une méthode d’extension GetAuthorizationHeaderProvider
. Pour obtenir un en-tête d’autorisation pour appeler une API au nom de l’utilisateur, utilisez CreateAuthorizationHeaderForUserAsync
. Pour obtenir un en-tête d’autorisation pour appeler une API en aval pour le compte de l’application elle-même, dans un scénario démon, utilisez CreateAuthorizationHeaderForAppAsync
.
L’extrait de code suivant montre l’action de HomeController
, qui obtient un en-tête d’autorisation pour appeler Microsoft Graph en tant qu’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);
}
}
L’extrait de code suivant montre l’action de HomeController
, qui obtient un jeton d’accès pour appeler Microsoft Graph en tant qu’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);
}
}
Dans l’exemple Java, le code qui appelle une API se trouve dans la getUsersFromGraph
méthode AuthPageController.java#L62.
La méthode tente d’appeler getAuthResultBySilentFlow
. Si l’utilisateur a besoin de consentir à d’autres étendues, le code traite l’objet MsalInteractionRequiredException
pour interroger l’utilisateur.
@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
Dans l’exemple Node.js, le code qui acquiert un jeton se trouve dans la méthode acquireToken
de la classe AuthProvider
.
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);
}
};
}
Ce jeton d’accès est ensuite utilisé pour gérer les demandes adressées au point de terminaison /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);
}
}
);
Dans l’exemple Python, le code qui appelle l’API se trouve dans app.py.
Le code tente d’obtenir un jeton à partir du cache de jetons. S’il ne peut pas obtenir de jeton, il redirige l’utilisateur vers la route de connexion. Autrement, il peut procéder à l’appel de 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)