Sie haben das Clientanwendungsobjekt erstellt. Jetzt rufen Sie damit ein Token ab, um eine Web-API aufzurufen. In ASP.NET oder ASP.NET Core erfolgt das Aufrufen einer Web-API im Controller:
Von Microsoft.Identity.Web werden Erweiterungsmethoden hinzugefügt, die praktische Dienste zum Aufrufen von Microsoft Graph oder einer Downstream-Web-API bereitstellen. Eine ausführliche Beschreibung dieser Methoden finden Sie unter Web-App, die Web-APIs aufruft: Aufrufen einer Web-API. Mit diesen Hilfsmethoden ist kein manueller Tokenabruf erforderlich.
Wenn Sie jedoch ein Token manuell abrufen möchten, sehen Sie sich den folgenden Code an, der ein Beispiel dafür bietet, wie Sie dies mithilfe von Microsoft.Identity.Web in einem Home-Controller tun können. Microsoft Graph wird mit der REST-API (anstelle des Microsoft Graph SDK) aufgerufen. In der Regel müssen Sie kein Token abrufen, sondern einen Autorisierungsheader erstellen, den Sie Ihrer Anforderung hinzufügen. Zum Abrufen eines Autorisierungsheaders fügen Sie den IAuthorizationHeaderProvider
-Dienst durch eine Abhängigkeitsinjektion in den Konstruktor Ihres Controllers ein (oder in den Seitenkonstruktor, wenn Sie Blazor verwenden) und verwenden ihn in den Controlleraktionen. Diese Schnittstelle verfügt über Methoden, die eine Zeichenfolge erzeugen, die das Protokoll (Bearer, Pop...) und ein Token enthält. Zum Abrufen eines Autorisierungsheaders zum Aufrufen einer API für den Benutzer verwenden Sie (CreateAuthorizationHeaderForUserAsync
). Verwenden Sie zum Abrufen eines Autorisierungsheaders zum Aufrufen einer Downstream-API für die Anwendung selbst in einem Daemon-Szenario (CreateAuthorizationHeaderForAppAsync
).
Die Controllermethoden sind durch ein [Authorize]
-Attribut geschützt, das sicherstellt, dass nur authentifizierte Benutzer die Web-App verwenden dürfen.
[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 stellt IAuthorizationHeaderProvider
durch Abhängigkeitsinjektion zur Verfügung.
Im Folgenden finden Sie den vereinfachten Code für die Aktion des HomeController
, mit der ein Token zum Aufrufen von Microsoft Graph abgerufen wird:
[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);
}
Um den für dieses Szenario erforderlichen Code besser zu verstehen, sollten Sie sich den Schritt der 2. Phase (2.1 Web-App ruft Microsoft Graph auf) im Tutorial ms-identity-aspnetcore-webapp-tutorial ansehen.
Das AuthorizeForScopes
-Attribut über der Controlleraktion (oder der Razor-Seite bei Verwendung einer Razor-Vorlage) wird von Microsoft.Identity.Web bereitgestellt. Damit wird sichergestellt, dass der Benutzer bei Bedarf (und inkrementell) zur Zustimmung aufgefordert wird.
Es gibt andere komplexe Varianten, wie beispielsweise:
- Aufrufen mehrerer APIs
- Verarbeiten der inkrementeller Einwilligung und des bedingten Zugriffs.
Diese erweiterten Schritte werden in Kapitel 3 des Tutorials 3-WebApp-multi-APIs erörtert.
Der Code für ASP.NET ähnelt dem für ASP.NET Core dargestellten Code:
- Eine durch ein
[Authorize]
-Attribut geschützte Controlleraktion extrahiert die Mandanten- und Benutzer-ID des ClaimsPrincipal
-Members aus dem Controller (ASP.NET verwendet HttpContext.User
). Dadurch wird sichergestellt, dass nur authentifizierte Benutzer*innen die App verwenden können.
Microsoft.Identity.Web fügt dem Controller Erweiterungsmethoden hinzu, die praktische Dienste zum Aufrufen von Microsoft Graph oder einer Downstream-Web-API oder zum Abrufen eines Autorisierungsheaders oder sogar eines Tokens bereitstellen. Die Methoden zum direkten Aufruf einer API werden ausführlich unter Eine Web-App, die Web-APIs aufruft: Aufrufen einer API beschrieben. Mit diesen Hilfsmethoden ist kein manueller Tokenabruf erforderlich.
Wenn Sie jedoch manuell ein Token abrufen oder einen Autorisierungsheader erstellen möchten, zeigt der folgende Code, wie dies mit Microsoft.Identity.Web in einem Controller möglich ist. Dabei wird eine API (Microsoft Graph) mit der REST-API und nicht mit dem Microsoft Graph SDK aufgerufen.
Zum Abrufen eines Autorisierungsheaders rufen Sie mit einer GetAuthorizationHeaderProvider
-Erweiterungsmethode einen IAuthorizationHeaderProvider
-Dienst vom Controller ab. Wenn Sie einen Autorisierungsheader zum Aufrufen einer API für einen Benutzer abrufen möchten, verwenden Sie CreateAuthorizationHeaderForUserAsync
. Wenn Sie einen Autorisierungsheader zum Aufrufen einer Downstream-API für die Anwendung selbst in einem Daemon-Szenario abrufen möchten, verwenden Sie CreateAuthorizationHeaderForAppAsync
.
Der folgende Codeausschnitt zeigt die HomeController
-Aktion, mit der ein Autorisierungsheader zum Aufrufen von Microsoft Graph als REST-API abgerufen wird:
[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);
}
}
Der folgende Codeausschnitt zeigt die HomeController
-Aktion, mit der ein Zugriffstoken zum Aufrufen von Microsoft Graph als REST-API abgerufen wird:
[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);
}
}
Im Java-Beispiel befindet sich der Code, der eine API aufruft, in der getUsersFromGraph
-Methode in AuthPageController.java#L62.
Die Methode versucht, getAuthResultBySilentFlow
aufzurufen. Wenn der Benutzer mehr Bereichen zustimmen muss, verarbeitet der Code das MsalInteractionRequiredException
-Objekt, um den Benutzer dazu aufzufordern.
@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
In dem Node.js-Beispiel befindet sich der Code, der ein Token erwirbt, in der acquireToken
-Methode der AuthProvider
-Klasse.
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);
}
};
}
Dieses Zugriffstoken wird dann verwendet, um Anforderungen an den Endpunkt /profile
zu behandeln:
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);
}
}
);
Im Python-Beispiel befindet sich der Code, der die API aufruft, in app.py.
Der Code versucht, ein Token aus dem Tokencache abzurufen. Wenn kein Token abgerufen werden kann, leitet er den Benutzer zur Anmelderoute um. Andernfalls kann er mit dem Aufrufen der API fortfahren.
@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)