Вы создали объект клиентского приложения. Теперь вы используете его для получения маркера для вызова веб-API. В ASP.NET и ASP.NET Core вызов веб-API выполняется в контроллере:
Microsoft.Identity.Web добавляет методы расширения, которые предоставляют удобные службы для вызова Microsoft Graph или подчиненных веб-API. Эти методы подробно описаны в статье Веб-приложение, которое вызывает веб-API: вызов API. При использовании этих вспомогательных методов вам не нужно получать токен вручную.
Однако если вы хотите получить токен вручную, в следующем коде показан пример использования Microsoft.Identity.Web в контроллере Home для этой цели. Он вызывает Microsoft Graph с помощью REST API (вместо пакета SDK для Microsoft Graph). Как правило, вам не нужно получать маркер, необходимо создать заголовок авторизации, добавляемый в запрос. Чтобы получить заголовок авторизации, вы внедряете IAuthorizationHeaderProvider
службу путем внедрения зависимостей в конструктор контроллера (или конструктор страницы при использовании Blazor) и используете ее в действиях контроллера. Этот интерфейс содержит методы, которые создают строку, содержащую протокол (Bearer, Pop, ...) и токен. Чтобы получить заголовок авторизации для вызова API от имени пользователя, используйте (CreateAuthorizationHeaderForUserAsync
). Чтобы получить заголовок авторизации для вызова нижестоящего API от имени самого приложения, в сценарии управляющей программы используйте (CreateAuthorizationHeaderForAppAsync
).
Методы контроллера защищены атрибутом [Authorize]
, который разрешает использовать веб-приложение только авторизованным пользователям.
[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
путем внедрения зависимостей.
Ниже приведен упрощенный код для действия 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);
}
Чтобы лучше понять, как работает код, необходимый для этого сценария, см. шаг 2 (2-1. Веб-приложение, вызывающее Microsoft Graph) в учебнике ms-identity-aspnetcore-webapp-tutorial.
Атрибут AuthorizeForScopes
в верхней части действия контроллера (или страницы Razor, если используется шаблон Razor) предоставляется с помощью Microsoft.Identity.Web. Это гарантирует, что при необходимости (и постепенно) пользователь будет получать запрос на согласие.
Существуют и другие сложные варианты, например:
- Вызов нескольких интерфейсов API.
- Обработка добавочного согласия и условного доступа.
Эти дополнительные действия описаны в главе 3 учебника 3-WebApp-multi-APIs.
Код для ASP.NET аналогичен коду, показанному для ASP.NET Core:
- Действие контроллера, защищенное атрибутом
[Authorize]
, извлекает идентификатор клиента и идентификатор ClaimsPrincipal
пользователя члена контроллера (ASP.NET используется HttpContext.User
). Это гарантирует, что только прошедшие проверку подлинности пользователи могут использовать приложение.
Microsoft.Identity.Web добавляет методы расширения в контроллер, предоставляющий удобные службы для вызова Microsoft Graph или нижнего веб-API, или для получения заголовка авторизации или даже маркера. Методы, используемые для вызова API напрямую, подробно описаны в веб-приложении, которое вызывает веб-API: вызов API. При использовании этих вспомогательных методов вам не нужно получать токен вручную.
Однако если вы хотите вручную получить маркер или создать заголовок авторизации, в следующем коде показано, как использовать Microsoft.Identity.Web для этого в контроллере. Он вызывает API (Microsoft Graph) с помощью REST API вместо пакета SDK Microsoft Graph.
Чтобы получить заголовок авторизации, вы получите IAuthorizationHeaderProvider
службу от контроллера с помощью метода GetAuthorizationHeaderProvider
расширения. Чтобы получить заголовок авторизации для вызова API от имени пользователя, используйте CreateAuthorizationHeaderForUserAsync
. Чтобы получить заголовок авторизации для вызова нижестоящего API от имени самого приложения, в сценарии управляющей программы используйте CreateAuthorizationHeaderForAppAsync
.
В следующем фрагменте кода показано действие заголовка HomeController
авторизации для вызова Microsoft Graph в качестве REST API:
[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);
}
}
В следующем фрагменте кода показано действие маркера HomeController
доступа для вызова Microsoft Graph в качестве REST API:
[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);
}
}
В примере Java код, вызывающий API, находится в методе getUsersFromGraph
в AuthPageController.java#L62.
Метод пытается вызвать getAuthResultBySilentFlow
. Если пользователь должен предоставить согласие на использование дополнительных областей, код обрабатывает объект MsalInteractionRequiredException
для отправки запроса пользователю.
@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
В примере Node.js код, который получает маркер, находится в acquireToken
методе 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);
}
};
}
Затем этот маркер доступа используется для обработки запросов к конечной точке /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);
}
}
);
В примере Python код, вызывающий API, находится в app.py.
Код пытается получить токен из кэша токенов. Если маркер не удается получить, он перенаправляет пользователя на маршрут входа. В противном случае он может продолжить вызов 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)