Anropa underordnade API:er från webbappar

Den här guiden beskriver hur du anropar underordnade API:er från ASP.NET Core- och OWIN-webbprogram med hjälp av Microsoft. Identity.Web. I webbappar hämtar du token för den inloggade användarens räkning för att anropa API:er med delegerade behörigheter.

Förstå tokenflödet

När en användare loggar in på din webbapp kan du anropa underordnade API:er (Microsoft Graph, Azure tjänster eller anpassade API:er) för deras räkning. Microsoft. Identity.Web hanterar tokenförvärv, cachelagring och automatisk uppdatering.

Granska användartokenflödet

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

Förutsättningar för granskning

Kontrollera att din miljö uppfyller följande krav innan du börjar.

  • Webbapp som konfigurerats med OpenID Connect-autentisering
  • Användarinloggning fungerar
  • Appregistrering med konfigurerade API-behörigheter
  • Användarmedgivande erhållet (eller administratörsmedgivande beviljat)

Implementera ASP.NET Core

1. Konfigurera autentisering och tokenförvärv

Lägg till autentiseringstjänster och aktivera tokenförvärv i filen Program.cs .

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. Konfigurera appsettings.json

Definiera din Microsoft Entra ID appregistrering och underordnade API-inställningar i 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"]
    }
  }
}

Viktigt: För webbappar som anropar underordnade API:er behöver du klientautentiseringsuppgifter (certifikat eller hemlighet) utöver inloggningskonfigurationen.

3. Lägg till underordnat API-stöd

Välj något av följande alternativ för att registrera dina underordnade API:er.

Alternativ A: Registrera namngivna API:er

Följande kod registrerar flera underordnade API:er från konfigurationen.

using Microsoft.Identity.Web;

// Register multiple downstream APIs
builder.Services.AddDownstreamApis(
    builder.Configuration.GetSection("DownstreamApis"));

Option B: Använd Microsoft Graph Helper

Följande kod registrerar Microsoft Graph SDK-klienten från konfigurationen.

// Install: Microsoft.Identity.Web.GraphServiceClient
builder.Services.AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApis:GraphAPI"));

4. Anropa nedströms-API:et från kontrollanten

Mata IDownstreamApi in i kontrollanten och anropa API:et för den inloggade användarens räkning.

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. Anropa underordnat API från Razor-sidan

Mata IDownstreamApi in i razor-sidmodellen och anropa API:et.

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;
        }
    }
}

Anropa Microsoft Graph

För Microsoft Graph API-anrop använder du den dedikerade GraphServiceClient.

Installera paket

Installera Microsoft Graph-paketet för Microsoft. Identity.Web.

dotnet add package Microsoft.Identity.Web.GraphServiceClient

Konfigurera Graph-klienten i startkoden.

// Startup configuration
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddMicrosoftGraph(options =>
    {
        options.Scopes = "user.read mail.read";
    })
    .AddInMemoryTokenCaches();

Anropa Graph API

Mata in GraphServiceClient i kontrollanten för att anropa Microsoft Graph slutpunkter.

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

Läs mer om Microsoft Graph integrering


Anropa Azure SDKs klientbibliotek

För att anropa Azure tjänster använder du MicrosoftIdentityTokenCredential.

Installera paket

Installera de nödvändiga Azure SDKs paketen.

dotnet add package Microsoft.Identity.Web.Azure
dotnet add package Azure.Storage.Blobs

Registrera Microsoft Entra tokenautentiseringsuppgifter i startkoden.

using Microsoft.Identity.Web;

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

// Add Azure token credential
builder.Services.AddMicrosoftIdentityAzureTokenCredential();

Åtkomst Azure tjänster

Mata in tokenautentiseringsuppgifterna och använd den med Azure SDKs klienter.

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

Läs mer om Azure SDKs integrering


Anropa anpassade API:er med IDownstreamApi

För dina egna REST-API:er IDownstreamApi tillhandahåller en enkel, konfigurationsdriven metod.

Konfigurera API:et

Definiera de underordnade API-inställningarna i appsettings.json.

