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.
In diesem Handbuch wird erläutert, wie Downstream-APIs aus ASP.NET Core- und OWIN-Webanwendungen mithilfe von Microsoft.Identity.Web aufgerufen werden. In Web-Apps erwerben Sie Token im Namen des angemeldeten Benutzers , um APIs mit delegierten Berechtigungen aufzurufen.
Verständnis des Tokenflusses
Wenn sich ein Benutzer bei Ihrer Webanwendung anmeldet, können Sie nachgeschaltete APIs (Microsoft Graph, Azure Dienste oder benutzerdefinierte APIs) in ihrem Namen aufrufen. Microsoft. Identity.Web verarbeitet Tokenakquisition, Zwischenspeicherung und automatische Aktualisierung.
Überprüfung des Benutzertokenflusses
sequenceDiagram
participant User as User Browser
participant WebApp as Your Web App
participant AzureAD as Microsoft Entra ID
participant API as Downstream API
User->>WebApp: 1. Access page requiring API data
Note over WebApp: User already signed in
WebApp->>AzureAD: 2. Request access token for API<br/>(using user's refresh token)
AzureAD->>AzureAD: 3. Validate & check consent
AzureAD->>WebApp: 4. Return access token
Note over WebApp: Cache token
WebApp->>API: 5. Call API with token
API->>WebApp: 6. Return data
WebApp->>User: 7. Render page with data
Überprüfen der Voraussetzungen
Stellen Sie sicher, dass Ihre Umgebung die folgenden Anforderungen erfüllt, bevor Sie beginnen.
- Web-App mit OpenID Connect-Authentifizierung konfiguriert
- Benutzeranmeldung funktioniert
- App-Registrierung mit konfigurierten API-Berechtigungen
- Benutzerzustimmung erhalten (oder Administratorzustimmung erteilt)
Implementieren von ASP.NET Core
1. Konfigurieren der Authentifizierung und des Tokenerwerbs
Fügen Sie Authentifizierungsdienste hinzu, und aktivieren Sie die Tokenakquisition in Ihrer Program.cs Datei.
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
// Add authentication with explicit scheme
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
builder.Services.AddRazorPages()
.AddMicrosoftIdentityUI();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
2. Konfigurieren von appsettings.json
Definieren Sie Ihre Microsoft Entra ID-App-Registrierung und nachgeschaltete API-Einstellungen in appsettings.json.
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-client-id",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath": "/signout-callback-oidc",
"ClientCredentials": [
{
"SourceType": "ClientSecret",
"ClientSecret": "your-client-secret"
}
]
},
"DownstreamApis": {
"GraphAPI": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": ["user.read", "mail.read"]
},
"MyAPI": {
"BaseUrl": "https://myapi.example.com",
"Scopes": ["api://my-api-id/access_as_user"]
}
}
}
Wichtig: Für Web-Apps, die nachgeschaltete APIs aufrufen, benötigen Sie zusätzlich zur Anmeldekonfiguration Clientanmeldeinformationen (Zertifikat oder geheimer Schlüssel).
3. Hinzufügen der Nachgelagerten API-Unterstützung
Wählen Sie eine der folgenden Optionen aus, um Ihre downstream-APIs zu registrieren.
Option A: Registrieren benannter APIs
Der folgende Code registriert mehrere downstream-APIs aus der Konfiguration.
using Microsoft.Identity.Web;
// Register multiple downstream APIs
builder.Services.AddDownstreamApis(
builder.Configuration.GetSection("DownstreamApis"));
Option B: Microsoft Graph Hilfsprogramm verwenden
Der folgende Code registriert den Microsoft Graph SDK-Client aus der Konfiguration.
// Install: Microsoft.Identity.Web.GraphServiceClient
builder.Services.AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApis:GraphAPI"));
4. Aufrufen der downstream-API vom Controller
Fügen Sie IDownstreamApi in Ihren Controller ein, und rufen Sie die API im Namen des angemeldeten Benutzers auf.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web;
using Microsoft.Identity.Abstractions;
[Authorize]
public class ProfileController : Controller
{
private readonly IDownstreamApi _downstreamApi;
private readonly ILogger<ProfileController> _logger;
public ProfileController(
IDownstreamApi downstreamApi,
ILogger<ProfileController> logger)
{
_downstreamApi = downstreamApi;
_logger = logger;
}
public async Task<IActionResult> Index()
{
try
{
// Call downstream API on behalf of user
var userData = await _downstreamApi.GetForUserAsync<UserData>(
"MyAPI",
options => options.RelativePath = "api/profile");
return View(userData);
}
catch (MicrosoftIdentityWebChallengeUserException ex)
{
// Incremental consent required
// Redirect user to consent page
return Challenge(
new AuthenticationProperties
{
RedirectUri = "/Profile"
},
OpenIdConnectDefaults.AuthenticationScheme);
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Failed to call downstream API");
return View("Error");
}
}
}
5. Aufrufen der downstream-API von Razor Page
Fügen Sie IDownstreamApi in das Razor Page-Modell ein und rufen Sie die API auf.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Identity.Web;
using Microsoft.Identity.Abstractions;
[Authorize]
public class ProfileModel : PageModel
{
private readonly IDownstreamApi _downstreamApi;
public UserData UserData { get; set; }
public ProfileModel(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
public async Task OnGetAsync()
{
try
{
UserData = await _downstreamApi.GetForUserAsync<UserData>(
"MyAPI",
options => options.RelativePath = "api/profile");
}
catch (MicrosoftIdentityWebChallengeUserException)
{
// Handle incremental consent
// User will be redirected to consent page
throw;
}
}
}
Microsoft Graph aufrufen
Verwenden Sie für Microsoft Graph-API-Anrufe die dedizierte GraphServiceClient.
Pakete installieren
Installieren Sie das Microsoft Graph-Paket für Microsoft. Identity.Web.
dotnet add package Microsoft.Identity.Web.GraphServiceClient
Konfigurieren Sie den Graph-Client im Startcode.
// Startup configuration
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph(options =>
{
options.Scopes = "user.read mail.read";
})
.AddInMemoryTokenCaches();
Rufen Sie die Graph-API an
Injizieren Sie GraphServiceClient in Ihren Controller, um Microsoft Graph Endpunkte aufzurufen.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Graph;
[Authorize]
{
private readonly GraphServiceClient _graphClient;
public HomeController(GraphServiceClient graphClient)
{
_graphClient = graphClient;
}
public async Task<IActionResult> Index()
{
// Get current user's profile
var user = await _graphClient.Me.GetAsync();
// Get user's emails
var messages = await _graphClient.Me.Messages
.GetAsync(config => config.QueryParameters.Top = 10);
return View(new { User = user, Messages = messages });
}
}
Erfahren Sie mehr über Microsoft Graph Integration
Azure SDK-Clients anrufen
Verwenden Sie zum Aufrufen von Azure-Diensten MicrosoftIdentityTokenCredential.
Pakete installieren
Installieren Sie die erforderlichen Azure SDK Pakete.
dotnet add package Microsoft.Identity.Web.Azure
dotnet add package Azure.Storage.Blobs
Registrieren Sie die Microsoft Entra Tokenanmeldedaten im Startcode.
using Microsoft.Identity.Web;
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
// Add Azure token credential
builder.Services.AddMicrosoftIdentityAzureTokenCredential();
Zugreifen auf Azure-Dienste
Fügen Sie die Tokenanmeldeinformationen ein, und verwenden Sie sie mit Azure SDK Clients.
using Azure.Storage.Blobs;
using Microsoft.Identity.Web;
public class StorageController : Controller
{
private readonly MicrosoftIdentityTokenCredential _credential;
public StorageController(MicrosoftIdentityTokenCredential credential)
{
_credential = credential;
}
[Authorize]
public async Task<IActionResult> ListBlobs()
{
var blobClient = new BlobServiceClient(
new Uri("https://myaccount.blob.core.windows.net"),
_credential);
var container = blobClient.GetBlobContainerClient("mycontainer");
var blobs = new List<string>();
await foreach (var blob in container.GetBlobsAsync())
{
blobs.Add(blob.Name);
}
return View(blobs);
}
}
Erfahren Sie mehr über Azure SDK Integration
Aufrufen benutzerdefinierter APIs mit IDownstreamApi
Für Ihre eigenen REST-APIs, IDownstreamApi bietet ein einfacher, konfigurationsgesteuerter Ansatz.
Konfigurieren der API
Definieren Sie die nachgeschalteten API-Einstellungen in appsettings.json.
{
"DownstreamApis": {
"MyAPI": {
"BaseUrl": "https://myapi.example.com",
"Scopes": ["api://my-api-id/access_as_user"],
"RequestAppToken": false
}
}
}
GET-Anforderungen senden
Abrufen von Daten aus der downstream-API mit optionalen Abfrageparametern.
// Simple GET
var data = await _downstreamApi.GetForUserAsync<MyData>(
"MyAPI",
options => options.RelativePath = "api/resource");
// GET with query parameters
var results = await _downstreamApi.GetForUserAsync<SearchResults>(
"MyAPI",
options =>
{
options.RelativePath = "api/search";
options.QueryParameters = new Dictionary<string, string>
{
["query"] = "test",
["limit"] = "10"
};
});
Senden von POST-Anfragen
Erstellen Sie eine neue Ressource für die downstream-API, indem Sie einen Anforderungstext posten.
var newItem = new CreateItemRequest
{
Name = "New Item",
Description = "Item description"
};
var created = await _downstreamApi.PostForUserAsync<CreateItemRequest, CreatedItem>(
"MyAPI",
newItem,
options => options.RelativePath = "api/items");
PUT- und DELETE-Anforderungen senden
Aktualisieren oder Löschen von Ressourcen in der downstream-API.
// PUT request
var updated = await _downstreamApi.PutForUserAsync<UpdateRequest, UpdatedItem>(
"MyAPI",
updateData,
options => options.RelativePath = "api/items/123");
// DELETE request
await _downstreamApi.DeleteForUserAsync(
"MyAPI",
null,
options => options.RelativePath = "api/items/123");
Weitere Informationen zu benutzerdefinierten API-Aufrufen
Verwenden von IAuthorizationHeaderProvider (erweitert)
Verwenden Sie IAuthorizationHeaderProviderfür die maximale Kontrolle über HTTP-Anforderungen .
Registrieren des HTTP-Clients
Registrieren Sie einen benannten HTTP-Client für Ihre downstream-API.
builder.Services.AddHttpClient("MyAPI", client =>
{
client.BaseAddress = new Uri("https://myapi.example.com");
});
Erstellen benutzerdefinierter HTTP-Anforderungen
Erstellen und Senden von HTTP-Anforderungen mit benutzerdefinierten Headern und Autorisierungen.
using Microsoft.Identity.Abstractions;
public class CustomApiService
{
private readonly IAuthorizationHeaderProvider _authProvider;
private readonly IHttpClientFactory _httpClientFactory;
public CustomApiService(
IAuthorizationHeaderProvider authProvider,
IHttpClientFactory httpClientFactory)
{
_authProvider = authProvider;
_httpClientFactory = httpClientFactory;
}
public async Task<MyData> GetDataAsync()
{
// Get authorization header
var authHeader = await _authProvider.CreateAuthorizationHeaderForUserAsync(
new[] { "api://my-api-id/access_as_user" });
// Create HTTP request with custom logic
var client = _httpClientFactory.CreateClient("MyAPI");
var request = new HttpRequestMessage(HttpMethod.Get, "api/resource");
request.Headers.Add("Authorization", authHeader);
request.Headers.Add("X-Custom-Header", "custom-value");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<MyData>();
}
}
Weitere Informationen zur benutzerdefinierten HTTP-Logik
Bearbeitung der inkrementellen Zustimmung und des bedingten Zugriffs
Beim Aufrufen nachgeschalteter APIs muss Ihre Anwendung möglicherweise Szenarien behandeln, in denen benutzerinteraktion erforderlich ist. Dies geschieht in drei Hauptszenarien:
- Inkrementelle Zustimmung – Anfordern zusätzlicher Berechtigungen über das, was ursprünglich erteilt wurde
- Bedingter Zugriff – Erfüllen von Sicherheitsanforderungen wie MFA, Gerätekompatibilität oder Standortrichtlinien
- Token Cache Eviction – Wiederbefüllung des Token-Caches nach Neustart der Anwendung oder Ablauf des Caches
Microsoft. Identity.Web bietet eine automatische Behandlung dieser Szenarien mit minimalem Code erforderlich.
Verstehen des Flusses
Wenn Microsoft. Identity.Web erkennt, dass eine Benutzerinteraktion erforderlich ist, wird ein MicrosoftIdentityWebChallengeUserException ausgelöst. Das Framework verarbeitet dies automatisch über das [AuthorizeForScopes] Attribut oder den MicrosoftIdentityConsentAndConditionalAccessHandler Dienst (für Blazor), der:
- Leitet den Benutzer zur Microsoft Entra ID zur Zustimmung/Authentifizierung um.
- Behält die ursprüngliche Anforderungs-URL bei
- Gibt den Benutzer nach Abschluss des Prozesses an sein beabsichtigtes Ziel zurück.
- Speichert die neu erworbenen Token.
Überprüfen der Voraussetzungen
Um die automatische Zustimmungsbehandlung zu aktivieren, stellen Sie sicher, dass Program.cs die folgende Konfiguration enthält.
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDownstreamApi("MyAPI", builder.Configuration.GetSection("MyAPI"))
.AddInMemoryTokenCaches();
// For MVC applications - enables the account controller
builder.Services.AddControllersWithViews()
.AddMicrosoftIdentityUI();
// Ensure routes are mapped
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers(); // Required for AccountController
Anwenden von [AuthorizeForScopes] in MVC-Controllern
Das [AuthorizeForScopes] Attribut, das für Controller- oder Controlleraktionen festgelegt wird, verarbeitet MicrosoftIdentityWebChallengeUserException automatisch, indem der Benutzer gefragt wird, wenn zusätzliche Berechtigungen erforderlich sind.
Geltungsbereiche inline deklarieren
Geben Sie die erforderlichen Bereiche direkt im Attribut an.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web;
using Microsoft.Identity.Abstractions;
[Authorize]
[AuthorizeForScopes(Scopes = new[] { "user.read" })]
public class ProfileController : Controller
{
private readonly IDownstreamApi _downstreamApi;
public ProfileController(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
public async Task<IActionResult> Index()
{
// AuthorizeForScopes automatically handles consent challenges
var userData = await _downstreamApi.GetForUserAsync<UserData>(
"MyAPI",
options => options.RelativePath = "api/profile");
return View(userData);
}
// Different action requires additional scopes
[AuthorizeForScopes(Scopes = new[] { "user.read", "mail.read" })]
public async Task<IActionResult> Emails()
{
var emails = await _downstreamApi.GetForUserAsync<EmailList>(
"GraphAPI",
options => options.RelativePath = "me/messages");
return View(emails);
}
}
Konfigurieren von Bereichen aus appsettings
Speichern Sie Bereiche in appsettings.json, um die Wartbarkeit zu verbessern.
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "common",
"ClientId": "[Your-Client-ID]",
"ClientCredentials": [
{
"SourceType": "ClientSecret",
"ClientSecret": "[Your-Client-Secret]"
}
]
},
"DownstreamApis": {
"TodoList": {
"BaseUrl": "https://localhost:5001",
"Scopes": [ "api://[API-Client-ID]/access_as_user" ]
},
"GraphAPI": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": [ "https://graph.microsoft.com/Mail.Read", "https://graph.microsoft.com/Mail.Send" ]
}
}
}
Controller:
[Authorize]
[AuthorizeForScopes(ScopeKeySection = "DownstreamApis:TodoList:Scopes:0")]
public class TodoListController : Controller
{
private readonly IDownstreamApi _downstreamApi;
public TodoListController(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
public async Task<IActionResult> Index()
{
var todos = await _downstreamApi.GetForUserAsync<IEnumerable<TodoItem>>(
"TodoList",
options => options.RelativePath = "api/todolist");
return View(todos);
}
[AuthorizeForScopes(ScopeKeySection = "DownstreamApis:GraphAPI:Scopes:0")]
public async Task<IActionResult> EmailTodos()
{
// If user hasn't consented to Mail.Send, they'll be prompted
await _downstreamApi.PostForUserAsync<EmailMessage, object>(
"GraphAPI",
new EmailMessage { /* ... */ },
options => options.RelativePath = "me/sendMail");
return RedirectToAction("Index");
}
}
Konfigurieren von Microsoft Entra External ID mit Benutzerflüssen
Geben Sie für B2C-Anwendungen (External ID) mit mehreren Benutzerflüssen den Benutzerfluss im Attribut an.
[Authorize]
public class AccountController : Controller
{
private const string SignUpSignInFlow = "b2c_1_susi";
private const string EditProfileFlow = "b2c_1_edit_profile";
private const string ResetPasswordFlow = "b2c_1_reset";
[AuthorizeForScopes(
ScopeKeySection = "DownstreamApis:TodoList:Scopes:0",
UserFlow = SignUpSignInFlow)]
public async Task<IActionResult> Index()
{
var data = await _downstreamApi.GetForUserAsync<UserData>(
"TodoList",
options => options.RelativePath = "api/data");
return View(data);
}
[AuthorizeForScopes(
Scopes = new[] { "openid", "offline_access" },
UserFlow = EditProfileFlow)]
public async Task<IActionResult> EditProfile()
{
// This triggers the B2C edit profile flow
return RedirectToAction("Index");
}
}
[AuthorizeForScopes] auf Razor-Seiten anwenden
Wenden Sie [AuthorizeForScopes] auf die Seitenmodellklasse an.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Identity.Web;
using Microsoft.Identity.Abstractions;
[Authorize]
[AuthorizeForScopes(ScopeKeySection = "DownstreamApis:MyAPI:Scopes:0")]
public class IndexModel : PageModel
{
private readonly IDownstreamApi _downstreamApi;
public UserData UserData { get; set; }
public IndexModel(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
public async Task OnGetAsync()
{
// Automatically handles consent challenges
UserData = await _downstreamApi.GetForUserAsync<UserData>(
"MyAPI",
options => options.RelativePath = "api/profile");
}
}
Umgang mit Einwilligungen in Blazor Server
Blazor Server-Anwendungen erfordern eine explizite Ausnahmebehandlung mithilfe des Diensts MicrosoftIdentityConsentAndConditionalAccessHandler .
Konfigurieren von Program.cs
Registrieren Sie den Zustimmungshandler für Blazor Server in Ihrem Startcode.
builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDownstreamApis("TodoList", builder.Configuration.GetSection("DownstreamApis"))
.AddInMemoryTokenCaches();
// Register the consent handler for Blazor
builder.Services.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
Erstellen Sie die Blazor-Komponente
Verpacken Sie API-Aufrufe in Try-Catch-Blöcken und verwenden Sie ConsentHandler.HandleException(), um Einwilligungsherausforderungen zu bewältigen.
@page "/todolist"
@using Microsoft.Identity.Web
@using Microsoft.Identity.Abstractions
@using MyApp.Models
@inject MicrosoftIdentityConsentAndConditionalAccessHandler ConsentHandler
@inject IDownstreamApi DownstreamApi
<h3>My Todo List</h3>
@if (todos == null)
{
<p><em>Loading...</em></p>
}
else
{
<ul>
@foreach (var todo in todos)
{
<li>@todo.Title</li>
}
</ul>
}
@code {
private IEnumerable<TodoItem> todos;
protected override async Task OnInitializedAsync()
{
await LoadTodosAsync();
}
[AuthorizeForScopes(ScopeKeySection = "DownstreamApis:TodoList:Scopes:0")]
private async Task LoadTodosAsync()
{
try
{
todos = await DownstreamApi.GetForUserAsync<IEnumerable<TodoItem>>(
"TodoList",
options => options.RelativePath = "api/todolist");
}
catch (Exception ex)
{
// Handles MicrosoftIdentityWebChallengeUserException
// and initiates user consent/authentication flow
ConsentHandler.HandleException(ex);
}
}
private async Task AddTodoAsync(string title)
{
try
{
await DownstreamApi.PostForUserAsync<TodoItem, TodoItem>(
"TodoList",
new TodoItem { Title = title },
options => options.RelativePath = "api/todolist");
await LoadTodosAsync();
}
catch (Exception ex)
{
ConsentHandler.HandleException(ex);
}
}
}
Manuelles Behandeln von Ausnahmen (erweitert)
Wenn Sie eine benutzerdefinierte Zustimmungsflusslogik benötigen, behandeln Sie MicrosoftIdentityWebChallengeUserException explizit Folgendes:
[Authorize]
public class AdvancedController : Controller
{
private readonly IDownstreamApi _downstreamApi;
private readonly ILogger<AdvancedController> _logger;
public AdvancedController(
IDownstreamApi downstreamApi,
ILogger<AdvancedController> logger)
{
_downstreamApi = downstreamApi;
_logger = logger;
}
public async Task<IActionResult> SendEmail()
{
try
{
await _downstreamApi.PostForUserAsync<EmailMessage, object>(
"GraphAPI",
new EmailMessage
{
Subject = "Test",
Body = "Test message"
},
options => options.RelativePath = "me/sendMail");
return RedirectToAction("Success");
}
catch (MicrosoftIdentityWebChallengeUserException ex)
{
// Log the consent requirement
_logger.LogWarning(
"Consent required for scopes: {Scopes}. Challenging user.",
string.Join(", ", ex.Scopes));
// Custom properties for redirect
var properties = new AuthenticationProperties
{
RedirectUri = Url.Action("SendEmail", "Advanced"),
};
// Add custom state if needed
properties.Items["consent_attempt"] = "1";
return Challenge(properties, OpenIdConnectDefaults.AuthenticationScheme);
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Failed to send email");
return View("Error");
}
}
}
Bearbeitung von Szenarien für bedingten Zugriff
Richtlinien für bedingten Zugriff können zusätzliche Authentifizierungsfaktoren erfordern. Die Verarbeitung ist identisch mit der inkrementellen Zustimmung:
[Authorize]
[AuthorizeForScopes(ScopeKeySection = "DownstreamApis:SecureAPI:Scopes:0")]
public class SecureDataController : Controller
{
private readonly IDownstreamApi _downstreamApi;
public SecureDataController(IDownstreamApi downstreamApi)
{
_downstreamApi = downstreamApi;
}
public async Task<IActionResult> Index()
{
// If conditional access requires MFA, AuthorizeForScopes
// automatically challenges the user
var sensitiveData = await _downstreamApi.GetForUserAsync<SensitiveData>(
"SecureAPI",
options => options.RelativePath = "api/sensitive");
return View(sensitiveData);
}
}
Häufige Trigger für bedingten Zugriff:
- Mehrstufige Authentifizierung (MFA)
- Kompatible Geräteanforderung
- Vertrauenswürdiger Netzwerkstandort
- Zustimmung zu den Nutzungsbedingungen
- Kennwortänderungsanforderung
Bewährte Methoden befolgen
Wenden Sie diese Empfehlungen an, wenn Sie die Zustimmungs- und bedingten Zugriffsbehandlung implementieren.
Verwenden [AuthorizeForScopes] - Einfachster Ansatz für MVC-Controller und Razor Pages
Scopes in der Konfiguration speichern - Verwenden Sie ScopeKeySection = "DownstreamApis:ApiName:Scopes:0", um auf die Scopes in appsettings.json zu referenzieren.
Anwenden auf Controllerebene – Festlegen von Standardbereichen auf dem Controller, Außerkraftsetzen für bestimmte Aktionen
Behandeln von Ausnahmen in Blazor – API-Aufrufe immer mit Try-Catch umschließen und verwenden ConsentHandler.HandleException()
Erneutes Auslösen von Ausnahmen - Wenn Sie MicrosoftIdentityWebChallengeUserException abfangen, lösen Sie es erneut aus, damit [AuthorizeForScopes] es verarbeiten kann.
Testen des bedingten Zugriffs – Überprüfen, ob Ihre App MFA und andere Zertifizierungsstellenrichtlinien ordnungsgemäß verarbeitet
Ausnahmen nicht unterdrücken – Abfangen ohne sie erneut auszulösen, unterbricht den Zustimmungsfluss.
Keine Antworten auf unbestimmte Zeit zwischenspeichern – Token laufen ab; Entwurf für die erneute Authentifizierung
Vergleichen statischer Berechtigungen und inkrementeller Zustimmung
Statische Berechtigungen (Administratorzustimmung)
Alle Berechtigungen werden während der App-Registrierung angefordert und von einem Mandantenadministrator genehmigt.
Vorteile:
- Benutzer sehen keine Zustimmungsaufforderungen
- Erforderlich für Erstanbieter-Microsoft-Apps
- Einfachere Benutzererfahrung
Nachteile:
- Erfordert die Beteiligung des Mandantenadministrators
- Überprivilegiert von Anfang an
- Weniger flexibel für Szenarien mit mehreren Mandanten
Configuration:
// Request all pre-approved scopes for Microsoft Graph
var scopes = new[] { "https://graph.microsoft.com/.default" };
var userData = await _downstreamApi.GetForUserAsync<UserData>(
"GraphAPI",
options =>
{
options.RelativePath = "me";
options.Scopes = scopes; // Use .default scope
});
Inkrementelle Zustimmung (dynamisch)
Berechtigungen werden während der Laufzeit nach Bedarf angefordert:
Vorteile:
- Bessere Sicherheit (Prinzip der geringsten Rechte)
- Benutzer stimmen zu, was sie tatsächlich verwenden
- Funktioniert für Mehrinstanzen-Apps
Nachteile:
- Benutzer können mit Zustimmungsaufforderungen unterbrochen werden.
- Erfordert Bearbeitung
MicrosoftIdentityWebChallengeUserException
Empfehlung: Verwenden Sie die inkrementelle Zustimmung für Anwendungen mit mehreren Mandanten; Verwenden statischer Berechtigungen für Unternehmens-Apps von Erstanbietern, bei denen die Administratorzustimmung garantiert ist
Tokenzwischenspeicherung konfigurieren
Microsoft. Identity.Web speichert Token zwischen, um die Leistung zu verbessern und Aufrufe von Microsoft Entra zu reduzieren.
In-Memory-Cache verwenden (Standard)
Fügen Sie einen Cache für In-Memory-Token für Entwicklungs- oder Einzelserverszenarien hinzu.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches(); // In-memory cache
Verwendung für:
- Entwicklung
- Bereitstellungen mit einem einzelnen Server
- Kleine Benutzerbasis
Limitations:
- Nicht instanzenübergreifend geteilt
- Beim Neustart der App verloren
- Der Arbeitsspeicherverbrauch wächst mit Benutzern.
Verwenden des verteilten Caches (empfohlen für die Produktion)
Konfigurieren Sie einen verteilten Cache wie Redis oder SQL Server für Produktionsbereitstellungen.
// Install: Microsoft.Identity.Web.TokenCache
// Redis
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration["Redis:ConnectionString"];
options.InstanceName = "MyApp_";
});
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDistributedTokenCaches();
// SQL Server
builder.Services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = builder.Configuration["SqlCache:ConnectionString"];
options.SchemaName = "dbo";
options.TableName = "TokenCache";
});
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDistributedTokenCaches();
Verwendung für:
- Bereitstellungen mit mehreren Servern (Lastenausgleich)
- Hochverfügbarkeitsszenarien
- Große Benutzerbasis
- Beständiger Cache über Neustarts hinweg
Umgang mit Tokenerwerbsfehlern
Allgemeine Ausnahmen abfangen
Der folgende Code veranschaulicht, wie die am häufigsten verwendeten Tokenakquisitions-Ausnahmen erfasst und behandelt werden.
try
{
var data = await _downstreamApi.GetForUserAsync<MyData>(
"MyAPI",
options => options.RelativePath = "api/resource");
}
catch (MicrosoftIdentityWebChallengeUserException ex)
{
// User needs to consent or reauthenticate
_logger.LogWarning($"User consent required: {ex.Message}");
return Challenge(new AuthenticationProperties { RedirectUri = Request.Path });
}
catch (MsalUiRequiredException ex)
{
// User interaction required (sign-in again, MFA, etc.)
_logger.LogWarning($"User interaction required: {ex.Message}");
return Challenge(OpenIdConnectDefaults.AuthenticationScheme);
}
catch (MsalServiceException ex)
{
// Service error (Microsoft Entra ID unavailable, etc.)
_logger.LogError(ex, "Microsoft Entra ID service error");
return StatusCode(503, "Authentication service temporarily unavailable");
}
catch (HttpRequestException ex)
{
// Downstream API unreachable
_logger.LogError(ex, "Downstream API call failed");
return StatusCode(503, "Downstream service unavailable");
}
Implementierung von stufenweiser Verschlechterung
Laden Sie optionale Daten aus nachgeschalteten APIs, und greifen Sie auf Standardwerte zurück, wenn Aufrufe fehlschlagen.
public async Task<IActionResult> Dashboard()
{
var model = new DashboardModel();
// Try to load optional data from downstream API
try
{
model.EnrichedData = await _downstreamApi.GetForUserAsync<EnrichedData>(
"MyAPI",
options => options.RelativePath = "api/enriched");
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to load enriched data, using defaults");
model.EnrichedData = new EnrichedData { /* defaults */ };
}
return View(model);
}
Implementieren von OWIN (.NET Framework)
Führen Sie für OWIN-basierte Webanwendungen im .NET Framework die folgenden Schritte aus.
1. Installieren von Paketen
Installieren Sie die erforderlichen NuGet-Pakete.
Install-Package Microsoft.Identity.Web.OWIN
Install-Package Microsoft.Owin.Host.SystemWeb
2. Konfigurieren des Startvorgangs
Konfigurieren Sie Microsoft Entra Authentifizierung und Tokenakquisition in der OWIN-Startklasse.
using Microsoft.Identity.Web;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Owin;
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.AddMicrosoftIdentityWebApp(
Configuration,
configSectionName: "AzureAd",
openIdConnectScheme: "OpenIdConnect",
cookieScheme: CookieAuthenticationDefaults.AuthenticationType,
subscribeToOpenIdConnectMiddlewareDiagnosticsEvents: true);
app.EnableTokenAcquisitionToCallDownstreamApi();
app.AddDistributedTokenCaches();
}
}
3. Aufrufen der downstream-API
Rufen Sie ein Token ab und rufen Sie die Downstream-API von einem MVC-Controller auf.
using Microsoft.Identity.Web;
using System.Threading.Tasks;
using System.Web.Mvc;
[Authorize]
public class ProfileController : Controller
{
public async Task<ActionResult> Index()
{
var downstreamApi = TokenAcquirerFactory.GetDefaultInstance()
.GetTokenAcquirer()
.GetDownstreamApi();
var userData = await downstreamApi.GetForUserAsync<UserData>(
"MyAPI",
options => options.RelativePath = "api/profile");
return View(userData);
}
}
Note: OWIN-Unterstützung unterscheidet sich von ASP.NET Core. Ausführliche Informationen finden Sie in der OWIN-Dokumentation .
Befolgen bewährter Sicherheitsmethoden
Verwalten von Bereichen
Wenden Sie das Prinzip der geringsten Berechtigungen beim Anfordern von API-Berechtigungen an.
Zu tun:
- Fordern Sie nur Bereiche an, die Sie benötigen.
- Verwenden Sie die inkrementelle Zustimmung für erweiterte Funktionen
- Dokumentieren Sie erforderliche Berechtigungen in Ihrer App
Nicht:
- Anfordern unnötiger Bereiche im Voraus
- Anfordern von Bereichen, die nur für Administratoren vorbehalten sind, ohne Angabe von Gründen.
- Gehen Sie davon aus, dass alle Berechtigungen gewährt werden.
Sicherer Umgang mit Tokens
Befolgen Sie diese Richtlinien, um Zugriffstoken in Ihrer Anwendung zu schützen.
Zu tun:
- Lassen Sie Microsoft. Identity.Web manage tokens
- Verwenden des verteilten Caches in der Produktion
- Sorgfältiger Umgang mit Tokenbeschaffungsfehlern
Nicht:
- Token selbst speichern
- Protokollieren von Zugriffstoken
- Tokens an den clientseitigen Code senden
Fehler behandeln
Implementieren Sie eine robuste Fehlerbehandlung für Authentifizierungs- und API-Aufruffehler.
Zu tun:
- Abfangen und Verarbeiten von Zustimmungsausnahmen
- Bereitstellen klarer Fehlermeldungen für Benutzer
- Protokollfehler für das Debuggen
Nicht:
- Anzeigen von Tokenfehlern für die Benutzer
- API-Aufrufe geräuschlos scheitern
- Authentifizierungs-Ausnahmen ignorieren
Häufige Probleme beheben
Überprüfen Sie diese Lösungen auf häufig auftretende Authentifizierungsfehler.
Problem: "AADSTS65001: Der Benutzer oder Administrator hat nicht zugestimmt"
Ursache: Der Benutzer hat nicht den erforderlichen Bereichen zugestimmt.
Lösung:
catch (MicrosoftIdentityWebChallengeUserException ex)
{
// Redirect to consent page
return Challenge(
new AuthenticationProperties { RedirectUri = Request.Path },
OpenIdConnectDefaults.AuthenticationScheme);
}
Problem: "AADSTS50076: Mehrstufige Authentifizierung erforderlich"
Ursache: Der Benutzer muss MFA abschließen.
Lösung:
catch (MsalUiRequiredException)
{
// Redirect user to sign in with MFA
return Challenge(OpenIdConnectDefaults.AuthenticationScheme);
}
Problem: Token bleiben nicht über App-Neustarts hinweg bestehen
Ursache: Verwenden des Speichercaches.
Solution: Wechseln zum verteilten Cache (Redis, SQL Server oder Cosmos DB).
Problem: 401 Nicht autorisiert von der downstream-API
Mögliche Ursachen:
- Falsche Bereiche angefordert
- API-Berechtigung wurde in der App-Registrierung nicht erteilt
- Token abgelaufen
Lösung:
- Überprüfen Sie, ob die Scopes in der appsettings.json den API-Anforderungen entsprechen.
- Überprüfen der App-Registrierung auf API-Berechtigungen
- Sicherstellen, dass Token zwischengespeichert und aktualisiert werden
Detaillierte Diagnosen: Siehe das Protokollierungs- und Diagnosehandbuch für Korrelations-IDs, Token-Cache-Debugging und umfassende Problemlösungsmuster.
Optimieren der Leistung
Strategie für Token-Caching planen
Wählen Sie eine Zwischenspeicherungsstrategie aus, die Ihrer Bereitstellungstopologie entspricht.
- Verwenden des verteilten Caches für Bereitstellungen mit mehreren Servern
- Konfigurieren der entsprechenden Cache-Ablaufzeit
- Überwachen der Cacheleistung
Minimieren von Tokenanforderungen
Microsoft.Identity.Web zwischenspeichert Token automatisch. Beide Aufrufe im folgenden Beispiel verwenden dasselbe zwischengespeicherte Token wieder.
// Bad: Multiple token acquisitions
var profile = await _downstreamApi.GetForUserAsync<Profile>(
"API",
options => options.RelativePath = "profile");
var settings = await _downstreamApi.GetForUserAsync<Settings>(
"API",
options => options.RelativePath = "settings");
// Good: Single token, multiple calls (token is cached)
// Both calls use the same cached token
var profile = await _downstreamApi.GetForUserAsync<Profile>(
"API",
options => options.RelativePath = "profile");
var settings = await _downstreamApi.GetForUserAsync<Settings>(
"API",
options => options.RelativePath = "settings");
Parallele API-Aufrufe durchführen
Rufen Sie mehrere nachgeschaltete APIs gleichzeitig auf, um die Gesamtlatenz zu reduzieren.
// Call multiple APIs in parallel
var profileTask = _downstreamApi.GetForUserAsync<Profile>(
"API1",
options => options.RelativePath = "profile");
var settingsTask = _downstreamApi.GetForUserAsync<Settings>(
"API2",
options => options.RelativePath = "settings");
await Task.WhenAll(profileTask, settingsTask);
var profile = profileTask.Result;
var settings = settingsTask.Result;
Durchsuchen verwandter Inhalte
Hier finden Sie weitere Anleitungen für verwandte Szenarien.