ASP.NET Core および OWIN アプリケーションから Microsoft.Identity.Web と Microsoft Graph SDK を使用して、Microsoft Graph により Microsoft 365 のデータやサービスにアクセスします。
Microsoft Graph統合について
Microsoft Graphは、Microsoft 365、Windows、Enterprise Mobility + Security間でデータにアクセスするための統合 API エンドポイントを提供します。 Microsoft。Identity.Web は、Microsoft Graphの認証とトークンの取得を簡素化しますが、Microsoft Graph SDK は Graph エンドポイントを呼び出すための fluent 型の API を提供します。
Microsoft.Identity.Web.GraphServiceClient を選択します。
次の利点が、Microsoft.Identity.Web.GraphServiceClient を使用して Microsoft Graph を呼び出す際の推奨方法であることを示しています。
- トークンの自動取得: ユーザー トークンとアプリ トークンをシームレスに処理します
- トークン キャッシュ: パフォーマンスのための組み込みのキャッシュ
- Fluent API: タイプ セーフで IntelliSense に対応した Graph 呼び出し
- 増分同意: 要求時に追加のスコープを要求する
- 複数の認証スキーム: Web アプリと Web API のサポート
- v1.0 とベータ版の両方: 安定したエンドポイントとプレビュー エンドポイントを一緒に使用する
必要なパッケージをインストールする
Microsoft Graph SDK 統合パッケージをインストールします。
dotnet add package Microsoft.Identity.Web.GraphServiceClient
Microsoft Graph ベータ API の場合:
dotnet add package Microsoft.Identity.Web.GraphServiceClientBeta
ASP.NET Coreを設定する
1. サービスを構成する
あなたのアプリケーションにMicrosoft Graphサポートを追加します。
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
// Add authentication (web app or web API)
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
// Add Microsoft Graph support
builder.Services.AddMicrosoftGraph();
builder.Services.AddControllersWithViews();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
2. appsettings.json を構成する
構成ファイルで Graph オプションを構成します。
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-client-id",
"ClientSecret": "your-client-secret",
"CallbackPath": "/signin-oidc"
},
"DownstreamApis": {
"MicrosoftGraph": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": ["User.Read", "User.ReadBasic.All"]
}
}
}
コードを使用した構成:
builder.Services.AddMicrosoftGraph(options =>
{
builder.Configuration.GetSection("DownstreamApis:MicrosoftGraph").Bind(options);
});
または、コードで直接構成します。
builder.Services.AddMicrosoftGraph();
builder.Services.Configure<MicrosoftGraphOptions>(options =>
{
options.BaseUrl = "https://graph.microsoft.com/v1.0";
options.Scopes = new[] { "User.Read", "Mail.Read" };
});
3. 各国のクラウド サポートを構成する
国内クラウドでMicrosoft Graphを使用するには、構成で BaseUrl を指定します。
{
"DownstreamApis": {
"MicrosoftGraph": {
"BaseUrl": "https://graph.microsoft.us/v1.0",
"Scopes": ["User.Read"]
}
}
}
エンドポイント URL については、「Microsoft Graph デプロイ」を参照してください。
GraphServiceClient を使用する
GraphServiceClient を挿入する
コンストラクターから GraphServiceClient を挿入します。
using Microsoft.Graph;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[Authorize]
public class ProfileController : Controller
{
private readonly GraphServiceClient _graphClient;
public ProfileController(GraphServiceClient graphClient)
{
_graphClient = graphClient;
}
public async Task<IActionResult> Index()
{
// Call Microsoft Graph
var user = await _graphClient.Me.GetAsync();
return View(user);
}
}
委任されたアクセス許可を使用する (ユーザー トークン)
委任されたアクセス許可を持つサインインユーザーの代わりに Graph を呼び出します。
基本的なユーザー プロファイルを取得する
Microsoft Graphから現在のユーザーのプロファイル情報を取得します。
[Authorize]
public class ProfileController : Controller
{
private readonly GraphServiceClient _graphClient;
public ProfileController(GraphServiceClient graphClient)
{
_graphClient = graphClient;
}
public async Task<IActionResult> Me()
{
// Get current user's profile
var user = await _graphClient.Me.GetAsync();
return View(new UserViewModel
{
DisplayName = user.DisplayName,
Mail = user.Mail,
JobTitle = user.JobTitle
});
}
}
増分同意を要求する
アプリケーションで必要なときに、追加のスコープを動的に要求します。
[Authorize]
[AuthorizeForScopes("Mail.Read")]
public class MailController : Controller
{
private readonly GraphServiceClient _graphClient;
public MailController(GraphServiceClient graphClient)
{
_graphClient = graphClient;
}
public async Task<IActionResult> Inbox()
{
try
{
// Request Mail.Read scope dynamically
var messages = await _graphClient.Me.Messages
.GetAsync(r => r.Options.WithScopes("Mail.Read"));
return View(messages);
}
catch (MicrosoftIdentityWebChallengeUserException)
{
// ASP.NET Core will redirect user to consent
// thansk to the AuthorizeForScopes attribute.
throw;
}
}
}
クエリ オプションを適用する
Graph SDK クエリ オプションを使用して、結果をフィルター処理、選択、並べ替えます。
public async Task<IActionResult> UnreadMessages()
{
var messages = await _graphClient.Me.Messages
.GetAsync(requestConfiguration =>
{
requestConfiguration.QueryParameters.Filter = "isRead eq false";
requestConfiguration.QueryParameters.Select = new[] { "subject", "from", "receivedDateTime" };
requestConfiguration.QueryParameters.Orderby = new[] { "receivedDateTime desc" };
requestConfiguration.QueryParameters.Top = 10;
// Request specific scope
requestConfiguration.Options.WithScopes("Mail.Read");
});
return View(messages);
}
結果を順に見ていく
各ページを反復処理して、Microsoft Graphからのページングされた結果を処理します。
public async Task<IActionResult> AllUsers()
{
var allUsers = new List<User>();
// Get first page
var users = await _graphClient.Users
.GetAsync(r => r.Options.WithScopes("User.ReadBasic.All"));
// Add first page
allUsers.AddRange(users.Value);
// Iterate through remaining pages
var pageIterator = PageIterator<User, UserCollectionResponse>
.CreatePageIterator(
_graphClient,
users,
user =>
{
allUsers.Add(user);
return true; // Continue iteration
});
await pageIterator.IterateAsync();
return View(allUsers);
}
アプリケーションのアクセス許可を使用する (アプリ専用トークン)
ユーザー コンテキストが必要ない場合は、アプリケーションのアクセス許可で Graph を呼び出します。
WithAppOnly() を使用して Graph を呼び出す
WithAppOnly() メソッドを使用して、アプリケーションのアクセス許可を持つ Graph 呼び出しを行います。
[Authorize]
[ApiController]
[Route("api/[controller]")]
public class AdminController : ControllerBase
{
private readonly GraphServiceClient _graphClient;
public AdminController(GraphServiceClient graphClient)
{
_graphClient = graphClient;
}
[HttpGet("users/count")]
public async Task<ActionResult<int>> GetUserCount()
{
// Get count using app permissions
var count = await _graphClient.Users.Count
.GetAsync(r => r.Options.WithAppOnly());
return Ok(count);
}
[HttpGet("applications")]
public async Task<ActionResult> GetApplications()
{
// List applications using app permissions
var apps = await _graphClient.Applications
.GetAsync(r => r.Options.WithAppOnly());
return Ok(apps.Value);
}
}
アプリケーション権限を構成する
appsettings.jsonでアプリ トークン要求を指定します。
{
"DownstreamApis": {
"MicrosoftGraph": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"RequestAppToken": true
}
}
}
スコープは自動的に ["https://graph.microsoft.com/.default"]に設定されます。
詳細なアプリ専用オプションを構成する
明示的なアプリ専用認証オプションをコードで設定します。
public async Task<IActionResult> GetApplicationsDetailed()
{
var apps = await _graphClient.Applications
.GetAsync(r =>
{
r.Options.WithAuthenticationOptions(options =>
{
// Request app token explicitly
options.RequestAppToken = true;
// Scopes automatically become [.default]
// No need to specify: options.Scopes = new[] { "https://graph.microsoft.com/.default" };
});
});
return Ok(apps);
}
複数の認証スキームを処理する
アプリで複数の認証スキーム (Web アプリ + API など) を使用する場合は、使用するスキームを指定します。
using Microsoft.AspNetCore.Authentication.JwtBearer;
[Authorize]
public class ApiDataController : ControllerBase
{
private readonly GraphServiceClient _graphClient;
public ApiDataController(GraphServiceClient graphClient)
{
_graphClient = graphClient;
}
[HttpGet("profile")]
public async Task<ActionResult> GetProfile()
{
// Specify JWT Bearer scheme
var user = await _graphClient.Me
.GetAsync(r => r.Options
.WithAuthenticationScheme(JwtBearerDefaults.AuthenticationScheme));
return Ok(user);
}
}
詳細なスキーム オプションを構成する
認証スキームとスコープをコードで明示的に設定します。
public async Task<ActionResult> GetMailWithScheme()
{
var messages = await _graphClient.Me.Messages
.GetAsync(r =>
{
r.Options.WithAuthenticationOptions(options =>
{
// Specify authentication scheme
options.AcquireTokenOptions.AuthenticationOptionsName =
JwtBearerDefaults.AuthenticationScheme;
// Specify scopes
options.Scopes = new[] { "Mail.Read" };
});
});
return Ok(messages);
}
v1.0 エンドポイントとベータ エンドポイントの両方を使用する
同じアプリケーションMicrosoft Graph v1.0 と Beta の両方を登録して呼び出します。
1. 両方のパッケージをインストールする
dotnet add package Microsoft.Identity.Web.GraphServiceClient
dotnet add package Microsoft.Identity.Web.GraphServiceClientBeta
2. 両方のサービスを登録する
using Microsoft.Identity.Web;
builder.Services.AddMicrosoftGraph();
builder.Services.AddMicrosoftGraphBeta();
3. 両方のクライアントを使用する
using GraphServiceClient = Microsoft.Graph.GraphServiceClient;
using GraphBetaServiceClient = Microsoft.Graph.Beta.GraphServiceClient;
public class MyController : Controller
{
private readonly GraphServiceClient _graphClient;
private readonly GraphBetaServiceClient _graphBetaClient;
public MyController(
GraphServiceClient graphClient,
GraphBetaServiceClient graphBetaClient)
{
_graphClient = graphClient;
_graphBetaClient = graphBetaClient;
}
public async Task<IActionResult> GetData()
{
// Use stable v1.0 endpoint
var user = await _graphClient.Me.GetAsync();
// Use beta endpoint for preview features
var profile = await _graphBetaClient.Me.Profile.GetAsync();
return View(new { user, profile });
}
}
バッチ要求を送信する
パフォーマンスを向上させるために、複数の Graph 呼び出しを 1 つの HTTP 要求に結合します。
using Microsoft.Graph.Models;
public async Task<IActionResult> GetDashboard()
{
var batchRequestContent = new BatchRequestContentCollection(_graphClient);
// Add multiple requests to batch
var userRequest = _graphClient.Me.ToGetRequestInformation();
var messagesRequest = _graphClient.Me.Messages.ToGetRequestInformation();
var eventsRequest = _graphClient.Me.Events.ToGetRequestInformation();
var userRequestId = await batchRequestContent.AddBatchRequestStepAsync(userRequest);
var messagesRequestId = await batchRequestContent.AddBatchRequestStepAsync(messagesRequest);
var eventsRequestId = await batchRequestContent.AddBatchRequestStepAsync(eventsRequest);
// Send batch request
var batchResponse = await _graphClient.Batch.PostAsync(batchRequestContent);
// Extract responses
var user = await batchResponse.GetResponseByIdAsync<User>(userRequestId);
var messages = await batchResponse.GetResponseByIdAsync<MessageCollectionResponse>(messagesRequestId);
var events = await batchResponse.GetResponseByIdAsync<EventCollectionResponse>(eventsRequestId);
return View(new DashboardViewModel
{
User = user,
Messages = messages.Value,
Events = events.Value
});
}
一般的なグラフ パターンを適用する
これらのパターンを使用して、アプリケーションで頻繁にMicrosoft Graph操作を実行します。
ユーザーのマネージャーの取得
サインインしているユーザーのマネージャーをディレクトリから取得します。
public async Task<IActionResult> GetManager()
{
var manager = await _graphClient.Me.Manager.GetAsync();
// Cast to User (manager is DirectoryObject)
if (manager is User managerUser)
{
return View(managerUser);
}
return NotFound("Manager not found");
}
ユーザーの写真を取得する
サインインしているユーザーのプロファイル写真をストリームとしてダウンロードします。
public async Task<IActionResult> GetPhoto()
{
try
{
var photoStream = await _graphClient.Me.Photo.Content.GetAsync();
return File(photoStream, "image/jpeg");
}
catch (ServiceException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return NotFound("Photo not available");
}
}
電子メールを送信する
サインインしているユーザーの代わりに電子メール メッセージを送信します。
public async Task<IActionResult> SendEmail([FromBody] EmailRequest request)
{
var message = new Message
{
Subject = request.Subject,
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = request.Body
},
ToRecipients = new List<Recipient>
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = request.ToEmail
}
}
}
};
await _graphClient.Me.SendMail
.PostAsync(new SendMailPostRequestBody
{
Message = message,
SaveToSentItems = true
},
requestConfiguration =>
{
requestConfiguration.Options.WithScopes("Mail.Send");
});
return Ok("Email sent");
}
予定表イベントを作成する
サインインしているユーザーの出席者を含む新しい予定表イベントを作成します。
public async Task<IActionResult> CreateEvent([FromBody] EventRequest request)
{
var newEvent = new Event
{
Subject = request.Subject,
Start = new DateTimeTimeZone
{
DateTime = request.StartTime.ToString("yyyy-MM-ddTHH:mm:ss"),
TimeZone = "UTC"
},
End = new DateTimeTimeZone
{
DateTime = request.EndTime.ToString("yyyy-MM-ddTHH:mm:ss"),
TimeZone = "UTC"
},
Attendees = request.Attendees.Select(email => new Attendee
{
EmailAddress = new EmailAddress { Address = email },
Type = AttendeeType.Required
}).ToList()
};
var createdEvent = await _graphClient.Me.Events
.PostAsync(newEvent, r => r.Options.WithScopes("Calendars.ReadWrite"));
return Ok(createdEvent);
}
ユーザーの検索
ディレクトリ内のユーザーを表示名または電子メール アドレスで検索します。
public async Task<IActionResult> SearchUsers(string searchTerm)
{
var users = await _graphClient.Users
.GetAsync(requestConfiguration =>
{
requestConfiguration.QueryParameters.Filter =
$"startswith(displayName,'{searchTerm}') or startswith(mail,'{searchTerm}')";
requestConfiguration.QueryParameters.Select =
new[] { "displayName", "mail", "jobTitle" };
requestConfiguration.QueryParameters.Top = 10;
requestConfiguration.Options.WithScopes("User.ReadBasic.All");
});
return Ok(users.Value);
}
OWIN のサポートを実装する
OWIN を使用する ASP.NET アプリケーションの場合は、トークン取得ファクトリを構成し、Microsoft Graphサービスを登録します。
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.OWIN;
using Owin;
public class Startup
{
public void Configuration(IAppBuilder app)
{
OwinTokenAcquirerFactory factory = TokenAcquirerFactory.GetDefaultInstance<OwinTokenAcquirerFactory>();
app.AddMicrosoftIdentityWebApi(factory);
factory.Services
.AddMicrosoftGraph();
factory.Build();
}
}
2. コントローラーから API を呼び出す
コントローラーで GraphServiceClient インスタンスを取得し、Microsoft Graphを呼び出します。
using Microsoft.Identity.Abstractions;
using Microsoft.Identity.Web;
using System.Web.Http;
[Authorize]
public class DataController : ApiController
{
public DataController()
{
}
public async Task<IHttpActionResult> GetMyProfile()
{
GraphServiceClient graphServiceClient = this.GetGraphServiceClient();
var me = await graphServiceClient.Me.GetAsync();
return Ok(me);
}
}
Microsoft.Identity.Web.MicrosoftGraph 2.x から移行します。
古い Microsoft.Identity.Web.MicrosoftGraph パッケージ (SDK 4.x) から移行する場合、以下の重要な変更を確認してください。
1. 古いパッケージを削除して新しいパッケージを追加する
dotnet remove package Microsoft.Identity.Web.MicrosoftGraph
dotnet add package Microsoft.Identity.Web.GraphServiceClient
2. メソッド呼び出しを更新する
.Request() メソッドは SDK 5.x で削除されました。
Before (SDK 4.x):
var user = await _graphClient.Me.Request().GetAsync();
var messages = await _graphClient.Me.Messages
.Request()
.WithScopes("Mail.Read")
.GetAsync();
(SDK 5.x 以降):
var user = await _graphClient.Me.GetAsync();
var messages = await _graphClient.Me.Messages
.GetAsync(r => r.Options.WithScopes("Mail.Read"));
3. WithScopes() の場所を更新する
以前:
var users = await _graphClient.Users
.Request()
.WithScopes("User.Read.All")
.GetAsync();
After:
var users = await _graphClient.Users
.GetAsync(r => r.Options.WithScopes("User.Read.All"));
4. WithAppOnly() の場所を更新する
以前:
var apps = await _graphClient.Applications
.Request()
.WithAppOnly()
.GetAsync();
After:
var apps = await _graphClient.Applications
.GetAsync(r => r.Options.WithAppOnly());
5. WithAuthenticationScheme() の場所を更新する
以前:
var user = await _graphClient.Me
.Request()
.WithAuthenticationScheme(JwtBearerDefaults.AuthenticationScheme)
.GetAsync();
After:
var user = await _graphClient.Me
.GetAsync(r => r.Options
.WithAuthenticationScheme(JwtBearerDefaults.AuthenticationScheme));
完全な移行の詳細については、Microsoft Graph .NET SDK v5 changelog を参照してください。
エラーを処理する
ServiceException の処理
ODataError と MicrosoftIdentityWebChallengeUserException をキャッチして、Graph APIエラーを適切に処理します。
using Microsoft.Graph.Models.ODataErrors;
public async Task<IActionResult> GetData()
{
try
{
var user = await _graphClient.Me.GetAsync();
return Ok(user);
}
catch (ODataError ex) when (ex.ResponseStatusCode == 404)
{
return NotFound("Resource not found");
}
catch (ODataError ex) when (ex.ResponseStatusCode == 403)
{
return Forbid("Insufficient permissions");
}
catch (MicrosoftIdentityWebChallengeUserException)
{
// User needs to consent
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Graph API call failed");
return StatusCode(500, "An error occurred");
}
}
ベスト プラクティスに従う
1. 最小スコープを要求する
必要なスコープのみ要求してください
// Bad: Requesting too many scopes
options.Scopes = new[] { "User.Read", "Mail.ReadWrite", "Calendars.ReadWrite", "Files.ReadWrite.All" };
// Good: Request only what you need
options.Scopes = new[] { "User.Read" };
2. 増分同意を使用する
必要な場合にのみ、追加のスコープを要求します。
// Sign-in: Only User.Read
// Later, when accessing mail:
var messages = await _graphClient.Me.Messages
.GetAsync(r => r.Options.WithScopes("Mail.Read"));
3. GraphServiceClient をキャッシュする
GraphServiceClient は再利用しても安全です。 シングルトンとして登録するか、DI から挿入します。
4. select を使用して応答サイズを小さくする
// Bad: Getting all properties
var users = await _graphClient.Users.GetAsync();
// Good: Select only needed properties
var users = await _graphClient.Users
.GetAsync(r => r.QueryParameters.Select =
new[] { "displayName", "mail", "id" });
一般的な問題のトラブルシューティング
"操作を完了するための特権が不足しています" を解決する
原因: アプリに必要な Graph アクセス許可がありません。
解決策:
- アプリの登録に必要な API アクセス許可を追加する
- アプリのアクセス許可に必要な管理者の同意
- 委任されたアクセス許可に必要なユーザーの同意
"AADSTS65001: ユーザーまたは管理者が同意していません" を解決する
原因: ユーザーが要求されたスコープに同意していません。
解決策: 同意フローをトリガーするには、 .WithScopes() で増分同意を使用します。
写真404エラーを解決する
原因: ユーザーにプロフィール写真がありません。
解決策: 404 を適切に処理し、既定のアバターを提供します。
バッチ要求エラーを解決する
原因: バッチ内の個々の要求が個別に失敗する可能性があります。
解決策: エラーがないか、各応答をバッチで確認します。
var userResponse = await batchResponse.GetResponseByIdAsync<User>(userRequestId);
if (userResponse == null)
{
// Handle individual request failure
}
関連するコンテンツ
次の手順: calling Azure SDK または custom API について説明します。