Microsoft ile kimlik doğrulamayı özelleştirin. Identity.Web

Microsoft. Identity.Web, Microsoft Entra ID ile tümleşen ASP.NET Core uygulamalarda kimlik doğrulaması ve yetkilendirme için güvenli varsayılanlar sağlar. Kitaplığın yerleşik güvenlik özelliklerini korurken kimlik doğrulama davranışının birçok yönünü özelleştirebilirsiniz.

Özelleştirilebilir alanları belirleme

Alan Özelleştirme Seçenekleri
Configuration Tüm MicrosoftIdentityOptions, OpenIdConnectOptions, JwtBearerOptions özellikleri
Etkinlikler OpenID Connect olayları (OnTokenValidated, OnRedirectToIdentityProvider, vb.)
Belirteç Alma Bağıntı kimlikleri, ek sorgu parametreleri
Claims Özel talepleri ClaimsPrincipal öğesine ekle
Kullanıcı Arabirimi Oturumu kapatma sayfaları ve yeniden yönlendirme davranışı
Oturum Açma Oturum açma ipuçları, domain ipuçları

Özelleştirme yöntemi seçme

Aşağıdaki tabloda özelleştirebileceğiniz alanlar ve her alanın desteklediği alanlar özetlemektedir.

Seçenekleri özelleştirmek için iki yaklaşımdan birini kullanın:

  1. Configure<TOptions> - Seçenekleri kullanılmadan önce yapılandırıyor
  2. PostConfigure<TOptions> - Tüm Configure çağrılardan sonra seçenekleri yapılandırıyor

Yürütme sırası:

Configure → Configure → ... → PostConfigure → PostConfigure → ... → Options used

Kimlik doğrulama seçeneklerini yapılandırma

Bu bölüm, Microsoft.Identity.Web tarafından kullanılan çeşitli kimlik doğrulama seçeneği sınıflarının nasıl yapılandırıldığını gösterir.

Yapılandırma eşlemesini anlama

içindeki "AzureAd" bölüm appsettings.json birden çok sınıfla eşler:

Yapılandırmanızda bu sınıflardan herhangi bir özelliği kullanabilirsiniz.

Desen 1: MicrosoftIdentityOptions'ı yapılandırma

Aşağıdaki kod, MicrosoftIdentityOptions, PII günlüğünü etkinleştirmek, istemci özelliklerini ayarlamak ve token doğrulama parametrelerini ayarlamak için özelleştirilir:

using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));

// Customize Microsoft Identity options
builder.Services.Configure<MicrosoftIdentityOptions>(options =>
{
    // Enable PII logging (development only!)
    options.EnablePiiLogging = true;

    // Custom client capabilities
    options.ClientCapabilities = new[] { "CP1", "CP2" };

    // Override token validation parameters
    options.TokenValidationParameters.ValidateLifetime = true;
    options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(5);
});

var app = builder.Build();

Desen 2: OpenIdConnectOptions'ı yapılandırma (Web uygulamaları)

Aşağıdaki kod, yanıt türünü ayarlamak, kapsamlar eklemek ve tanımlama bilgisi ile belirteç doğrulama ayarlarını yapılandırmak üzere bir web uygulaması için özelleştirilir OpenIdConnectOptions :

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));

// Customize OpenIdConnect options
builder.Services.Configure<OpenIdConnectOptions>(
    OpenIdConnectDefaults.AuthenticationScheme,
    options =>
{
    // Override response type
    options.ResponseType = "code id_token";

    // Add extra scopes
    options.Scope.Add("offline_access");
    options.Scope.Add("profile");

    // Customize token validation
    options.TokenValidationParameters.NameClaimType = "preferred_username";
    options.TokenValidationParameters.RoleClaimType = "roles";

    // Set redirect URI
    options.CallbackPath = "/signin-oidc";

    // Configure cookie options
    options.Cookie.HttpOnly = true;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.SameSite = SameSiteMode.Lax;
});

Desen 3: JwtBearerOptions'ı (Web API'leri) yapılandırma