{
  "DownstreamApis": {
    "MyAPI": {
      "BaseUrl": "https://myapi.example.com",
      "Scopes": ["api://my-api-id/access_as_user"],
      "RequestAppToken": false
    }
  }
}

Skicka GET-begäranden

Hämta data från det underordnade API:et med valfria frågeparametrar.

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

Skicka POST-begäranden

Skapa en ny resurs i det underordnade API:et genom att publicera en begärandetext.

var newItem = new CreateItemRequest
{
    Name = "New Item",
    Description = "Item description"
};

var created = await _downstreamApi.PostForUserAsync<CreateItemRequest, CreatedItem>(
    "MyAPI",
    newItem,
    options => options.RelativePath = "api/items");

Skicka PUT- och DELETE-begäranden

Uppdatera eller ta bort resurser i det underordnade API:et.

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

Läs mer om anpassade API-anrop


Använda IAuthorizationHeaderProvider (avancerat)

För maximal kontroll över HTTP-begäranden använder du IAuthorizationHeaderProvider.

Registrera HTTP-klienten

Registrera en namngiven HTTP-klient för ditt underordnade API.

builder.Services.AddHttpClient("MyAPI", client =>
{
    client.BaseAddress = new Uri("https://myapi.example.com");
});

Skapa anpassade HTTP-begäranden

Skapa och skicka HTTP-begäranden med anpassade huvuden och auktorisering.

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>();
    }
}

Läs mer om anpassad HTTP-logik


När du anropar underordnade API:er kan ditt program behöva hantera scenarier där användarinteraktion krävs. Detta sker i tre huvudscenarier:

  1. Inkrementellt medgivande – Begära ytterligare behörigheter utöver vad som ursprungligen beviljades
  2. Villkorlig åtkomst – Uppfylla säkerhetskrav som MFA, enhetsefterlevnad eller platsprinciper
  3. Utrensning av tokencache – Återskapa tokencacheminnet efter programomstart eller cacheminnets utgång

Microsoft. Identity.Web tillhandahåller automatisk hantering av dessa scenarier med minimal kod som krävs.

Förstå flödet

När Microsoft. Identity.Web identifierar att användarinteraktion behövs, den genererar en MicrosoftIdentityWebChallengeUserException. Ramverket hanterar detta automatiskt via [AuthorizeForScopes] attributet eller MicrosoftIdentityConsentAndConditionalAccessHandler tjänsten (för Blazor), som:

  1. Omdirigerar användaren till Microsoft Entra ID för medgivande/autentisering
  2. Bevarar den ursprungliga begärande-URL:en
  3. Returnerar användaren till sitt avsedda mål när flödet har slutförts
  4. Cachelagrar de nyligen förvärvade tokens

Förutsättningar för granskning

Om du vill aktivera automatisk medgivandehantering kontrollerar du att du Program.cs har följande konfiguration.

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

Använd [AuthorizeForScopes] i MVC-styrenheter

Attributet [AuthorizeForScopes] , som anges för kontrollanter eller kontrollantåtgärder, hanterar MicrosoftIdentityWebChallengeUserException automatiskt genom att utmana användaren när ytterligare behörigheter behövs.

Deklarera omfång inline

Ange de nödvändiga omfången direkt i attributet.

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

Konfigurera omfång från apparinställningar

Lagra omfång i appsettings.json för bättre underhållbarhet:

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

Konfigurera Microsoft Entra External ID med användarflöden

För externa ID-program (B2C) med flera användarflöden anger du användarflödet i attributet.

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

Använd [AuthorizeForScopes] på Razor Pages

Använd [AuthorizeForScopes] för sidmodellklassen:

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

Blazor Server-program kräver explicit undantagshantering med hjälp av MicrosoftIdentityConsentAndConditionalAccessHandler tjänsten.

Konfigurera Program.cs

Registrera medgivandehanteraren för Blazor Server i startkoden.

builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDownstreamApis("TodoList", builder.Configuration.GetSection("DownstreamApis"))
    .AddInMemoryTokenCaches();

// Register the consent handler for Blazor
builder.Services.AddServerSideBlazor()
    .AddMicrosoftIdentityConsentHandler();

