Você criou seu objeto de aplicativo cliente. Agora, você o usa para adquirir um token para chamar uma API da Web. No ASP.NET ou ASP.NET Core, chamar uma API da Web é feito no controlador:
Microsoft.Identity.Web adiciona métodos de extensão que fornecem serviços de conveniência para chamar o Microsoft Graph ou uma API da Web downstream. Esses métodos são explicados em detalhes em Um aplicativo Web que chama APIs da Web: Chamar uma API. Com esses métodos auxiliares, você não precisa adquirir manualmente um token.
Se, no entanto, você quiser adquirir manualmente um token, o código a seguir mostra um exemplo de uso de Microsoft.Identity.Web para fazer isso em um controlador doméstico. Ele chama o Microsoft Graph usando a API REST (em vez do SDK do Microsoft Graph). Normalmente, você não precisa obter um token, você precisa criar um cabeçalho de Autorização que você adiciona à sua solicitação. Para obter um cabeçalho de autorização, você injeta o IAuthorizationHeaderProvider
serviço por injeção de dependência no construtor do controlador (ou no construtor de página se usar Blazor) e o usa nas ações do controlador. Esta interface tem métodos que produzem uma string contendo o protocolo (Bearer, Pop, ...) e um token. Para obter um cabeçalho de autorização para chamar uma API em nome do usuário, use (CreateAuthorizationHeaderForUserAsync
). Para obter um cabeçalho de autorização para chamar uma API downstream em nome do próprio aplicativo, em um cenário de daemon, use (CreateAuthorizationHeaderForAppAsync
).
Os métodos do controlador são protegidos por um [Authorize]
atributo que garante que apenas usuários autenticados possam usar o aplicativo 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 é IAuthorizationHeaderProvider
disponibilizado por injeção de dependência.
Aqui está o código simplificado para a ação do , que recebe um token para chamar o HomeController
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 melhor o código necessário para esse cenário, consulte a etapa de fase 2 (2-1-Web app Calls Microsoft Graph) do tutorial ms-identity-aspnetcore-webapp-tutorial .
O AuthorizeForScopes
atributo na parte superior da ação do controlador (ou da página Razor se você usar um modelo Razor) é fornecido por Microsoft.Identity.Web. Ele garante que o usuário seja solicitado a dar consentimento, se necessário, e de forma incremental.
Existem outras variações complexas, tais como:
- Chamando várias APIs.
- Processamento de consentimento incremental e Acesso Condicional.
Essas etapas avançadas são abordadas no capítulo 3 do tutorial 3-WebApp-multi-APIs .
O código para ASP.NET é semelhante ao código mostrado para ASP.NET Core:
- Uma ação do controlador, protegida por um
[Authorize]
atributo, extrai o ID do locatário e o ID do usuário do ClaimsPrincipal
membro do controlador (ASP.NET usa HttpContext.User
). Isso garante que apenas usuários autenticados possam usar o aplicativo.
Microsoft.Identity.Web adiciona métodos de extensão ao Controlador que fornecem serviços de conveniência para chamar o Microsoft Graph ou uma API da Web downstream, ou para obter um cabeçalho de autorização ou até mesmo um token. Os métodos usados para chamar uma API diretamente são explicados em detalhes em Um aplicativo Web que chama APIs da Web: Chamar uma API. Com esses métodos auxiliares, você não precisa adquirir manualmente um token.
Se, no entanto, você quiser adquirir manualmente um token ou criar um cabeçalho de autorização, o código a seguir mostra como usar Microsoft.Identity.Web para fazer isso em um controlador. Ele chama uma API (Microsoft Graph) usando a API REST em vez do SDK do Microsoft Graph.
Para obter um cabeçalho de autorização, você obtém um IAuthorizationHeaderProvider
serviço do controlador usando um método GetAuthorizationHeaderProvider
de extensão . Para obter um cabeçalho de autorização para chamar uma API em nome do usuário, use CreateAuthorizationHeaderForUserAsync
. Para obter um cabeçalho de autorização para chamar uma API downstream em nome do próprio aplicativo, em um cenário de daemon, use CreateAuthorizationHeaderForAppAsync
.
O trecho a seguir mostra a ação do , que obtém um cabeçalho de autorização para chamar o HomeController
Microsoft Graph como uma 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);
}
}
O trecho a seguir mostra a ação do , que obtém um token de acesso para chamar o HomeController
Microsoft Graph como uma 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);
}
}
No exemplo Java, o código que chama uma API está no getUsersFromGraph
método em AuthPageController.java#L62.
O método tenta chamar getAuthResultBySilentFlow
. Se o usuário precisar consentir com mais escopos, o código processa o MsalInteractionRequiredException
objeto para desafiar o usuário.
@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
No exemplo de Node.js, o código que adquire um token está no acquireToken
método da 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);
}
};
}
Esse token de acesso é usado para lidar com solicitações para o ponto de /profile
extremidade:
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);
}
}
);
No exemplo de Python, o código que chama a API está em app.py.
O código tenta obter um token do cache de token. Se não conseguir obter um token, redireciona o utilizador para a rota de início de sessão. Caso contrário, ele pode continuar a chamar a 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)