Você criou o objeto de aplicativo cliente. Agora, você o usará para adquirir um token para chamar uma API Web. No ASP.NET ou ASP.NET Core, a chamada de uma API Web é feita 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 Web downstream. Esses métodos são explicados em detalhes em Um aplicativo Web que chama APIs Web: chamar uma API. Com esses métodos auxiliares, não será necessário adquirir um token manualmente.
No entanto, se você quiser adquirir um token manualmente, o código a seguir mostra um exemplo de como usar 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 para adicionar à sua solicitação. Para obter um cabeçalho de autorização, injete o serviço IAuthorizationHeaderProvider
por dependência em seu construtor do controlador (ou seu construtor de página, se você usar o Blazor) e use-o em suas ações de controlador. Essa interface tem métodos que produzem uma cadeia de caracteres que contém o protocolo (Portador, 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 atributo [Authorize]
que garante que somente 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)
}
O ASP.NET Core disponibiliza IAuthorizationHeaderProvider
por injeção de dependência.
Aqui está o código simplificado para a ação do HomeController
, que obtém um token para chamar o 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 (2-1-Aplicativo Web chama o Microsoft Graph) da fase 2 do tutorial ms-identity-aspnetcore-webapp-tutorial.
O atributo AuthorizeForScopes
sobre a ação do controlador (ou da página Razor se você usar um modelo Razor) é fornecido pelo Microsoft.Identity.Web. Ele garante que o usuário seja solicitado a dar consentimento, se necessário, e incrementalmente.
Há outras variações complexas, tais como:
- Chamar 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 atributo
[Authorize]
, extrai a ID de locatário e a ID de usuário do membro ClaimsPrincipal
do controlador (ASP.NET usa HttpContext.User
). Isso garante que somente usuários autenticados possam usar o aplicativo.
O 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 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 Web: chamar uma API. Com esses métodos auxiliares, não será necessário adquirir um token manualmente.
No entanto, se você quiser adquirir um token manualmente ou compilar 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 serviço IAuthorizationHeaderProvider
do controlador usando um método de extensão GetAuthorizationHeaderProvider
. 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 de código a seguir mostra a ação do HomeController
, que obtém um cabeçalho de autorização para chamar o 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 de código a seguir mostra a ação do HomeController
, que obtém um token de acesso para chamar o 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 de Java, o código que chama uma API está no método getUsersFromGraph
em AuthPageController.java#L62.
O método tenta chamar getAuthResultBySilentFlow
. Se o usuário precisar consentir com mais escopos, o código processa o objeto MsalInteractionRequiredException
para desafiá-lo.
@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 Node.js, o código que adquire um token está no método acquireToken
da 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);
}
};
}
Esse token de acesso é usado para lidar com solicitações para o ponto de extremidade /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);
}
}
);
No exemplo do 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, ele redirecionará o usuário para a rota de entrada. Caso contrário, ele poderá 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)