# Azure Identity (Microsoft Identity)

Especialista em autenticação com Microsoft Identity Platform para aplicações .NET/Blazor.

## Responsabilidades

1. **Configurar Microsoft Identity** em projetos .NET
2. **Implementar autenticação** com Azure AD / Entra ID
3. **Gerenciar tokens** e autorização
4. **Integrar com APIs** protegidas

## Triggers

Keywords: `identity`, `entra`, `azure ad`, `microsoft auth`, `msal`, `oauth`, `oidc`, `microsoft identity`

## Sobre Microsoft Identity

- **Plataforma oficial** da Microsoft para autenticação
- **Suporta**: Azure AD, Microsoft accounts, B2C
- **SDK nativo**: Microsoft.Identity.Web
- **Ideal para**: Enterprise, Azure-first, Microsoft 365

## Instalação

```bash
dotnet add package Microsoft.Identity.Web
dotnet add package Microsoft.Identity.Web.UI  # Para Blazor
```

## Configuração Básica

```csharp
// appsettings.json
{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "yourdomain.onmicrosoft.com",
    "TenantId": "your-tenant-id",
    "ClientId": "your-client-id",
    "ClientSecret": "${AZURE_AD_CLIENT_SECRET}",
    "CallbackPath": "/signin-oidc"
  }
}

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

builder.Services.AddControllersWithViews()
    .AddMicrosoftIdentityUI();

builder.Services.AddAuthorization();

// Pipeline
app.UseAuthentication();
app.UseAuthorization();
```

## Blazor Server

```csharp
// Program.cs
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));

builder.Services.AddControllersWithViews()
    .AddMicrosoftIdentityUI();

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor()
    .AddMicrosoftIdentityConsentHandler();

// App.razor
<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(App).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                <NotAuthorized>
                    @if (!context.User.Identity?.IsAuthenticated ?? true)
                    {
                        <RedirectToLogin />
                    }
                    else
                    {
                        <p>Você não tem permissão para acessar este recurso.</p>
                    }
                </NotAuthorized>
            </AuthorizeRouteView>
        </Found>
    </Router>
</CascadingAuthenticationState>

// Components/RedirectToLogin.razor
@inject NavigationManager Navigation

@code {
    protected override void OnInitialized()
    {
        var returnUrl = Uri.EscapeDataString(Navigation.Uri);
        Navigation.NavigateTo($"MicrosoftIdentity/Account/SignIn?redirectUri={returnUrl}", forceLoad: true);
    }
}
```

## Páginas Protegidas

```razor
@* Pages/Secure.razor *@
@page "/secure"
@attribute [Authorize]

<h1>Área Protegida</h1>

<AuthorizeView>
    <Authorized>
        <p>Bem-vindo, @context.User.Identity?.Name!</p>
        <p>Email: @context.User.FindFirst("preferred_username")?.Value</p>
    </Authorized>
</AuthorizeView>

@* Por role *@
<AuthorizeView Roles="Admin">
    <Authorized>
        <AdminPanel />
    </Authorized>
</AuthorizeView>

@* Por policy *@
<AuthorizeView Policy="RequireManagerRole">
    <Authorized>
        <ManagerDashboard />
    </Authorized>
</AuthorizeView>
```

## Proteger API

```csharp
// Program.cs para API
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

builder.Services.AddAuthorization();

// Controller
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class ProfileController : ControllerBase
{
    [HttpGet]
    public IActionResult GetProfile()
    {
        return Ok(new
        {
            UserId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value,
            Name = User.Identity?.Name,
            Email = User.FindFirst("preferred_username")?.Value
        });
    }

    [HttpGet("admin")]
    [Authorize(Roles = "Admin")]
    public IActionResult AdminOnly()
    {
        return Ok("Admin access");
    }
}
```

## Chamar APIs Protegidas (Downstream APIs)

```csharp
// Program.cs
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddMicrosoftGraph(builder.Configuration.GetSection("Graph"))
    .AddInMemoryTokenCaches();

// Service
public class ProfileService
{
    private readonly GraphServiceClient _graphClient;

    public ProfileService(GraphServiceClient graphClient)
    {
        _graphClient = graphClient;
    }

    public async Task<User> GetCurrentUserAsync()
    {
        return await _graphClient.Me.GetAsync();
    }

    public async Task<byte[]?> GetProfilePhotoAsync()
    {
        try
        {
            var photoStream = await _graphClient.Me.Photo.Content.GetAsync();
            if (photoStream is null) return null;

            using var memoryStream = new MemoryStream();
            await photoStream.CopyToAsync(memoryStream);
            return memoryStream.ToArray();
        }
        catch
        {
            return null;
        }
    }
}
```

## Authorization Policies

```csharp
// Program.cs
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("RequireAdmin", policy =>
        policy.RequireRole("Admin"));

    options.AddPolicy("RequireManager", policy =>
        policy.RequireAssertion(context =>
            context.User.IsInRole("Admin") ||
            context.User.IsInRole("Manager")));

    options.AddPolicy("RequireVerifiedEmail", policy =>
        policy.RequireClaim("email_verified", "true"));
});
```

## Multi-tenant

```csharp
// appsettings.json para multi-tenant
{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "common",  // ou "organizations" para apenas work accounts
    "ClientId": "your-client-id",
    "ClientSecret": "${AZURE_AD_CLIENT_SECRET}"
  }
}

// Validar tenant
builder.Services.Configure<OpenIdConnectOptions>(
    OpenIdConnectDefaults.AuthenticationScheme,
    options =>
    {
        options.TokenValidationParameters.IssuerValidator = (issuer, token, parameters) =>
        {
            // Validar que o tenant é permitido
            var allowedTenants = new[] { "tenant-id-1", "tenant-id-2" };
            var tenantId = issuer.Split('/')[3];

            if (!allowedTenants.Contains(tenantId))
                throw new SecurityTokenInvalidIssuerException("Tenant not allowed");

            return issuer;
        };
    });
```

## Azure AD B2C

```csharp
// appsettings.json
{
  "AzureAdB2C": {
    "Instance": "https://yourtenant.b2clogin.com",
    "Domain": "yourtenant.onmicrosoft.com",
    "TenantId": "your-tenant-id",
    "ClientId": "your-client-id",
    "SignUpSignInPolicyId": "B2C_1_signupsignin",
    "ResetPasswordPolicyId": "B2C_1_passwordreset",
    "EditProfilePolicyId": "B2C_1_editprofile"
  }
}

// Program.cs
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAdB2C"));
```

## Documentação de Referência

- [Microsoft Identity Platform](https://learn.microsoft.com/en-us/entra/identity-platform/)
- [Microsoft.Identity.Web](https://learn.microsoft.com/en-us/entra/msal/dotnet/)
- [Blazor + Azure AD](https://learn.microsoft.com/en-us/aspnet/core/blazor/security/server/)
- [Microsoft Graph](https://learn.microsoft.com/en-us/graph/overview)
- [Azure AD B2C](https://learn.microsoft.com/en-us/azure/active-directory-b2c/)

## Checklist de Integração

- [ ] App registrado no Azure Portal
- [ ] Client ID e Tenant ID configurados
- [ ] Client Secret no Key Vault
- [ ] Redirect URIs configurados
- [ ] API permissions definidas
- [ ] Token caching configurado
- [ ] Authorization policies criadas
- [ ] Logout flow implementado
- [ ] Error handling para tokens expirados

---

*MORPH-SPEC by Polymorphism Tech*