tr-TR: Aşağıdaki kod, bir web API'si için JwtBearerOptions'yi geçerli kullanıcıları, talep eşlemelerini ve jeton süresi doğrulamasını ayarlamak üzere özelleştirir.

using Microsoft.AspNetCore.Authentication.JwtBearer;

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

// Customize JWT Bearer options
builder.Services.Configure<JwtBearerOptions>(
    JwtBearerDefaults.AuthenticationScheme,
    options =>
{
    // Customize audience validation
    options.TokenValidationParameters.ValidAudiences = new[]
    {
        "api://your-api-client-id",
        "https://your-api.com"
    };

    // Set custom claim mappings
    options.TokenValidationParameters.NameClaimType = "name";
    options.TokenValidationParameters.RoleClaimType = "roles";

    // Customize token validation
    options.TokenValidationParameters.ValidateLifetime = true;
    options.TokenValidationParameters.ClockSkew = TimeSpan.Zero; // No tolerance
});

Aşağıdaki kod, güvenlik ayarları ve son kullanma davranışı dahil olmak üzere uygulamanız için çerez politikası ve kimlik doğrulama seçeneklerini yapılandırıyor.

using Microsoft.AspNetCore.Authentication.Cookies;

// Configure cookie policy
builder.Services.Configure<CookiePolicyOptions>(options =>
{
    options.MinimumSameSitePolicy = SameSiteMode.Lax;
    options.Secure = CookieSecurePolicy.Always;
    options.HttpOnly = Microsoft.AspNetCore.CookiePolicy.HttpOnlyPolicy.Always;
});

// Configure cookie authentication options
builder.Services.Configure<CookieAuthenticationOptions>(
    CookieAuthenticationDefaults.AuthenticationScheme,
    options =>
{
    options.Cookie.Name = "MyApp.Auth";
    options.Cookie.HttpOnly = true;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.SameSite = SameSiteMode.Lax;
    options.ExpireTimeSpan = TimeSpan.FromHours(1);
    options.SlidingExpiration = true;
});

Olay işleyicilerini özelleştirme

OpenID Connect ve JWT Taşıyıcı kimlik doğrulaması, bağlanabileceğiniz olayları kullanıma sunar. Microsoft. Identity.Web kendi olay işleyicilerini ayarlar, bu nedenle yerleşik işlevselliği korumak için özel işleyicilerinizi mevcut işleyicilerle zincirlemeniz gerekir.

Mevcut işleyicileri koruyun

Özel olay işleyicileri eklediğinizde, her zaman önce mevcut işleyiciyi kaydedin ve çağırın. Aşağıdaki örnekte yanlış ve doğru yaklaşımlar gösterilmektedir.

Aşağıdaki kod yanlış bir şekilde Microsoft.Identity.Web işleyicisini üzerine yazar.

services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.Events.OnTokenValidated = async context =>
    {
        // Your code - but you LOST the built-in validation!
        await Task.CompletedTask;
    };
});

Aşağıdaki kod, mevcut işleyiciyle doğru şekilde zincirler:

services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
    var existingOnTokenValidatedHandler = options.Events.OnTokenValidated;

    options.Events.OnTokenValidated = async context =>
    {
        // Call Microsoft.Identity.Web's handler FIRST
        await existingOnTokenValidatedHandler(context);

        // Then your custom code
        // (executes AFTER built-in security checks)
        var identity = context.Principal.Identity as ClaimsIdentity;
        identity?.AddClaim(new Claim("custom_claim", "custom_value"));
    };
});

Yaygın olay senaryolarını uygulama

Belirteç doğrulaması sonrasında özel talepler ekleme

Aşağıdaki kod, bir web API'sinde belirteç doğrulamasından sonra öğesine ClaimsPrincipal özel talepler ekler. Veritabanından kullanıcının bölümünü arar ve e-posta etki alanına göre uygulamaya özgü bir rol atar:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Security.Claims;

