Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Microsoft. Identity.Web bietet sichere Standardeinstellungen für die Authentifizierung und Autorisierung in ASP.NET Core Anwendungen, die in Microsoft Entra ID integriert werden. Sie können viele Aspekte des Authentifizierungsverhaltens anpassen und gleichzeitig die integrierten Sicherheitsfeatures der Bibliothek beibehalten.
Identifizieren anpassbarer Bereiche
| Fläche | Anpassungsoptionen |
|---|---|
| Konfiguration | Alle MicrosoftIdentityOptions, OpenIdConnectOptions, JwtBearerOptions Eigenschaften |
| Ereignisse | OpenID Connect-Ereignisse (OnTokenValidated, OnRedirectToIdentityProviderusw.) |
| Tokenerwerb | Korrelations-IDs, zusätzliche Abfrageparameter |
| Ansprüche | Hinzufügen von benutzerdefinierten Ansprüchen zu ClaimsPrincipal |
| UI | Abmeldeseiten, Umleitungsverhalten |
| Anmelden | Anmeldehinweise, Domänenhinweise |
Auswählen einer Anpassungsmethode
In der folgenden Tabelle sind die Bereiche zusammengefasst, die Sie anpassen können und welche Bereiche von den einzelnen Bereichen unterstützt werden.
Verwenden Sie eine von zwei Ansätzen zum Anpassen von Optionen:
-
Configure<TOptions>– Konfiguriert Optionen, bevor sie verwendet werden -
PostConfigure<TOptions>– Konfiguriert Optionen nach allenConfigureAnrufen
Reihenfolge der Ausführung:
Configure → Configure → ... → PostConfigure → PostConfigure → ... → Options used
Konfigurieren von Authentifizierungsoptionen
In diesem Abschnitt wird gezeigt, wie Sie die verschiedenen Authentifizierungsoptionsklassen konfigurieren, die Microsoft. Identity.Web verwendet.
Konfigurationszuordnung verstehen
Der "AzureAd" Abschnitt in appsettings.json wird auf mehrere Klassen abgebildet.
Sie können eine beliebige Eigenschaft aus diesen Klassen in Ihrer Konfiguration verwenden.
Muster 1: Konfigurieren von MicrosoftIdentityOptions
Der folgende Code konfiguriert MicrosoftIdentityOptions zur Aktivierung der PII-Protokollierung, um Clientfähigkeiten festzulegen und Tokenvalidierungsparameter anzupassen.
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();
Muster 2: Konfigurieren von OpenIdConnectOptions (Web-Apps)
Der folgende Code passt OpenIdConnectOptions für eine Web-App an, um den Antworttyp festzulegen, Cookie- und Tokenvalidierungseinstellungen zu konfigurieren und Scopes hinzuzufügen.
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;
});
Muster 3: Konfigurieren von JwtBearerOptions (Web-APIs)
Der folgende Code passt sich JwtBearerOptions für eine Web-API an, um gültige Zielgruppen, Anspruchszuordnungen und Tokenlebensdauerüberprüfung festzulegen:
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
});
Muster 4: Konfigurieren von Cookieoptionen
Der folgende Code konfiguriert die Cookierichtlinie und cookieauthentifizierungsoptionen für Ihre App, einschließlich Sicherheitseinstellungen und Ablaufverhalten:
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;
});
Anpassen von Ereignishandlern
Die OpenID Connect- und JWT Bearer-Authentifizierung machen Ereignisse verfügbar, mit denen Sie eine Verbindung herstellen können. Microsoft. Identity.Web richtet eigene Ereignishandler ein, sodass Sie Ihre benutzerdefinierten Handler mit den vorhandenen Handlern verketten müssen, um integrierte Funktionen beizubehalten.
Beibehalten der vorhandenen Handler
Wenn Sie benutzerdefinierte Ereignishandler hinzufügen, speichern und rufen Sie zuerst den vorhandenen Handler auf. Das folgende Beispiel zeigt die falschen und korrekten Ansätze.
Der folgende Code fälschlicherweise überschreibt den Microsoft.Identity.Web-Handler.
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Events.OnTokenValidated = async context =>
{
// Your code - but you LOST the built-in validation!
await Task.CompletedTask;
};
});
Der folgende Code wird ordnungsgemäß mit dem vorhandenen Handler verkettet:
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"));
};
});
Anwenden allgemeiner Ereignisszenarien
Hinzufügen von benutzerdefinierten Ansprüchen nach der Tokenüberprüfung
Der folgende Code fügt der ClaimsPrincipal nach der Token-Überprüfung in einer Web-API benutzerdefinierte Ansprüche hinzu. Sie sucht die Abteilung des Benutzers aus einer Datenbank und weist eine anwendungsspezifische Rolle basierend auf der E-Mail-Domäne zu:
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"));
}
};
});
Der folgende Code fügt benutzerdefinierte Ansprüche in einer Web-App hinzu, indem Microsoft Graph aufgerufen wird, um zusätzliche Benutzerprofildaten nach der Tokenüberprüfung abzurufen:
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 ?? ""));
};
});
Hinzufügen von Abfrageparametern zur Autorisierungsanforderung
Der folgende Code fügt der Autorisierungsanforderung, die an den identitätsanbieter Microsoft Entra gesendet wird, benutzerdefinierte Abfrageparameter hinzu:
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"];
}
};
});
Anpassen der Behandlung von Authentifizierungsfehlern
Der folgende Code behandelt Authentifizierungsfehler, indem der Fehler protokolliert und eine benutzerdefinierte JSON-Fehlerantwort zurückgegeben wird:
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
};
});
Behandeln des verweigerten Zugriffs
Der folgende Code leitet Benutzer zu einer benutzerdefinierten Seite um, wenn sie die Zustimmung verweigern:
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;
};
});
Tokenerwerb anpassen
Sie können anpassen, wie Token beim Aufrufen von downstream-APIs erworben werden, indem Sie Optionen übergeben.IDownstreamApi
Verwenden von IDownstreamApi mit benutzerdefinierten Optionen
Der folgende Code übergibt beim Abrufen eines Tokens durch IDownstreamApi eine Korrelations-ID und zusätzliche Abfrageparameter:
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);
}
}
Anpassen der Benutzeroberfläche
Sie können steuern, wo Benutzer nach der Anmeldung und Abmeldung landen, und die Abmeldeerfahrung anpassen.
Umleiten zu einer bestimmten Seite nach der Anmeldung
Verwenden Sie den redirectUri Parameter, um Benutzer nach der Anmeldung an eine bestimmte Seite zu senden:
<!-- 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"
});
}
Abmeldeseite anpassen
Option 1: Überschreiben der Razor-Seite
Erstellen Sie eine Datei bei Areas/MicrosoftIdentity/Pages/Account/SignedOut.cshtml mit Ihren benutzerdefinierten Inhalten.
@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>
Option 2: Umleiten zu einer benutzerdefinierten Seite
Mit dem folgenden Code werden Benutzer anstelle der Standardeinstellung auf eine benutzerdefinierte abgemeldete Seite umgeleitet:
builder.Services.Configure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
options.Events.OnSignedOutCallbackRedirect = context =>
{
context.Response.Redirect("/Home/SignedOut");
context.HandleResponse();
return Task.CompletedTask;
};
});
Anpassung des Anmeldeerlebnisses
Verwenden von Anmeldehinweisen und Domänenhinweisen
Optimieren Sie die Anmeldeumgebung, indem Sie Benutzernamen vorab auffüllen und Benutzer an bestimmte Microsoft Entra Mandanten weiterleiten.
Hinweise verstehen
| Hint | Purpose | Beispiel |
|---|---|---|
| loginHint | Vorausfüllen des Benutzernamen-/E-Mail-Felds | "user@contoso.com" |
| domainHint | Gehe direkt zu einer bestimmten Mandanten-Login-Seite | "contoso.com" |
Hinweismuster anwenden
Muster 1: Controllerbasiert
Der folgende Code zeigt Controlleraktionen für standardanmeldung, Anmeldung mit Anmeldehinweisen, Domänenhinweisen oder beides:
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"
});
}
}
Muster 2: Ansichtsbasiert
Der folgende HTML-Code zeigt Anmeldelinks mit unterschiedlichen Hinweiskonfigurationen:
<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>
Muster 3: Programmgesteuert mit OnRedirectToIdentityProvider
Der folgende Code legt hinweise basierend auf Abfrageparametern und Cookies während der Umleitung zum Identitätsanbieter dynamisch fest:
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;
}
};
});
Anwendungsfälle
E-Commerce-Plattform:
// Pre-fill returning customer email
loginHint = customerEmail
B2B-Anwendung:
// Direct to customer's tenant
domainHint = customerDomain
Mehrinstanzenfähige SaaS:
// Route based on subdomain
domainHint = GetTenantFromSubdomain(Request.Host)
Bewährte Methoden befolgen
Empfohlen
1. Vorhandene Ereignishandler immer beibehalten. Speichern und Aufrufen des vorhandenen Handlers vor dem Ausführen der benutzerdefinierten Logik:
var existingHandler = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await existingHandler(context); // Call Microsoft.Identity.Web's handler
// Your custom code
};
2. Verwenden Sie Korrelations-IDs für die Ablaufverfolgung. Anfügen einer Korrelations-ID an Tokenakquisitionsanforderungen für die Diagnose:
var tokenOptions = new TokenAcquisitionOptions
{
CorrelationId = Activity.Current?.Id ?? Guid.NewGuid()
};
3. Überprüfen sie benutzerdefinierte Ansprüche. Stellen Sie sicher, dass benutzerdefinierte Ansprüche erwartete Werte enthalten, bevor Sie Zugriff gewähren:
var department = context.Principal.FindFirst("department")?.Value;
if (!IsValidDepartment(department))
{
throw new UnauthorizedAccessException("Invalid department");
}
4. Protokollieren von Anpassungsfehlern. Umschließen Sie benutzerdefinierte Logik in "try-catch"-Blöcken und protokollieren Sie Fehler.
try
{
// Custom logic
}
catch (Exception ex)
{
logger.LogError(ex, "Custom authentication logic failed");
throw;
}
5. Testen Sie sowohl Erfolgs- als auch Fehlerpfade. Behandeln Sie alle Authentifizierungsszenarien in Ihren Tests:
// Test with valid tokens
// Test with missing claims
// Test with expired tokens
// Test with wrong audience
DONT‘s
1. Überspringen Sie die Ereignishandler von Microsoft.Identity.Web nicht:
// 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. Aktivieren Sie keine PII-Protokollierung in der Produktion:
// Wrong
options.EnablePiiLogging = true; // In production!
// Correct
if (builder.Environment.IsDevelopment())
{
options.EnablePiiLogging = true;
}
3. Umgehen Sie die Tokenüberprüfung nicht:
// Wrong - insecure!
options.TokenValidationParameters.ValidateLifetime = false;
options.TokenValidationParameters.ValidateAudience = false;
// Correct - maintain security
options.TokenValidationParameters.ValidateLifetime = true;
options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(5);
4. Hartcodieren Sie keine vertraulichen Werte:
// Wrong
options.ClientSecret = "mysecret123";
// Correct
options.ClientSecret = builder.Configuration["AzureAd:ClientSecret"];
5. Ändern Sie die Authentifizierung in Middleware nicht:
// Wrong - configure in Startup, not middleware
app.Use(async (context, next) =>
{
// Modifying auth options here is too late!
});
Häufige Probleme beheben
Anpassung nicht wirksam
Ausführungsreihenfolge überprüfen:
-
AddMicrosoftIdentityWebApp/AddMicrosoftIdentityWebApilegt Standardwerte fest. - Ihre
ConfigureAnrufe werden ausgeführt -
PostConfigureAufrufe werden ausgeführt (falls vorhanden) - Optionen werden verwendet.
Lösung: Verwenden Sie PostConfigure , wenn Ihr Configure Anruf nicht wirksam wird, da PostConfigure er nach allen Configure Anrufen ausgeführt wird:
services.PostConfigure<OpenIdConnectOptions>(
OpenIdConnectDefaults.AuthenticationScheme,
options => { /* your changes */ }
);
Beheben fehlender benutzerdefinierter Ansprüche
Überprüfen Sie Folgendes, wenn keine benutzerdefinierten Ansprüche angezeigt werden:
- Der
OnTokenValidatedHandler wird korrekt mit dem vorhandenen Handler verkettet. - Die Authentifizierung ist erfolgreich, bevor Ihr Code Ansprüche hinzufügt.
- Ansprüche werden dem richtigen
ClaimsIdentityhinzugefügt.
Der folgende Code protokolliert alle Ansprüche für das Debuggen:
var claims = context.Principal.Claims.ToList();
logger.LogInformation($"Claims count: {claims.Count}");
foreach (var claim in claims)
{
logger.LogInformation($"{claim.Type}: {claim.Value}");
}
Beheben von Ereignissen, die nicht ausgelöst werden
Wenn Ereignisse nicht ausgelöst werden, überprüfen Sie, ob die Authentifizierungs- und Autorisierungs-Middleware in der richtigen Reihenfolge registriert sind:
app.UseAuthentication(); // Must be first
app.UseAuthorization(); // Must be second
app.MapControllers(); // Then endpoints