Ha compilado el objeto de aplicación cliente. Ahora, se usa para adquirir un token para llamar a una API web. En ASP.NET o ASP.NET Core, las llamadas a las API web se hacen en el controlador:
Microsoft.Identity.Web agrega métodos de extensión que proporcionan servicios útiles para llamar a Microsoft Graph o a una API web de nivel inferior. Estos métodos se explican con detalle en Aplicación web que llama a las API web: llamada a una API. Con estos métodos auxiliares, no es necesario adquirir un token manualmente.
Sin embargo, si desea adquirir manualmente un token, el código siguiente muestra un ejemplo de uso de Microsoft.Identity.Web para hacerlo en un controlador de inicio. Llama a Microsoft Graph mediante la API REST (en lugar del SDK de Microsoft Graph). Normalmente, no es necesario obtener un token; debe crear un encabezado de autorización que agregue a la solicitud. Para obtener un encabezado de autorización, inyecte el servicio IAuthorizationHeaderProvider
mediante inyección de dependencia en el constructor de su controlador (o en el constructor de su página si utiliza Blazor), y utilícelo en las acciones de su controlador. Esta interfaz tiene métodos que generan una cadena que contiene el protocolo (Portador, Pop, ...) y un token. Para obtener un encabezado de autorización para llamar a una API en nombre del usuario, use (CreateAuthorizationHeaderForUserAsync
). Para obtener un encabezado de autorización para llamar a una API de bajada en nombre de la propia aplicación, en un escenario de demonio, use (CreateAuthorizationHeaderForAppAsync
).
Los métodos del controlador están protegidos por el atributo [Authorize]
, que garantiza que solo los usuarios autenticados pueden usar la aplicación 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 pone IAuthorizationHeaderProvider
a disposición de la inserción de dependencias.
Este es código simplificado para la acción de HomeController
, que obtiene un token para llamar a 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);
}
Para entender mejor el código necesario para este escenario, consulte el paso de la fase 2 2-1- Aplicación web llama a Microsoft Graph del tutorial ms-identity-aspnetcore-webapp-tutorial.
Microsoft.Identity.Web proporciona el atributo AuthorizeForScopes
en la parte superior de la acción del controlador (o de la página de Razor si usa una plantilla de Razor). Garantiza que se le solicita consentimiento al usuario, en caso necesario y de manera incremental.
Hay otras variaciones complejas, como:
- Llamada a varias API.
- Procesamiento del consentimiento incremental y el acceso condicional.
Estos pasos avanzados se tratan en el capítulo 3 del tutorial 3-WebApp-multi-APIs.
El código para ASP.NET es similar al que se muestra para ASP.NET Core:
- Una acción del controlador, protegida por un
[Authorize]
atributo, extrae el ID de inquilino y el ID de usuario del ClaimsPrincipal
miembro del controlador ( usa ASP.NETHttpContext.User
). Esto garantiza que solo los usuarios autenticados puedan usar la aplicación.
Microsoft.Identity.Web agrega métodos de extensión al controlador que proporcionan servicios prácticos para llamar a Microsoft Graph o a una API web posterior, o para obtener un encabezado de autorización o incluso un token. Los métodos utilizados para llamar directamente a una API se explican con detalle en Aplicación web que llama a las API web: llamada a una API. Con estos métodos auxiliares, no es necesario adquirir un token manualmente.
Sin embargo, si desea adquirir manualmente un token o crear un encabezado de autorización, el siguiente código muestra cómo utilizar Microsoft.Identity.Web para hacerlo en un controlador. Llama a una API (Microsoft Graph) mediante la API de REST, en lugar del SDK de Microsoft Graph.
Para obtener un encabezado de autorización, obtenga un servicio de IAuthorizationHeaderProvider
del controlador mediante un método de extensión GetAuthorizationHeaderProvider
. Para obtener un encabezado de autorización para llamar a una API en nombre del usuario, use CreateAuthorizationHeaderForUserAsync
. Para obtener un encabezado de autorización para llamar a una API de bajada en nombre de la propia aplicación, en un escenario de demonio, use CreateAuthorizationHeaderForAppAsync
.
En el fragmento de código siguiente se muestra la acción de HomeController
, que obtiene un encabezado de autorización para llamar a Microsoft Graph como una API de 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);
}
}
En el fragmento de código siguiente se muestra la acción de HomeController
, que obtiene un token de acceso para llamar a Microsoft Graph como una API de 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);
}
}
En el ejemplo de Java, el código que llama a una API se encuentra en el getUsersFromGraph
método de AuthPageController.java#L62.
El método intenta llamar a getAuthResultBySilentFlow
. Si el usuario necesita dar su consentimiento a más ámbitos, el código procesa el objeto MsalInteractionRequiredException
para desafiar al usuario.
@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
En el ejemplo Node.js, el código que adquiere un token está en el método acquireToken
de la clase 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);
}
};
}
A continuación, este token de acceso se usa para controlar solicitudes al punto de conexión /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);
}
}
);
En el ejemplo de Python, el código que llama a la API está en app.py.
El código intenta obtener un token de la caché de tokens. Si no puede obtener un token, redirige al usuario a la ruta de inicio de sesión. De lo contrario, puede continuar con la llamada a la 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)