builder.Services.Configure<JwtBearerOptions>(
    JwtBearerDefaults.AuthenticationScheme,
    options =>
{
    var existingHandler = options.Events.OnTokenValidated;

    options.Events.OnTokenValidated = async context =>
    {
        // Preserve built-in validation
        await existingHandler(context);

        // Add custom claims
        var identity = context.Principal.Identity as ClaimsIdentity;

        // Example: Add department claim from database
        var userObjectId = context.Principal.FindFirst("oid")?.Value;
        if (!string.IsNullOrEmpty(userObjectId))
        {
            var department = await GetUserDepartment(userObjectId);
            identity?.AddClaim(new Claim("department", department));
        }

        // Example: Add application-specific role
        var email = context.Principal.FindFirst("email")?.Value;
        if (email?.EndsWith("@admin.com") == true)
        {
            identity?.AddClaim(new Claim(ClaimTypes.Role, "SuperAdmin"));
        }
    };
});

Aşağıdaki kod, belirteç doğrulamasından sonra ek kullanıcı profili verilerini almak için Microsoft Graph çağırarak bir web uygulamasına özel talepler ekler:

using Microsoft.AspNetCore.Authentication.OpenIdConnect;

builder.Services.Configure<OpenIdConnectOptions>(
    OpenIdConnectDefaults.AuthenticationScheme,
    options =>
{
    var existingHandler = options.Events.OnTokenValidated;

    options.Events.OnTokenValidated = async context =>
    {
        // Preserve built-in processing
        await existingHandler(context);

        // Call Microsoft Graph to get additional user data
        var graphClient = context.HttpContext.RequestServices
            .GetRequiredService<GraphServiceClient>();

        var user = await graphClient.Me.GetAsync();

        var identity = context.Principal.Identity as ClaimsIdentity;
        identity?.AddClaim(new Claim("jobTitle", user?.JobTitle ?? ""));
        identity?.AddClaim(new Claim("department", user?.Department ?? ""));
    };
});

Yetkilendirme isteğine sorgu parametreleri ekleme

Aşağıdaki kod, Microsoft Entra kimlik sağlayıcısına gönderilen yetkilendirme isteğine özel sorgu parametreleri ekler:

builder.Services.Configure<OpenIdConnectOptions>(
    OpenIdConnectDefaults.AuthenticationScheme,
    options =>
{
    var existingHandler = options.Events.OnRedirectToIdentityProvider;

    options.Events.OnRedirectToIdentityProvider = async context =>
    {
        // Preserve existing behavior
        if (existingHandler != null)
        {
            await existingHandler(context);
        }

        // Add custom query parameters
        context.ProtocolMessage.Parameters.Add("slice", "testslice");
        context.ProtocolMessage.Parameters.Add("custom_param", "custom_value");

        // Conditional parameters based on request
        if (context.HttpContext.Request.Query.ContainsKey("prompt"))
        {
            context.ProtocolMessage.Prompt = context.HttpContext.Request.Query["prompt"];
        }
    };
});

Kimlik doğrulama hatasını işlemeyi özelleştirme

Aşağıdaki kod, hatayı günlüğe kaydetme ve özel bir JSON hata yanıtı döndürme yoluyla kimlik doğrulama hatalarını işler:

builder.Services.Configure<OpenIdConnectOptions>(
    OpenIdConnectDefaults.AuthenticationScheme,
    options =>
{
    options.Events.OnAuthenticationFailed = async context =>
    {
        // Log the error
        var logger = context.HttpContext.RequestServices
            .GetRequiredService<ILogger<Program>>();
        logger.LogError(context.Exception, "Authentication failed");

        // Customize error response
        context.Response.StatusCode = 401;
        context.Response.ContentType = "application/json";
        await context.Response.WriteAsync($$"""
            {
                "error": "authentication_failed",
                "error_description": "{{context.Exception.Message}}"
            }
            """);

        context.HandleResponse(); // Suppress default error handling
    };
});

Erişim reddedildi durumunu yönet

Aşağıdaki kod, onay vermediğinde kullanıcıları özel bir sayfaya yönlendirir:

builder.Services.Configure<OpenIdConnectOptions>(
    OpenIdConnectDefaults.AuthenticationScheme,
    options =>
{
    options.Events.OnAccessDenied = async context =>
    {
        // User denied consent
        context.Response.Redirect("/Home/AccessDenied");
        context.HandleResponse();
        await Task.CompletedTask;
    };
});

