OWIN'i Microsoft ile tümleştirin. Identity.Web

.NET Framework 4.7.2+ üzerinde ASP.NET MVC ve Web API uygulamalarınıza Microsoft.Identity.Web.OWIN paketini kullanarak modern kimlik doğrulaması ekleyin.

OWIN tümleştirmesini anlama

Microsoft.Identity.Web.OWIN paketi, Microsoft.Identity.Web gücünü Microsoft Entra ID ile OWIN ara yazılımını kullanan ASP.NET MVC ve Web API uygulamalarına getirir.

Önemli avantajları gözden geçirme

Aşağıdaki tabloda paketin temel özellikleri ve avantajları özetlenmiştir.

Özellik Fayda
TokenAcquirerFactory Önbelleğe alma ile otomatik jeton edinme
Denetleyici Uzantıları Kolay erişim için GraphServiceClient ve IDownstreamApi
Dağıtılmış Belirteç Önbelleği SQL Server, Redis, Cosmos DB, PostgreSQL için yerleşik destek
Otomatik Belirteç Yenileme Belirteç yenilemeyi saydam olarak işler
Artımlı Onay Onay akışının sorunsuz entegrasyonu

Desteklenen senaryoları gözden geçirme

Microsoft. Identity.Web.OWIN aşağıdaki uygulama türlerini ve senaryolarını destekler.

  • ASP.NET MVC Web Applications (.NET Framework 4.7.2+)
  • ASP.NET Web API (.NET Framework 4.7.2+)
  • Karma Uygulamalar (MVC + Web API))
  • Denetleyicilerden Microsoft Graph'i Çağırma
  • Otomatik kimlik doğrulaması ile Aşağı Akış API'lerini çağırma

Paketi yükle

Microsoft.Identity.Web.OWIN NuGet paketini tercih ettiğiniz yöntemi kullanarak yükleyin.

Paket Yöneticisi Console içinde aşağıdaki komutu çalıştırın:

Install-Package Microsoft.Identity.Web.OWIN

Alternatif olarak, .NET CLI ile aşağıdaki komutu çalıştırın:

dotnet add package Microsoft.Identity.Web.OWIN

Bağımlılıklar otomatik olarak dahil:

  • Microsoft.Identity.Web.TokenAcquisition
  • Microsoft. Identity.Web.TokenCache
  • Microsoft.Owin
  • System.Web

Uygulamayı yapılandırma

Uygulama ayarlarınızı Microsoft Entra ve aşağı akış API'lerine bağlanacak şekilde yapılandırın.

Web.config yapılandırma

aşağıdaki Microsoft Entra ve aşağı akış API'si ayarlarını Web.config dosyanıza ekleyin.

<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 yapılandırma (alternatif)

Ayrıca, aşağıdaki örnekte gösterildiği gibi ayarları bir appsettings.json dosyada depolayabilirsiniz.

{
  "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"
    }
  }
}

Başlangıç sınıfını ayarlama

Kimlik doğrulama ara yazılımı, belirteç alma ve aşağı akış API hizmetlerini başlangıç sınıfınıza kaydedin.

App_Start/Startup.Auth.cs yapılandırma

Aşağıdaki kod, Microsoft.Identity.Web.OWIN ile eksiksiz bir kurulum göstermektedir.

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_";
                });
            });
            */
        }
    }
}

Denetleyicileri tümleştirme

Paket tarafından sağlanan uzantı yöntemlerini kullanarak denetleyicilerinizden Microsoft Graph ve aşağı akış API'lerine erişin.

MVC denetleyicilerini tümleştirme

Aşağıdaki örnekte, Microsoft Graph erişmek için denetleyici uzantısı yöntemlerinin nasıl kullanılacağı gösterilmektedir:

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 denetleyicilerini tümleştirme

Aşağıdaki örnekte, aşağı akış API'sini çağırmak için ApiController uzantısı yöntemlerinin nasıl kullanılacağı gösterilmektedir:

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'ı Çağır

GraphServiceClient kullanarak denetleyicilerinizden Microsoft Graph verilerle etkileşime geçin.

Microsoft Graph istemcisini ayarlama

Microsoft Graph istemcisi şu çağrıyla Startup.Auth.cs'da zaten yapılandırılmıştır:

services.AddMicrosoftGraph();

Denetleyicilerde GraphServiceClient kullanma

Aşağıdaki örnek, bir MVC denetleyicisindeki yaygın Microsoft Graph işlemlerini gösterir.

[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");
    }
}

Aşağı akış API'lerini çağırma