Skapa Blazor-komponenten

Omslut API-anrop med try-catch-block och använd ConsentHandler.HandleException() för att hantera samtyckesutmaningar.

@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);
        }
    }
}

Hantera undantag manuellt (avancerat)

Om du behöver logik för ett anpassat medgivandeflöde, hantera MicrosoftIdentityWebChallengeUserException explicit.

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

Hantera scenarier för villkorlig åtkomst

Principer för villkorlig åtkomst kan kräva ytterligare autentiseringsfaktorer. Hanteringen är identisk med inkrementellt medgivande:

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

Vanliga utlösare för villkorlig åtkomst:

  • Multifaktorautentisering (MFA)
  • Krav på kompatibel enhet
  • Betrodd nätverksplats
  • Godkännande av användningsvillkor
  • Krav på lösenordsändring

Följ metodtipsen

Tillämpa dessa rekommendationer när du implementerar medgivande och hantering av villkorsstyrd åtkomst.

Använda [AuthorizeForScopes] – Enklaste metoden för MVC-styrenheter och Razor Pages

Lagra omfång i konfiguration – Använd ScopeKeySection = "DownstreamApis:ApiName:Scopes:0" för att referera till omfång i appsettings.json

Tillämpa på kontrollantnivå – Ange standardomfattningar på kontrollanten, åsidosätt specifika åtgärder

Hantera undantag i Blazor – Alltid omsluta API-anrop med try-catch och använd ConsentHandler.HandleException()

Återkasta undantag – Om du fångar MicrosoftIdentityWebChallengeUserException, återkasta det så att [AuthorizeForScopes] kan bearbeta det

Testa villkorlig åtkomst – Kontrollera att din app hanterar MFA och andra CA-principer korrekt

Utelämna inte undantag – Att fånga utan att kasta igen bryter medgivandeflödet

Cachelagrar inte svar på obestämd tid – token upphör att gälla; utforma för omautentisering


Statiska behörigheter (administratörsmedgivande)

Alla behörigheter begärs under appregistreringen och godkänns av en klientadministratör:

Fördelar:

  • Användare ser aldrig medgivandeprompter
  • Krävs för Microsoft appar från första part
  • Enklare användarupplevelse

Nackdelar:

  • Kräver innehavaradministratörsengagemang
  • Överprivilegierad från början
  • Mindre flexibelt för scenarier med flera klientorganisationer

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

Inkrementellt medgivande (dynamiskt)

Behörigheter begärs efter behov under körning:

Fördelar:

  • Bättre säkerhet (principen om minsta behörighet)
  • Användare samtycker till vad de faktiskt använder
  • Fungerar för appar med flera klientorganisationer

Nackdelar:

  • Användare kan bli avbrutna av samtyckesmeddelanden
  • Kräver hantering MicrosoftIdentityWebChallengeUserException

Rekommendation: Använd inkrementellt medgivande för program med flera klientorganisationer. använda statiska behörigheter för företagsappar från första part där administratörsmedgivande garanteras


Konfigurera cachelagring av token

Microsoft.Identity.Web cachar token för att förbättra prestanda och minska anrop till Microsoft Entra.

Använd minnesintern cache (standard)

Lägg till en minnesintern tokencache för utvecklings- eller enserverscenarier.

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches(); // In-memory cache

Används för:

  • Utveckling
  • Installationer med en server
  • Liten användarbas

Limitations:

  • Delas inte mellan instanser
  • Förlorade vid omstart av app
  • Minnesförbrukningen ökar med användare

Konfigurera en distribuerad cache som Redis eller SQL Server för produktionsdistributioner.

// 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();

Används för:

  • Distributioner med flera servrar (lastbalanserade)
  • Scenarier för hög tillgänglighet
  • Stor användarbas
  • Beständig cache vid omstarter

Hantera fel vid tokeninhämtning

Fånga vanliga undantag

Följande kod visar hur du fångar och hanterar de vanligaste undantagen för tokenförvärv.

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

Implementera mjuk nedtrappning

Läs in valfria data från underordnade API:er och återgå till standardvärden när anrop misslyckas.

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