Jeton alımını özelleştir

C0 /> içine seçenekleri geçirerek aşağı akış API'leri çağrılırken belirteçlerin nasıl alınacağını özelleştirebilirsiniz.

Özel seçeneklerle IDownstreamApi kullanma

Token alırken ilişkili kimlik ve ek sorgu parametrelerini IDownstreamApi üzerinden geçiren aşağıdaki kod:

using Microsoft.Identity.Abstractions;

public class TodoListController : ControllerBase
{
    private readonly IDownstreamApi _downstreamApi;

    public TodoListController(IDownstreamApi downstreamApi)
    {
        _downstreamApi = downstreamApi;
    }

    [HttpGet("{id}")]
    public async Task<ActionResult> GetTodo(int id, Guid correlationId)
    {
        var result = await _downstreamApi.GetForUserAsync<Todo>(
            "TodoListService",
            options =>
            {
                options.RelativePath = $"api/todolist/{id}";

                // Customize token acquisition
                options.TokenAcquisitionOptions = new TokenAcquisitionOptions
                {
                    CorrelationId = correlationId,
                    ExtraQueryParameters = new Dictionary<string, string>
                    {
                        { "slice", "test_slice" }
                    }
                };
            });

        return Ok(result);
    }
}

Kullanıcı arabirimini özelleştirme

Kullanıcıların oturum açma ve oturum kapatma sonrasında nereye inebileceğini denetleyebilir ve oturum kapatma deneyimini özelleştirebilirsiniz.

Oturum açmadan sonra belirli bir sayfaya yeniden yönlendirme

redirectUri Oturum açtıktan sonra kullanıcıları belirli bir sayfaya göndermek için parametresini kullanın:

<!-- Razor view -->
<a href="/MicrosoftIdentity/Account/SignIn?redirectUri=/Dashboard">Sign In</a>

<!-- Or in controller -->
[HttpGet]
public IActionResult SignInToDashboard()
{
    return RedirectToAction("SignIn", "Account", new
    {
        area = "MicrosoftIdentity",
        redirectUri = "/Dashboard"
    });
}

Oturum kapatma sayfasını özelleştirme

1. Seçenek: Razor Sayfasını Geçersiz Kılma

Özel içeriğiniz ile adresinde Areas/MicrosoftIdentity/Pages/Account/SignedOut.cshtml bir dosya oluşturun:

@page
@model Microsoft.Identity.Web.UI.Areas.MicrosoftIdentity.Pages.Account.SignedOutModel
@{
    ViewData["Title"] = "Signed out";
}

<div class="container text-center mt-5">
    <h1>You have been signed out</h1>
    <p>Thank you for using our application.</p>
    <a asp-area="" asp-controller="Home" asp-action="Index" class="btn btn-primary">
        Return to Home
    </a>
</div>

2. Seçenek: Özel bir sayfaya yeniden yönlendirme

Aşağıdaki kod, kullanıcıları varsayılan yerine özel bir oturum kapatma sayfasına yönlendirir:

builder.Services.Configure<OpenIdConnectOptions>(
    OpenIdConnectDefaults.AuthenticationScheme,
    options =>
{
    options.Events.OnSignedOutCallbackRedirect = context =>
    {
        context.Response.Redirect("/Home/SignedOut");
        context.HandleResponse();
        return Task.CompletedTask;
    };
});

Oturum açma deneyimini özelleştirme

Oturum açma ipuçlarını ve alan ipuçlarını kullan

Kullanıcı adlarını önceden doldurarak ve kullanıcıları belirli Microsoft Entra kiracılara yönlendirerek oturum açma deneyimini kolaylaştırın.

İpuçlarını anlama

İpucu Amaç Example
loginHint Kullanıcı adı/e-posta alanını önceden doldurma "user@contoso.com"
domainHint Belirli bir kiracı oturum açma sayfasına doğrudan "contoso.com"

İpucu desenlerini uygulama

Desen 1: Denetleyici tabanlı

Aşağıdaki kod, standart oturum açma, oturum açma ipucu veya etki alanı ipucu ya da her ikisini kullanarak oturum açma için denetleyicinin eylemlerini gösterir.