Denetleyicilerinizden otomatik belirteç alma ile aşağı akış API'lerini kaydedin ve çağırın.

Aşağı akış API'lerini yapılandırma

aşağı akış API'sini Startup.Auth.cs aşağıdaki kodla kaydedin:

services.AddDownstreamApi("TodoListService", configuration.GetSection("DownstreamApi:TodoListService"));

İlgili ayarları Web.config ekleyin.

<add key="DownstreamApi:TodoListService:BaseUrl" value="https://localhost:44351" />
<add key="DownstreamApi:TodoListService:Scopes" value="api://todo-api-client-id/.default" />

Denetleyicilerde IDownstreamApi kullanma

Aşağıdaki örnekte, MVC denetleyicisinden aşağı akış API'sine karşı CRUD işlemlerinin nasıl gerçekleştirıldığı gösterilmektedir.

[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");
    }
}

Örnek uygulamaları keşfetme

Aşağıdaki örnekleri kullanarak Microsoft.Identity.Web.OWIN'i iş başında görün.

Resmi Microsoft örneklerini gözden geçirin

Aşağıdaki tabloda, Microsoft.Identity.Web.OWIN tümleştirmesini gösteren resmi örnekler listelenmiştir.

Örnek Açıklama
ms-identity-aspnet-webapp-openidconnect Microsoft ile ASP.NET MVC uygulama. Identity.Web.OWIN
Anahtar Dosyaları App_Start/Startup.Auth.cs, Controllers/HomeController.cs

Aşağıdaki komutlarla örneği kopyalayın ve çalıştırın:

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

En iyi yöntemleri izleyin

Bu önerilen desenleri uygulayın ve Microsoft.Identity.Web.OWIN ile çalışırken yaygın hatalardan kaçının.

1. Üretimde dağıtılmış önbellek kullanın:

//  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. Artımlı onayı düzgün bir şekilde işleyin:

try
{
    var graphClient = this.GetGraphServiceClient();
    var user = await graphClient.Me.GetAsync();
}
catch (MsalUiRequiredException)
{
    // User needs to consent to additional scopes
    return new ChallengeResult();
}

3. Sorun giderme için bağıntı kimliklerini kullanın:

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. Uygun hata işlemeyi uygulayın:

try
{
    // Call API
}
catch (MsalUiRequiredException)
{
    return new ChallengeResult();
}
catch (HttpRequestException ex)
{
    logger.Error($"API call failed: {ex.Message}");
    return View("Error");
}

Yaygın hatalardan kaçının

1. Web grupları için bellek içi önbellek kullanmayın:

//  Wrong for load-balanced scenarios
services.AddDistributedTokenCaches(cacheServices =>
{
    cacheServices.AddDistributedMemoryCache();
});

//  Correct
services.AddDistributedTokenCaches(cacheServices =>
{
    cacheServices.AddDistributedSqlServerCache(/* ... */);
});

2. Yapılandırmayı sabit kodlamayın:

//  Wrong
ClientId = "your-client-id-here"

//  Correct
ClientId = ConfigurationManager.AppSettings["AzureAd:ClientId"]

3. Belirtecin süresinin dolmasını göz ardı etmeyin:

//  Microsoft.Identity.Web.OWIN handles this automatically
// No manual token refresh needed!

Yaygın sorunları giderme

Kurulum ve çalışma zamanı sırasında oluşan yaygın sorunlar için aşağıdaki çözümleri gözden geçirin.

Sık karşılaşılan sorunları çözme

Sorun 1: "IAuthorizationHeaderProvider bulunamıyor"

Çözüm:OwinTokenAcquirerFactory üzerinde Startup.Auth.cs kayıtlı olduğundan emin olun.

var tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();
tokenAcquirerFactory.Build(serviceProvider);
app.Use<OwinTokenAcquisitionMiddleware>(tokenAcquirerFactory);

Sorun 2: "GraphServiceClient bulunamıyor"

Çözüm:AddMicrosoftGraph() ekleyin Startup.Auth.cs:

services.AddMicrosoftGraph();

Sorun 3: Belirteç önbelleği kalıcı değil

Çözüm: Dağıtılmış önbellek yapılandırmasını doğrulayın:

services.AddDistributedTokenCaches(cacheServices =>
{
    cacheServices.AddDistributedSqlServerCache(options =>
    {
        // Ensure connection string is correct
        options.ConnectionString = ConfigurationManager.ConnectionStrings["TokenCache"].ConnectionString;
    });
});

İlgili özellikler ve senaryolar hakkında daha fazla bilgi edinin.