Implementera OWIN för .NET Framework

Följ dessa steg för OWIN-baserade webbprogram i .NET Framework.

1. Installera paket

Installera nödvändiga NuGet-paket.

Install-Package Microsoft.Identity.Web.OWIN
Install-Package Microsoft.Owin.Host.SystemWeb

2. Konfigurera start

Konfigurera Microsoft Entra autentisering och tokenförvärv i OWIN-startklassen.

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. Anropa underordnat API

Hämta en token och anropa det underordnade API:et från en MVC-styrenhet.

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-stöd har vissa skillnader jämfört med ASP.NET Core. Mer information finns i OWIN-dokumentationen .


Följ metodtips för säkerhet

Hantera omfång

Tillämpa principen om lägsta behörighet när du begär API-behörigheter.

Do:

  • Begär endast de behörigheter du behöver
  • Använda inkrementellt medgivande för avancerade funktioner
  • Dokumentera nödvändiga omfång i din app

Don't:

  • Begära onödiga behörigheter inledningsvis
  • Begär endast administratörsomfång utan motivering
  • Anta att alla omfång kommer att beviljas

Hantera token på ett säkert sätt

Följ dessa riktlinjer för att skydda åtkomsttoken i ditt program.

Do:

  • Låt Microsoft.Identity.Web hantera token
  • Använda distribuerad cache i produktion
  • Hantera misslyckanden vid tokenförvärv smidigt.

Don't:

  • Lagra tokenerna själv
  • Loggåtkomsttoken
  • Skicka token till programkod på klientsidan

Hantering av fel

Implementera robust felhantering för autentiserings- och API-anropsfel.

Do:

  • Fånga och hantera samtyckesundantag
  • Ge tydliga felmeddelanden till användare
  • Loggfel för felsökning

Don't:

  • Exponera tokenfel för användare
  • API-anrop som misslyckas tyst
  • Ignorera autentiseringsfel

Felsökning av vanliga problem

Granska de här lösningarna för autentiseringsfel som ofta påträffas.

Problem: "AADSTS65001: Användaren eller administratören har inte samtyckt"

Orsaka: Användaren har inte samtyckt till nödvändiga omfång.

Lösning:

catch (MicrosoftIdentityWebChallengeUserException ex)
{
    // Redirect to consent page
    return Challenge(
        new AuthenticationProperties { RedirectUri = Request.Path },
        OpenIdConnectDefaults.AuthenticationScheme);
}

Problem: "AADSTS50076: Multifaktorautentisering krävs"

Orsaka: Användaren måste slutföra MFA.

Lösning:

catch (MsalUiRequiredException)
{
    // Redirect user to sign in with MFA
    return Challenge(OpenIdConnectDefaults.AuthenticationScheme);
}

Problem: Token sparas inte mellan appomstarter

Orsaka: Använda minnesintern cache.

Solution: Växla till distribuerad cache (Redis, SQL Server eller Cosmos DB).

Problem: 401 Obehörig från underordnat API

Möjliga orsaker:

  • Fel omfång begärda
  • API-behörighet beviljas inte i appregistrering
  • Token har upphört att gälla

Lösning:

  1. Verifiera att omfång i appsettings.json matchar API-krav
  2. Kontrollera att appregistreringen har API-behörigheter
  3. Kontrollera att token är cachelagrade och uppdaterade

För detaljerad diagnostik: Se Loggnings- och diagnostikguiden för korrelations-ID:n, felsökning av tokencache och omfattande felsökningsmönster.


Optimera prestanda

Planera cachelagringsstrategi för token

Välj en cachelagringsstrategi som matchar din distributionstopologi.

  • Använda distribuerad cache för distributioner med flera servrar
  • Konfigurera lämplig cache förfallodatum
  • Övervaka cacheprestanda

Minimera tokenbegäranden

Microsoft. Identity.Web cachelagrar token automatiskt. Båda anropen i följande exempel återanvänder samma cachelagrade token.

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

Gör parallella API-anrop

Anropa flera underordnade API:er samtidigt för att minska den totala svarstiden.

// 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;

Hitta ytterligare vägledning för relaterade scenarier.