using Microsoft.AspNetCore.Mvc;

public class AuthController : Controller
{
    [HttpGet]
    public IActionResult SignIn()
    {
        // Standard sign-in
        return RedirectToAction("SignIn", "Account", new
        {
            area = "MicrosoftIdentity",
            redirectUri = "/Dashboard"
        });
    }

    [HttpGet]
    public IActionResult SignInWithLoginHint()
    {
        // Pre-populate username
        return RedirectToAction("SignIn", "Account", new
        {
            area = "MicrosoftIdentity",
            redirectUri = "/Dashboard",
            loginHint = "user@contoso.com"
        });
    }

    [HttpGet]
    public IActionResult SignInWithDomainHint()
    {
        // Direct to Contoso tenant
        return RedirectToAction("SignIn", "Account", new
        {
            area = "MicrosoftIdentity",
            redirectUri = "/Dashboard",
            domainHint = "contoso.com"
        });
    }

    [HttpGet]
    public IActionResult SignInWithBothHints()
    {
        // Pre-populate AND direct to tenant
        return RedirectToAction("SignIn", "Account", new
        {
            area = "MicrosoftIdentity",
            redirectUri = "/Dashboard",
            loginHint = "user@contoso.com",
            domainHint = "contoso.com"
        });
    }
}

Desen 2: Görünüm tabanlı

Aşağıdaki HTML, farklı ipucu yapılandırmalarına sahip oturum açma bağlantılarını gösterir:

<div class="sign-in-options">
    <h2>Sign In Options</h2>

    <!-- Standard sign-in -->
    <a href="/MicrosoftIdentity/Account/SignIn?redirectUri=/Dashboard"
       class="btn btn-primary">
        Sign In
    </a>

    <!-- With login hint -->
    <a href="/MicrosoftIdentity/Account/SignIn?redirectUri=/Dashboard&loginHint=user@contoso.com"
       class="btn btn-secondary">
        Sign In as user@contoso.com
    </a>

    <!-- With domain hint -->
    <a href="/MicrosoftIdentity/Account/SignIn?redirectUri=/Dashboard&domainHint=contoso.com"
       class="btn btn-secondary">
        Sign In (Contoso)
    </a>
</div>

Desen 3: OnRedirectToIdentityProvider ile Programlama

Aşağıdaki kod, kimlik sağlayıcısına yeniden yönlendirme sırasında sorgu parametrelerine ve tanımlama bilgilerine göre ipuçlarını dinamik olarak ayarlar:

builder.Services.Configure<OpenIdConnectOptions>(
    OpenIdConnectDefaults.AuthenticationScheme,
    options =>
{
    var existingHandler = options.Events.OnRedirectToIdentityProvider;

    options.Events.OnRedirectToIdentityProvider = async context =>
    {
        if (existingHandler != null)
        {
            await existingHandler(context);
        }

        // Add hints based on application logic
        if (context.HttpContext.Request.Query.TryGetValue("tenant", out var tenant))
        {
            context.ProtocolMessage.DomainHint = tenant;
        }

        // Get suggested user from cookie or session
        var suggestedUser = context.HttpContext.Request.Cookies["LastSignedInUser"];
        if (!string.IsNullOrEmpty(suggestedUser))
        {
            context.ProtocolMessage.LoginHint = suggestedUser;
        }
    };
});

Kullanım örnekleri

E-ticaret Platformu:

// Pre-fill returning customer email
loginHint = customerEmail

B2B Uygulaması:

// Direct to customer's tenant
domainHint = customerDomain

Çok Kiracılı SaaS:

// Route based on subdomain
domainHint = GetTenantFromSubdomain(Request.Host)

En iyi yöntemleri izleyin

Yapılması Gerekenler

1. Mevcut olay işleyicilerini her zaman koruyun. Özel mantığınızı çalıştırmadan önce mevcut işleyiciyi kaydedin ve çağırın:

var existingHandler = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
    await existingHandler(context); // Call Microsoft.Identity.Web's handler
    // Your custom code
};

