자습서: 보안 앱에서 사용자로 Microsoft Graph에 액세스
Azure App Service에서 실행되는 웹앱에서 Microsoft Graph에 액세스하는 방법에 대해 알아봅니다.
웹앱에서 Microsoft Graph에 대한 액세스 권한을 추가하고 로그인한 사용자로 작업을 수행하려고 합니다. 이 섹션에서는 위임된 권한을 웹앱에 부여하고 Microsoft Entra ID에서 로그인한 사용자의 프로필 정보를 가져오는 방법을 설명합니다.
이 자습서에서는 다음을 하는 방법을 알아볼 수 있습니다.
- 위임된 권한을 웹앱에 부여합니다.
- 로그인한 사용자를 위해 웹앱에서 Microsoft Graph를 호출합니다.
Azure를 구독하고 있지 않다면 시작하기 전에 Azure 체험 계정을 만듭니다.
사전 요구 사항
- App Service 인증/권한 부여 모듈을 사용하도록 설정된 Azure App Service에서 실행되는 웹 애플리케이션
프런트 엔드 액세스 권한을 부여하여 Microsoft Graph 호출
웹앱에서 인증 및 권한 부여를 사용하도록 설정했으므로, 웹앱이 Microsoft ID 플랫폼에 등록되고 Microsoft Entra 애플리케이션에서 지원됩니다. 이 단계에서는 사용자를 위해 Microsoft Graph에 액세스할 수 있는 권한을 웹앱에 부여합니다. (기술적으로는, 웹앱의 Microsoft Entra 애플리케이션에 사용자를 위해 Microsoft Graph Microsoft Entra 애플리케이션에 액세스할 수 있는 권한을 부여합니다.)
Microsoft Entra 관리 센터 메뉴에서 애플리케이션을 선택합니다.
앱 등록>소유 애플리케이션>이 디렉터리의 모든 애플리케이션 보기를 선택합니다. 웹앱 이름을 선택한 다음, API 사용 권한을 선택합니다.
사용 권한 추가를 선택한 다음, Microsoft API 및 Microsoft Graph를 선택합니다.
위임된 권한을 선택한 다음, 목록에서 User.Read를 선택합니다. 권한 추가를 선택합니다.
사용 가능한 액세스 토큰을 반환하도록 App Service 구성
이제 웹앱에 로그인한 사용자로 Microsoft Graph에 액세스하는 데 필요한 권한이 있습니다. 이 단계에서는 Microsoft Graph에 액세스하는 데 사용할 수 있는 액세스 토큰을 제공하도록 App Service 인증 및 권한 부여를 구성합니다. 이 단계에서는 다운스트림 서비스(Microsoft Graph)에 대한 User.Read 범위를 추가해야 합니다. https://graph.microsoft.com/User.Read
.
Important
사용 가능한 액세스 토큰을 반환하도록 App Service를 구성하지 않으면 코드에서 Microsoft Graph API를 호출할 때 CompactToken parsing failed with error code: 80049217
오류가 발생합니다.
Azure Resource Explorer로 이동하고 리소스 트리를 사용하여 웹앱을 찾습니다. 리소스 URL은 https://resources.azure.com/subscriptions/subscriptionId/resourceGroups/SecureWebApp/providers/Microsoft.Web/sites/SecureWebApp20200915115914
와 유사해야 합니다.
이제 Azure Resource Explorer가 리소스 트리에서 웹앱이 선택된 상태로 열립니다. 페이지의 위쪽에서 읽기/쓰기 를 선택하여 Azure 리소스 편집이 가능하도록 설정합니다.
왼쪽 브라우저에서 구성>authsettingsV2로 드릴다운합니다.
authsettingsV2 보기에서 편집을 선택합니다. identityProviders ->azureActiveDirectory의 login 섹션을 찾아 "loginParameters":[ "response_type=code id_token","scope=openid offline_access profile https://graph.microsoft.com/User.Read" ]
의 loginParameters 설정을 추가합니다.
"identityProviders": {
"azureActiveDirectory": {
"enabled": true,
"login": {
"loginParameters":[
"response_type=code id_token",
"scope=openid offline_access profile https://graph.microsoft.com/User.Read"
]
}
}
}
},
PUT을 선택하여 설정을 저장합니다. 이 설정이 적용되려면 몇 분 정도 걸릴 수 있습니다. 이제 웹앱이 적절한 액세스 토큰을 사용하여 Microsoft Graph에 액세스하도록 구성되었습니다. 그렇지 않으면 Microsoft Graph에서 압축 토큰의 형식이 올바르지 않다는 오류를 반환합니다.
Microsoft Graph 호출
이제 웹앱에 필요한 권한이 있으며 Microsoft Graph의 클라이언트 ID도 로그인 매개 변수에 추가됩니다.
Microsoft.Identity.Web 라이브러리를 사용하여 웹앱은 Microsoft Graph 인증을 위한 액세스 토큰을 얻습니다. 버전 1.2.0 이상에서 Microsoft.Identity.Web 라이브러리는 App Service 인증/권한 부여 모듈과 통합되며 함께 실행할 수 있습니다. Microsoft.Identity.Web은 웹앱이 App Service에서 호스트되는 것을 감지하고 App Service 인증/권한 부여 모듈에서 액세스 토큰을 가져옵니다. 그런 다음, 액세스 토큰은 Microsoft Graph API를 사용하여 인증된 요청에 전달됩니다.
이 코드를 샘플 애플리케이션의 일부로 보려면 GitHub의 샘플을 참조하세요.
참고 항목
Microsoft.Identity.Web 라이브러리는 기본 인증/권한 부여를 위해 또는 Microsoft Graph를 사용하여 요청을 인증하기 위해 웹앱에 필요하지 않습니다. App Service 인증/권한 부여 모듈만 사용하도록 설정된 상태에서 다운스트림 API를 안전하게 호출하는 것이 가능합니다.
하지만 App Service 인증/권한 부여는 보다 기본적인 인증 시나리오를 위해 설계되었습니다. 더 복잡한 시나리오(예: 사용자 지정 클레임 처리)의 경우 Microsoft.Identity.Web 라이브러리 또는 Microsoft 인증 라이브러리가 필요합니다. 처음에는 설정 및 구성 작업이 약간 더 많지만 Microsoft.Identity.Web 라이브러리는 App Service 인증/권한 부여 모듈과 함께 실행할 수 있습니다. 나중에 웹앱에서 더 복잡한 시나리오를 처리해야 하는 경우 App Service 인증/권한 부여 모듈을 사용하지 않도록 설정할 수 있으며 Microsoft.Identity.Web은 이미 앱의 일부입니다.
클라이언트 라이브러리 패키지 설치
.NET CLI(명령줄 인터페이스) 또는 Visual Studio의 패키지 관리자 콘솔을 사용하여 프로젝트에 Microsoft.Identity.Web 및 Microsoft.Identity.Web.GraphServiceClient NuGet 패키지를 설치합니다.
.NET CLI
명령줄을 열고 프로젝트 파일이 포함된 디렉터리로 전환합니다.
설치 명령을 실행합니다.
dotnet add package Microsoft.Identity.Web.GraphServiceClient
dotnet add package Microsoft.Identity.Web
패키지 관리자 콘솔
Visual Studio에서 프로젝트/솔루션을 열고, 도구>NuGet 패키지 관리자>패키지 관리자 콘솔 명령을 사용하여 콘솔을 엽니다.
설치 명령을 실행합니다.
Install-Package Microsoft.Identity.Web.GraphServiceClient
Install-Package Microsoft.Identity.Web
Startup.cs
Startup.cs 파일에서 AddMicrosoftIdentityWebApp
메서드는 웹앱에 Microsoft.Identity.Web을 추가합니다. AddMicrosoftGraph
메서드는 Microsoft Graph 지원을 추가합니다.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Identity.Web;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
// Some code omitted for brevity.
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
string[] initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy
options.FallbackPolicy = options.DefaultPolicy;
});
services.AddRazorPages()
.AddMvcOptions(options => {})
.AddMicrosoftIdentityUI();
services.AddControllersWithViews()
.AddMicrosoftIdentityUI();
}
}
appsettings.json
Microsoft Entra ID는 Microsoft.Identity.Web 라이브러리에 대한 구성을 지정합니다. Microsoft Entra 관리 센터의 포털 메뉴에서 애플리케이션을 선택한 다음 앱 등록을 선택합니다. App Service 인증/권한 부여 모듈을 활성화할 때 생성된 앱 등록을 선택합니다. (앱 등록은 웹앱과 이름이 같아야 합니다.) 테넌트 ID와 클라이언트 ID는 앱 등록 개요 페이지에서 찾을 수 있습니다. 도메인 이름은 테넌트의 Microsoft Entra 개요 페이지에서 찾을 수 있습니다.
Graph는 앱에 필요한 Microsoft Graph 엔드포인트와 초기 범위를 지정합니다.
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "[Enter the domain of your tenant, e.g. contoso.onmicrosoft.com]",
"TenantId": "[Enter 'common', or 'organizations' or the Tenant Id (Obtained from the Entra admin center. Select 'Endpoints' from the 'App registrations' blade and use the GUID in any of the URLs), e.g. aaaabbbb-0000-cccc-1111-dddd2222eeee]",
"ClientId": "[Enter the Client Id (Application ID obtained from the Microsoft Entra admin center), e.g. 00001111-aaaa-2222-bbbb-3333cccc4444]",
"ClientSecret": "[Copy the client secret added to the app from the Microsoft Entra admin center]",
"ClientCertificates": [
],
// the following is required to handle Continuous Access Evaluation challenges
"ClientCapabilities": [ "cp1" ],
"CallbackPath": "/signin-oidc"
},
"DownstreamApis": {
"MicrosoftGraph": {
// Specify BaseUrl if you want to use Microsoft graph in a national cloud.
// See https://learn.microsoft.com/graph/deployments#microsoft-graph-and-graph-explorer-service-root-endpoints
// "BaseUrl": "https://graph.microsoft.com/v1.0",
// Set RequestAppToken this to "true" if you want to request an application token (to call graph on
// behalf of the application). The scopes will then automatically
// be ['https://graph.microsoft.com/.default'].
// "RequestAppToken": false
// Set Scopes to request (unless you request an app token).
"Scopes": [ "User.Read" ]
// See https://aka.ms/ms-id-web/downstreamApiOptions for all the properties you can set.
}
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
Index.cshtml.cs
다음 예제는 로그인한 사용자로 Microsoft Graph를 호출하고 일부 사용자 정보를 가져오는 방법을 보여줍니다. GraphServiceClient
개체가 컨트롤러에 삽입되고 인증이 Microsoft.Identity.Web 라이브러리에 의해 구성되었습니다.
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Graph;
using System.IO;
using Microsoft.Identity.Web;
using Microsoft.Extensions.Logging;
// Some code omitted for brevity.
[AuthorizeForScopes(Scopes = new[] { "User.Read" })]
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly GraphServiceClient _graphServiceClient;
public IndexModel(ILogger<IndexModel> logger, GraphServiceClient graphServiceClient)
{
_logger = logger;
_graphServiceClient = graphServiceClient;
}
public async Task OnGetAsync()
{
try
{
var user = await _graphServiceClient.Me.GetAsync();
ViewData["Me"] = user;
ViewData["name"] = user.DisplayName;
using (var photoStream = await _graphServiceClient.Me.Photo.Content.GetAsync())
{
byte[] photoByte = ((MemoryStream)photoStream).ToArray();
ViewData["photo"] = Convert.ToBase64String(photoByte);
}
}
catch (Exception ex)
{
ViewData["photo"] = null;
}
}
}
리소스 정리
이 자습서를 완료하고 웹앱 또는 관련 리소스가 더 이상 필요하지 않으면 만든 리소스를 정리합니다.