Skip to main content

entra-id-aspire-authentication

Guide for adding Microsoft Entra ID (Azure AD) authentication to .NET Aspire applications. Use this when asked to add authentication, Entra ID, Azure AD, OIDC, or identity to an Aspire app, or when working with Microsoft.Identity.Web in Aspire projects.

설치로 이동

소스 정보

저장소
AzureAD/microsoft-identity-web
최근 소스 활동
2026년 3월 20일 16:07
감지된 SKILL.md 언어
영어
스타
787
포크
273

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
3 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
entra-id-aspire-authentication
description
Guide for adding Microsoft Entra ID (Azure AD) authentication to .NET Aspire applications. Use this when asked to add authentication, Entra ID, Azure AD, OIDC, or identity to an Aspire app, or when working with Microsoft.Identity.Web in Aspire projects.
license
MIT
# Entra ID Authentication for .NET Aspire Applications This skill helps you integrate **Microsoft Entra ID** (Azure AD) authentication into **.NET Aspire** distributed applications using **Microsoft.Identity.Web**. ## When to Use This Skill - Adding user authentication to Aspire apps - Protecting APIs with JWT Bearer authentication - Configuring OIDC sign-in for Blazor Server - Setting up token acquisition for downstream API calls - Implementing service-to-service authentication ## Architecture Overview ``` User Browser → Blazor Server (OIDC) → Entra ID → Access Token → Protected API (JWT) ``` **Key Components:** - **Blazor Frontend**: Uses `AddMicrosoftIdentityWebApp` for OIDC + `MicrosoftIdentityMessageHandler` for token attachment - **API Backend**: Uses `AddMicrosoftIdentityWebApi` for JWT validation - **Aspire**: Service discovery with `https+http://servicename` URLs --- ## Pre-Implementation Checklist Before starting, the agent MUST: ### 1. Detect Project Types Scan each project's `Program.cs` to identify its type: ```powershell # Find all Program.cs files in solution Get-ChildItem -Recurse -Filter "Program.cs" | ForEach-Object { $content = Get-Content $_.FullName -Raw $projectDir = Split-Path $_.FullName -Parent $projectName = Split-Path $projectDir -Leaf # Skip AppHost and ServiceDefaults if ($projectName -match "AppHost|ServiceDefaults") { return } $isWebApp = $content -match "AddRazorComponents|MapRazorComponents|AddServerSideBlazor" $isApi = $content -match "MapGet|MapPost|MapPut|MapDelete|AddControllers" if ($isWebApp) { Write-Host "WEB APP: $projectName (has Razor/Blazor components)" } elseif ($isApi) { Write-Host "API: $projectName (exposes endpoints)" } } ``` **Detection rules:** | Pattern in `Program.cs` | Project Type | |------------------------|--------------| | `AddRazorComponents` / `MapRazorComponents` / `AddServerSideBlazor` | **Blazor Web App** | | `MapGet` / `MapPost` / `AddControllers` (without Razor) | **Web API** | > **Note:** APIs can call other APIs (downstream). The Aspire `.WithReference()` shows service dependencies, not necessarily web-to-API relationships. ### 2. Confirm with User **AGENT: Show detected topology and ask for confirmation:** > "I detected: > - **Web App** (Blazor): `{webProjectName}` > - **API**: `{apiProjectName}` > > The web app will authenticate users and call the API. Is this correct?" ### 3. Establish Workflow **AGENT: Explain the two-phase approach:** > "I'll implement authentication in two phases: > > **Phase 1 (now):** Add authentication code with placeholder values. The app will **build** but won't **run** until app registrations are configured. > > **Phase 2 (after):** Use the `entra-id-aspire-provisioning` skill to create Entra ID app registrations and update the configuration with real values. > > Ready to proceed with Phase 1?" --- ## Implementation Checklist **CRITICAL: Complete ALL steps in order. Do not skip any step.** ### API Project Steps - [ ] Step 1.1: Add Microsoft.Identity.Web package - [ ] Step 1.2: Update appsettings.json with AzureAd section - [ ] Step 1.3: Update Program.cs with JWT Bearer authentication - [ ] Step 1.4: Add RequireAuthorization() to protected endpoints ### Web/Blazor Project Steps - [ ] Step 2.1: Add Microsoft.Identity.Web package - [ ] Step 2.2: Update appsettings.json with AzureAd and scopes - [ ] Step 2.3: Update Program.cs with OIDC, token acquisition, and **BlazorAuthenticationChallengeHandler** - [ ] Step 2.4: Verify Microsoft.Identity.Web version includes Blazor helpers (v3.3.0+) - [ ] Step 2.5: Create UserInfo.razor component (LOGIN BUTTON) - [ ] Step 2.6: Update MainLayout.razor to include UserInfo - [ ] Step 2.7: Update Routes.razor with AuthorizeRouteView - [ ] Step 2.8: Store client secret in user-secrets - [ ] Step 2.9: Add try/catch with ChallengeHandler on **every page calling APIs** --- ## Step-by-Step Implementation ### Prerequisites 1. .NET Aspire solution with API and Web (Blazor) projects 2. Azure AD tenant > **Two-phase workflow:** > - **Phase 1**: Add authentication code with placeholder values → App will **build** but **not run** > - **Phase 2**: Run `entra-id-aspire-provisioning` skill to create app registrations → App will **run** ### Part 1: Protect the API with JWT Bearer **1.1 Add Package:** ```powershell cd MyService.ApiService dotnet add package Microsoft.Identity.Web ``` **1.2 Configure `appsettings.json`:** ```json { "AzureAd": { "Instance": "https://login.microsoftonline.com/", "TenantId": "<your-tenant-id>", "ClientId": "<your-api-client-id>", "Audiences": ["api://<your-api-client-id>"] } } ``` **1.3 Update `Program.cs`:** ```csharp using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Identity.Web; var builder = WebApplication.CreateBuilder(args); builder.AddServiceDefaults(); // Add JWT Bearer authentication builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd")); builder.Services.AddAuthorization(); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); // Protect endpoints app.MapGet("/weatherforecast", () => { /* ... */ }) .RequireAuthorization(); app.Run(); ``` ### Part 2: Configure Blazor Frontend **2.1 Add Package:** ```powershell cd MyService.Web dotnet add package Microsoft.Identity.Web ``` **2.2 Configure `appsettings.json`:** ```json { "AzureAd": { "Instance": "https://login.microsoftonline.com/", "Domain": "<your-tenant>.onmicrosoft.com", "TenantId": "<tenant-guid>", "ClientId": "<web-app-client-id>", "CallbackPath": "/signin-oidc", "ClientCredentials": [ { "SourceType": "ClientSecret", "ClientSecret": "<your-client-secret>" } ] }, "WeatherApi": { "Scopes": ["api://<api-client-id>/.default"] } } ``` **2.3 Update `Program.cs`:** ```csharp using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.Identity.Web; var builder = WebApplication.CreateBuilder(args); builder.AddServiceDefaults(); // Authentication + token acquisition builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd")) .EnableTokenAcquisitionToCallDownstreamApi() .AddInMemoryTokenCaches(); builder.Services.AddCascadingAuthenticationState(); builder.Services.AddRazorComponents().AddInteractiveServerComponents(); // Add Blazor authentication challenge handler for incremental consent and Conditional Access builder.Services.AddScoped<BlazorAuthenticationChallengeHandler>(); // HttpClient with automatic token attachment builder.Services.AddHttpClient<WeatherApiClient>(client => { client.BaseAddress = new("https+http://apiservice"); // Aspire service discovery }) .AddMicrosoftIdentityMessageHandler(builder.Configuration.GetSection("WeatherApi").Bind); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); app.UseAntiforgery(); app.MapRazorComponents<App>().AddInteractiveServerRenderMode(); app.MapGroup("/authentication").MapLoginAndLogout(); app.Run(); ``` **2.4 Blazor authentication helpers:** These helpers ship in the `Microsoft.Identity.Web` NuGet package (version 3.3.0+). Simply add `using Microsoft.Identity.Web;` — no file copying required. > **AGENT:** The `LoginLogoutEndpointRouteBuilderExtensions` class provides the `MapLoginAndLogout()` extension method with support for incremental consent and Conditional Access. The `BlazorAuthenticationChallengeHandler` class handles authentication challenges in Blazor Server components. Both are now included in Microsoft.Identity.Web and are automatically available once you reference the package. **2.6 Create UserInfo Component (`Components/UserInfo.razor`) — THE LOGIN BUTTON:** > **CRITICAL: This step is frequently forgotten. Without this, users have no way to log in!** ```razor @using Microsoft.AspNetCore.Components.Authorization <AuthorizeView> <Authorized> <span class="nav-item">Hello, @context.User.Identity?.Name</span> <form action="/authentication/logout" method="post" class="nav-item"> <AntiforgeryToken /> <input type="hidden" name="returnUrl" value="/" /> <button type="submit" class="btn btn-link nav-link">Logout</button> </form> </Authorized> <NotAuthorized> <a href="/authentication/login?returnUrl=/" class="nav-link">Login</a> </NotAuthorized> </AuthorizeView> ``` **2.7 Update MainLayout.razor to include UserInfo:** Find the `<main>` or navigation section in `Components/Layout/MainLayout.razor` and add the UserInfo component: ```razor @inherits LayoutComponentBase <div class="page"> <div class="sidebar"> <NavMenu /> </div> <main> <div class="top-row px-4"> <UserInfo /> @* <-- ADD THIS LINE *@ </div> <article class="content px-4"> @Body </article> </main> </div> ``` **2.8 Update Routes.razor for AuthorizeRouteView:** Replace `RouteView` with `AuthorizeRouteView` in `Components/Routes.razor`: ```razor @using Microsoft.AspNetCore.Components.Authorization <Router AppAssembly="typeof(Program).Assembly"> <Found Context="routeData"> <AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)"> <NotAuthorized> <p>You are not authorized to view this page.</p> <a href="/authentication/login">Login</a> </NotAuthorized> </AuthorizeRouteView> <FocusOnNavigate RouteData="routeData" Selector="h1" /> </Found> </Router> ``` **2.9 Store Client Secret in User Secrets:** > **Never commit secrets to source control!** ```powershell cd MyService.Web dotnet user-secrets init dotnet user-secrets set "AzureAd:ClientCredentials:0:ClientSecret" "<your-client-secret>" ``` Then update `appsettings.json` to reference user secrets (remove the hardcoded secret): ```jsonc { "AzureAd": { "ClientCredentials": [ { // For more options see https://aka.ms/ms-id-web/credentials "SourceType": "ClientSecret" } ] } } ``` --- ## Common Patterns ### Protect Blazor Pages ```razor @page "/weather" @attribute [Authorize] ``` ### Scope Validation in API ```csharp app.MapGet("/weatherforecast", () => { /* ... */ }) .RequireAuthorization() .RequireScope("access_as_user"); ``` ### App-Only Tokens (Service-to-Service) ```csharp .AddMicrosoftIdentityMessageHandler(options => { options.Scopes.Add("api://<api-client-id>/.default"); options.RequestAppToken = true; }); ``` ### Override Scopes Per Request ```csharp var request = new HttpRequestMessage(HttpMethod.Get, "/endpoint") .WithAuthenticationOptions(options => { options.Scopes.Clear(); options.Scopes.Add("api://<client-id>/specific.scope"); }); ``` ### Production: Use Managed Identity ```json { "AzureAd": { "ClientCredentials": [ { "SourceType": "SignedAssertionFromManagedIdentity", "ManagedIdentityClientId": "<user-assigned-mi-client-id>" } ] } } ```
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기