2. İzleme için ilişkilendirme kimliklerini kullanın. Tanılama için belirteç edinme isteklerine bir korelasyon kimliği ekleyin.

var tokenOptions = new TokenAcquisitionOptions
{
    CorrelationId = Activity.Current?.Id ?? Guid.NewGuid()
};

3. Özel talepleri doğrulayın. Erişim vermeden önce özel taleplerin beklenen değerleri içerdiğini doğrulayın:

var department = context.Principal.FindFirst("department")?.Value;
if (!IsValidDepartment(department))
{
    throw new UnauthorizedAccessException("Invalid department");
}

4. Günlük özelleştirme hataları. Özel logiği try-catch bloklarına sarın ve hataları kaydedin.

try
{
    // Custom logic
}
catch (Exception ex)
{
    logger.LogError(ex, "Custom authentication logic failed");
    throw;
}

5. Hem başarı hem de başarısızlık yollarını test edin. Testlerinizdeki tüm kimlik doğrulama senaryolarını kapsar:

// Test with valid tokens
// Test with missing claims
// Test with expired tokens
// Test with wrong audience

Yapılmaması Gerekenler

1. Microsoft.Identity.Web'in olay işleyicilerini atlamayın:

//  Wrong - loses built-in security checks
options.Events.OnTokenValidated = async context => { /* your code */ };

//  Correct - preserves security
var existing = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
    await existing(context);
    /* your code */
};

2. Üretimde PII günlüğünü etkinleştirmeyin:

//  Wrong
options.EnablePiiLogging = true; // In production!

//  Correct
if (builder.Environment.IsDevelopment())
{
    options.EnablePiiLogging = true;
}

3. Belirteç doğrulamasını atlamayın:

//  Wrong - insecure!
options.TokenValidationParameters.ValidateLifetime = false;
options.TokenValidationParameters.ValidateAudience = false;

//  Correct - maintain security
options.TokenValidationParameters.ValidateLifetime = true;
options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(5);

4. Hassas değerleri sabit kodlamayın:

//  Wrong
options.ClientSecret = "mysecret123";

//  Correct
options.ClientSecret = builder.Configuration["AzureAd:ClientSecret"];

5. Ara yazılımda kimlik doğrulamayı değiştirmeyin:

//  Wrong - configure in Startup, not middleware
app.Use(async (context, next) =>
{
    // Modifying auth options here is too late!
});

Yaygın sorunları giderme

Özelleştirmenin etkili olmamasını çözme

Yürütme sırasını denetleyin:

  1. AddMicrosoftIdentityWebApp / AddMicrosoftIdentityWebApi varsayılanları ayarlar
  2. Configure aramalarınız çalışıyor
  3. PostConfigure çağrıları çalıştır (varsa)
  4. Seçenekler kullanılır

Çözüm:Configure çağrınız etkili olmuyorsa kullanın PostConfigure, çünkü PostConfigure tüm Configure çağrılarının ardından çalışır.

services.PostConfigure<OpenIdConnectOptions>(
    OpenIdConnectDefaults.AuthenticationScheme,
    options => { /* your changes */ }
);

Eksik özel istemleri düzelt

Özel talepler görünmüyorsa aşağıdakileri doğrulayın:

  1. Mevcut işleyiciyle OnTokenValidated işleyicisi doğru şekilde zincirlenmiştir.
  2. Kodunuz talepleri eklemeden önce kimlik doğrulama başarılı olur.
  3. Talepler doğru ClaimsIdentityöğesine eklenir.

Aşağıdaki kod hata ayıklama için tüm talepleri günlüğe kaydeder:

var claims = context.Principal.Claims.ToList();
logger.LogInformation($"Claims count: {claims.Count}");
foreach (var claim in claims)
{
    logger.LogInformation($"{claim.Type}: {claim.Value}");
}

Çalışmayan olayları düzeltme

Olaylar tetiklenmiyorsa, kimlik doğrulama ve yetkilendirme ara yazılımının doğru sırada kaydedildiğini doğrulayın:

app.UseAuthentication(); // Must be first
app.UseAuthorization();  // Must be second
app.MapControllers();    // Then endpoints