Microsoft.Identity.Web.OWIN パッケージを使用して、.NET Framework 4.7.2 以降の ASP.NET MVC および Web API アプリケーションにモダン認証を追加します。
OWIN 統合について
Microsoft.Identity.Web.OWIN パッケージは、Microsoft.Identity.Webの強力な機能を Microsoft Entra IDを使用する OWIN ミドルウェアを備えた ASP.NET MVC および Web API アプリケーションに提供します。
主な利点を確認する
次の表は、パッケージの主な機能と利点をまとめたものです。
| 特徴 | 給付金 |
|---|---|
| TokenAcquirerFactory | キャッシュを使用したトークンの自動取得 |
| コントローラー拡張機能 |
GraphServiceClientへの容易なアクセスおよびIDownstreamApi |
| 分散トークン キャッシュ | SQL Server、Redis、Cosmos DB、PostgreSQL の組み込みサポート |
| トークンの自動更新 | トークンの更新を透過的に処理する |
| 段階的同意 | シームレスな同意フローの統合 |
サポートされているシナリオを確認する
Microsoft。Identity.Web.OWIN では、次のアプリケーションの種類とシナリオがサポートされています。
- ASP.NET MVC Web アプリケーション (.NET Framework 4.7.2 以降)
- ASP.NET Web API (.NET Framework 4.7.2 以降)
- ハイブリッド アプリ (MVC + Web API)
- コントローラーからのMicrosoft Graphの呼び出し
- 自動認証を使用したダウンストリーム API の呼び出し
パッケージをインストールする
Microsoft.Identity.Web.OWIN NuGet パッケージをお好みの方法でインストールします。
パッケージ マネージャー コンソールで次のコマンドを実行します。
Install-Package Microsoft.Identity.Web.OWIN
または、.NET CLI で次のコマンドを実行します。
dotnet add package Microsoft.Identity.Web.OWIN
依存関係は自動的に含まれます。
- Microsoft.Identity.Web.TokenAcquisition
- Microsoft.Identity.Web.TokenCache
- Microsoft。Owin
- System.web
アプリケーションの構成
Microsoft Entraおよびダウンストリーム API に接続するようにアプリケーション設定を構成します。
Web.config の構成
Web.config ファイルに、次のMicrosoft Entraとダウンストリーム API の設定を追加します。
<configuration>
<appSettings>
<!-- Microsoft Entra ID Configuration -->
<add key="AzureAd:Instance" value="https://login.microsoftonline.com/" />
<add key="AzureAd:TenantId" value="your-tenant-id" />
<add key="AzureAd:ClientId" value="your-client-id" />
<add key="AzureAd:ClientSecret" value="your-client-secret" />
<add key="AzureAd:RedirectUri" value="https://localhost:44368/" />
<add key="AzureAd:PostLogoutRedirectUri" value="https://localhost:44368/" />
<!-- Microsoft Graph Configuration -->
<add key="DownstreamApi:MicrosoftGraph:BaseUrl" value="https://graph.microsoft.com/v1.0" />
<add key="DownstreamApi:MicrosoftGraph:Scopes" value="user.read" />
<!-- Custom Downstream API Configuration -->
<add key="DownstreamApi:TodoListService:BaseUrl" value="https://localhost:44351" />
<add key="DownstreamApi:TodoListService:Scopes" value="api://todo-api-client-id/.default" />
</appSettings>
<connectionStrings>
<!-- Optional: SQL Server Token Cache -->
<add name="TokenCache"
connectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=TokenCache;Integrated Security=True;" />
</connectionStrings>
</configuration>
appsettings.json の構成 (代替)
次の例に示すように、 appsettings.json ファイルに設定を格納することもできます。
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-client-id",
"ClientSecret": "your-client-secret",
"RedirectUri": "https://localhost:44368/",
"PostLogoutRedirectUri": "https://localhost:44368/"
},
"DownstreamApi": {
"MicrosoftGraph": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "user.read"
},
"TodoListService": {
"BaseUrl": "https://localhost:44351",
"Scopes": "api://todo-api-client-id/.default"
}
}
}
スタートアップ クラスを設定する
認証ミドルウェア、トークン取得、およびダウンストリーム API サービスをスタートアップ クラスに登録します。
App_Start/Startup.Auth.csの構成
次のコードは、Microsoft.Identity.Web.OWIN を使用した完全なセットアップを示しています。
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.OWIN;
using Microsoft.Identity.Web.TokenCacheProviders.Distributed;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;
using System;
using System.Configuration;
using System.Web;
namespace MyMvcApp
{
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Set default authentication type
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
// Configure cookie authentication
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieName = "MyApp.Auth",
ExpireTimeSpan = TimeSpan.FromHours(1),
SlidingExpiration = true
});
// Configure OpenID Connect authentication
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = ConfigurationManager.AppSettings["AzureAd:ClientId"],
Authority = $"https://login.microsoftonline.com/{ConfigurationManager.AppSettings["AzureAd:TenantId"]}",
RedirectUri = ConfigurationManager.AppSettings["AzureAd:RedirectUri"],
PostLogoutRedirectUri = ConfigurationManager.AppSettings["AzureAd:PostLogoutRedirectUri"],
Scope = "openid profile email offline_access",
ResponseType = "code id_token",
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
NameClaimType = "preferred_username"
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
}
}
});
// Configure Microsoft Identity Web services
var services = CreateOwinServiceCollection();
// Add token acquisition
services.AddTokenAcquisition();
// Add Microsoft Graph support
services.AddMicrosoftGraph();
// Add downstream API support
services.AddDownstreamApi("MicrosoftGraph", services.BuildServiceProvider()
.GetRequiredService<IConfiguration>().GetSection("DownstreamApi:MicrosoftGraph"));
services.AddDownstreamApi("TodoListService", services.BuildServiceProvider()
.GetRequiredService<IConfiguration>().GetSection("DownstreamApi:TodoListService"));
// Configure token cache (choose one option)
ConfigureTokenCache(services);
// Build service provider
var serviceProvider = services.BuildServiceProvider();
// Create and register token acquirer factory
var tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();
tokenAcquirerFactory.Build(serviceProvider);
// Add OWIN token acquisition middleware
app.Use<OwinTokenAcquisitionMiddleware>(tokenAcquirerFactory);
}
private IServiceCollection CreateOwinServiceCollection()
{
var services = new ServiceCollection();
// Add configuration from appsettings.json and/or Web.config
IConfiguration configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true)
.AddInMemoryCollection(new Dictionary<string, string>
{
["AzureAd:Instance"] = ConfigurationManager.AppSettings["AzureAd:Instance"],
["AzureAd:TenantId"] = ConfigurationManager.AppSettings["AzureAd:TenantId"],
["AzureAd:ClientId"] = ConfigurationManager.AppSettings["AzureAd:ClientId"],
["AzureAd:ClientSecret"] = ConfigurationManager.AppSettings["AzureAd:ClientSecret"],
["DownstreamApi:MicrosoftGraph:BaseUrl"] = ConfigurationManager.AppSettings["DownstreamApi:MicrosoftGraph:BaseUrl"],
["DownstreamApi:MicrosoftGraph:Scopes"] = ConfigurationManager.AppSettings["DownstreamApi:MicrosoftGraph:Scopes"],
})
.Build();
services.AddSingleton(configuration);
return services;
}
private void ConfigureTokenCache(IServiceCollection services)
{
// Option 1: In-memory cache (development)
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddDistributedMemoryCache();
});
// Option 2: SQL Server cache (production)
/*
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = ConfigurationManager.ConnectionStrings["TokenCache"].ConnectionString;
options.SchemaName = "dbo";
options.TableName = "TokenCache";
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});
});
*/
// Option 3: Redis cache (production, high-scale)
/*
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddStackExchangeRedisCache(options =>
{
options.Configuration = ConfigurationManager.AppSettings["Redis:ConnectionString"];
options.InstanceName = "MyMvcApp_";
});
});
*/
}
}
}
コントローラーを統合する
パッケージによって提供される拡張メソッドを使用して、コントローラーからMicrosoft Graphおよびダウンストリーム API にアクセスします。
MVC コントローラーを統合する
次の例は、コントローラー拡張メソッドを使用してMicrosoft Graphにアクセスする方法を示しています。
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.OWIN;
using Microsoft.Graph;
using System.Threading.Tasks;
using System.Web.Mvc;
namespace MyMvcApp.Controllers
{
[Authorize]
public class HomeController : Controller
{
// GET: Home/Index
public async Task<ActionResult> Index()
{
try
{
// Access Microsoft Graph using extension method
var graphClient = this.GetGraphServiceClient();
var user = await graphClient.Me.GetAsync();
ViewBag.UserName = user.DisplayName;
ViewBag.Email = user.Mail ?? user.UserPrincipalName;
ViewBag.JobTitle = user.JobTitle;
return View();
}
catch (MsalUiRequiredException)
{
// Incremental consent required
return new ChallengeResult();
}
catch (Exception ex)
{
return View("Error", new ErrorViewModel { Message = ex.Message });
}
}
// GET: Home/Profile
public async Task<ActionResult> Profile()
{
var graphClient = this.GetGraphServiceClient();
// Get user profile
var user = await graphClient.Me
.GetAsync(requestConfig => requestConfig.QueryParameters.Select = new[] { "displayName", "mail", "jobTitle", "department" });
return View(user);
}
// GET: Home/Photo
public async Task<ActionResult> Photo()
{
var graphClient = this.GetGraphServiceClient();
try
{
// Get user photo
var photoStream = await graphClient.Me.Photo.Content.GetAsync();
return File(photoStream, "image/jpeg");
}
catch (ServiceException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return File(Server.MapPath("~/Content/images/default-user.png"), "image/png");
}
}
}
}
Web API コントローラーを統合する
次の例は、ApiController 拡張メソッドを使用してダウンストリーム API を呼び出す方法を示しています。
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.OWIN;
using Microsoft.Identity.Abstractions;
using System.Threading.Tasks;
using System.Web.Http;
namespace MyWebApi.Controllers
{
[Authorize]
[RoutePrefix("api/todos")]
public class TodoController : ApiController
{
// GET: api/todos
[HttpGet]
[Route("")]
public async Task<IHttpActionResult> GetTodos()
{
try
{
// Call downstream API using extension method
var downstreamApi = this.GetDownstreamApi();
var todos = await downstreamApi.GetForUserAsync<List<TodoItem>>(
"TodoListService",
options =>
{
options.RelativePath = "api/todolist";
});
return Ok(todos);
}
catch (MsalUiRequiredException)
{
return Unauthorized();
}
catch (HttpRequestException ex)
{
return InternalServerError(ex);
}
}
// POST: api/todos
[HttpPost]
[Route("")]
public async Task<IHttpActionResult> CreateTodo([FromBody] TodoItem todo)
{
var downstreamApi = this.GetDownstreamApi();
var createdTodo = await downstreamApi.PostForUserAsync<TodoItem, TodoItem>(
"TodoListService",
todo,
options =>
{
options.RelativePath = "api/todolist";
});
return Created($"api/todos/{createdTodo.Id}", createdTodo);
}
}
}
Microsoft Graph を呼び出す
GraphServiceClientを使用して、コントローラーからMicrosoft Graphデータを操作します。
Microsoft Graph クライアントを設定する
Microsoft Graph クライアントは、次の呼び出しで Startup.Auth.cs で既に構成されています。
services.AddMicrosoftGraph();
コントローラーで GraphServiceClient を使用する
次の例は、MVC コントローラーでの一般的なMicrosoft Graph操作を示しています。
[Authorize]
public class GraphController : Controller
{
public async Task<ActionResult> MyProfile()
{
var graphClient = this.GetGraphServiceClient();
var user = await graphClient.Me.GetAsync();
return View(user);
}
public async Task<ActionResult> MyManager()
{
var graphClient = this.GetGraphServiceClient();
var manager = await graphClient.Me.Manager.GetAsync();
return View(manager);
}
public async Task<ActionResult> MyDirectReports()
{
var graphClient = this.GetGraphServiceClient();
var directReports = await graphClient.Me.DirectReports.GetAsync();
return View(directReports.Value);
}
public async Task<ActionResult> SendEmail([FromBody] EmailMessage message)
{
var graphClient = this.GetGraphServiceClient();
var email = new Message
{
Subject = message.Subject,
Body = new ItemBody
{
ContentType = BodyType.Text,
Content = message.Body
},
ToRecipients = new[]
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = message.To
}
}
}
};
await graphClient.Me.SendMail.PostAsync(new SendMailPostRequestBody
{
Message = email
});
return RedirectToAction("Index");
}
}
ダウンストリーム API を呼び出す
コントローラーからのトークンの自動取得を使用してダウンストリーム API を登録して呼び出します。
ダウンストリーム API を構成する
次のコードを使用して、 Startup.Auth.cs のダウンストリーム API を登録します。
services.AddDownstreamApi("TodoListService", configuration.GetSection("DownstreamApi:TodoListService"));
Web.configに対応する設定を追加します。
<add key="DownstreamApi:TodoListService:BaseUrl" value="https://localhost:44351" />
<add key="DownstreamApi:TodoListService:Scopes" value="api://todo-api-client-id/.default" />
コントローラーで IDownstreamApi を使用する
次の例は、MVC コントローラーからダウンストリーム API に対して CRUD 操作を実行する方法を示しています。
[Authorize]
public class TodoController : Controller
{
// GET all todos
public async Task<ActionResult> Index()
{
var downstreamApi = this.GetDownstreamApi();
var todos = await downstreamApi.GetForUserAsync<List<TodoItem>>(
"TodoListService",
options =>
{
options.RelativePath = "api/todolist";
});
return View(todos);
}
// GET specific todo
public async Task<ActionResult> Details(int id)
{
var downstreamApi = this.GetDownstreamApi();
var todo = await downstreamApi.GetForUserAsync<TodoItem>(
"TodoListService",
options =>
{
options.RelativePath = $"api/todolist/{id}";
});
return View(todo);
}
// POST new todo
[HttpPost]
public async Task<ActionResult> Create(TodoItem todo)
{
var downstreamApi = this.GetDownstreamApi();
var createdTodo = await downstreamApi.PostForUserAsync<TodoItem, TodoItem>(
"TodoListService",
todo,
options =>
{
options.RelativePath = "api/todolist";
});
return RedirectToAction("Index");
}
// PUT update todo
[HttpPost]
public async Task<ActionResult> Edit(int id, TodoItem todo)
{
var downstreamApi = this.GetDownstreamApi();
await downstreamApi.CallApiForUserAsync(
"TodoListService",
options =>
{
options.HttpMethod = HttpMethod.Put;
options.RelativePath = $"api/todolist/{id}";
options.RequestBody = todo;
});
return RedirectToAction("Index");
}
// DELETE todo
[HttpPost]
public async Task<ActionResult> Delete(int id)
{
var downstreamApi = this.GetDownstreamApi();
await downstreamApi.CallApiForUserAsync(
"TodoListService",
options =>
{
options.HttpMethod = HttpMethod.Delete;
options.RelativePath = $"api/todolist/{id}";
});
return RedirectToAction("Index");
}
}
サンプル アプリケーションを調べる
次のサンプルを使用して Microsoft.Identity.Web.OWIN の動作を確認してください。
公式のMicrosoftサンプルを確認する
次の表は、Microsoft.Identity.Web.OWIN の統合を示す公式サンプルをリストしています。
| サンプル | 説明 |
|---|---|
| ms-identity-aspnet-webapp-openidconnect | Microsoft.Identity.Web.OWINを使用したASP.NET MVCアプリケーション |
| 重要ファイル |
App_Start/Startup.Auth.cs、Controllers/HomeController.cs |
次のコマンドを使用してサンプルを複製して実行します。
git clone https://github.com/Azure-Samples/ms-identity-aspnet-webapp-openidconnect
cd ms-identity-aspnet-webapp-openidconnect
# Update Web.config with your Microsoft Entra app registration
# Run in Visual Studio
ベスト プラクティスに従う
Microsoft.Identity.Web.OWINを使用してアプリケーションを構築する際には、これらの推奨されるパターンを適用して、一般的な間違いを回避してください。
推奨されるパターンを適用する
1. 運用環境で分散キャッシュを使用する:
// Production
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = ConfigurationManager.ConnectionStrings["TokenCache"].ConnectionString;
options.SchemaName = "dbo";
options.TableName = "TokenCache";
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});
});
2. 増分同意を適切に処理します。
try
{
var graphClient = this.GetGraphServiceClient();
var user = await graphClient.Me.GetAsync();
}
catch (MsalUiRequiredException)
{
// User needs to consent to additional scopes
return new ChallengeResult();
}
3. トラブルシューティングに関連付け ID を使用します。
var downstreamApi = this.GetDownstreamApi();
var correlationId = Guid.NewGuid();
var result = await downstreamApi.GetForUserAsync<Todo>(
"TodoListService",
options =>
{
options.RelativePath = $"api/todolist/{id}";
options.TokenAcquisitionOptions = new TokenAcquisitionOptions
{
CorrelationId = correlationId
};
});
4. 適切なエラー処理を実装します。
try
{
// Call API
}
catch (MsalUiRequiredException)
{
return new ChallengeResult();
}
catch (HttpRequestException ex)
{
logger.Error($"API call failed: {ex.Message}");
return View("Error");
}
一般的な間違いを避ける
1. Web ファームにメモリ内キャッシュを使用しないでください。
// Wrong for load-balanced scenarios
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddDistributedMemoryCache();
});
// Correct
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddDistributedSqlServerCache(/* ... */);
});
2. 構成をハードコードしないでください。
// Wrong
ClientId = "your-client-id-here"
// Correct
ClientId = ConfigurationManager.AppSettings["AzureAd:ClientId"]
3. トークンの有効期限を無視しないでください。
// Microsoft.Identity.Web.OWIN handles this automatically
// No manual token refresh needed!
一般的な問題のトラブルシューティング
セットアップと実行時に発生する一般的な問題については、次の解決策を確認してください。
一般的な問題を解決
問題 1: "IAuthorizationHeaderProvider が見つかりません"
ソリューション:OwinTokenAcquirerFactoryがStartup.Auth.csに登録されていることを確認します。
var tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();
tokenAcquirerFactory.Build(serviceProvider);
app.Use<OwinTokenAcquisitionMiddleware>(tokenAcquirerFactory);
問題 2: "GraphServiceClient が見つかりません"
ソリューション:AddMicrosoftGraph()にStartup.Auth.csを追加します。
services.AddMicrosoftGraph();
問題 3: トークン キャッシュが保持されない
ソリューション: 分散キャッシュの構成を確認します。
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddDistributedSqlServerCache(options =>
{
// Ensure connection string is correct
options.ConnectionString = ConfigurationManager.ConnectionStrings["TokenCache"].ConnectionString;
});
});
関連コンテンツを調べる
関連する機能とシナリオの詳細を確認します。