您已建置用戶端應用程式物件。 現在,您可以使用它來取得令牌來呼叫 Web API。 在 ASP.NET 或 ASP.NET Core 中,呼叫 Web API 會在控制器中完成:
Microsoft.Identity.Web 新增擴充方法,以提供方便服務來呼叫 Microsoft Graph 或下游 Web API。 這些方法會在呼叫 Web API 的 Web 應用程式中詳細說明 :呼叫 API。 使用這些協助程式方法,您不需要手動取得令牌。
不過,如果您確實想要手動取得令牌,下列程式代碼會顯示在 主控制器中使用 Microsoft.Identity.Web 來執行此動作的範例。 它會使用 REST API 呼叫 Microsoft Graph(而不是 Microsoft Graph SDK)。 通常,您不需要取得令牌,您必須建置您新增至要求的授權標頭。 若要取得授權標頭,您可以在 IAuthorizationHeaderProvider
控制器的建構函式中插入相依性插入服務(或使用 Blazor 時的頁面建構函式),並在控制器動作中使用。 這個介面有方法可產生包含通訊協定的字串(Bearer、Pop、...) 和令牌。 若要取得代表使用者呼叫 API 的授權標頭,請使用 (CreateAuthorizationHeaderForUserAsync
)。 若要取得授權標頭來代表應用程式本身呼叫下游 API,請在精靈案例中使用 (CreateAuthorizationHeaderForAppAsync
)。
控制器方法受到 [Authorize]
屬性的保護,該屬性可確保只有已驗證的使用者可以使用 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
相依性插入使用。
以下是 的動作 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);
}
若要進一步瞭解此案例所需的程序代碼,請參閱 ms-identity-aspnetcore-webapp-tutorial 教學課程的階段 2 (2-1-Web 應用程式呼叫 Microsoft Graph) 步驟。
AuthorizeForScopes
Microsoft.Identity.Web 提供控制器動作上方的屬性(如果您使用 Razor 範本,則為 Razor 頁面的屬性。 它會確保使用者在需要時要求同意,並以累加方式進行。
還有其他複雜的變化,例如:
這些進階步驟涵蓋在 3-WebApp-multi-API 教學課程的第 3 章中。
ASP.NET 的程式代碼類似於 ASP.NET Core 所示的程式代碼:
- 受
[Authorize]
屬性保護的控制器動作會擷取控制器成員的租用戶標識碼和使用者標識碼 ClaimsPrincipal
(ASP.NET 使用 HttpContext.User
)。 這可確保只有已驗證的使用者可以使用應用程式。
Microsoft.Identity.Web 會將擴充方法新增至控制器,以提供方便的服務來呼叫 Microsoft Graph 或下游 Web API,或取得授權標頭,甚至是令牌。 在呼叫 Web API 的 Web 應用程式中,會詳細說明 用來直接呼叫 API 的方法:呼叫 API。 使用這些協助程式方法,您不需要手動取得令牌。
不過,如果您確實想要手動取得令牌或建置授權標頭,下列程式代碼會示範如何在控制器中使用 Microsoft.Identity.Web 來執行此動作。 它會使用 REST API 來呼叫 API(Microsoft Graph),而不是 Microsoft Graph SDK。
若要取得授權標頭,您可以使用擴充方法GetAuthorizationHeaderProvider
從控制器取得IAuthorizationHeaderProvider
服務。 若要取得代表使用者呼叫 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 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)