# Clerk Auth

Especialista em autenticação com Clerk para aplicações .NET/Blazor.

## Responsabilidades

1. **Configurar Clerk** em projetos .NET
2. **Implementar autenticação** com SDK oficial
3. **Gerenciar sessões** e tokens
4. **Proteger endpoints** e páginas

## Triggers

Keywords: `clerk`, `auth`, `login`, `signup`, `authentication`, `session`, `jwt`, `user`

## Sobre o Clerk

- **Plataforma de autenticação** SaaS moderna
- **SDK oficial C#** lançado em Janeiro/2025
- **Suporta**: Email/senha, Social login, MFA, Passkeys
- **Dashboard** para gerenciar usuários
- **Ideal para**: SaaS B2C, MVPs rápidos

## Instalação

```bash
dotnet add package Clerk.Net.AspNetCore.Security
```

## Configuração Básica

```csharp
// appsettings.json
{
  "Clerk": {
    "SecretKey": "${CLERK_SECRET_KEY}",
    "PublishableKey": "pk_test_xxx"  // Para frontend
  }
}

// Program.cs
builder.Services.AddClerk(builder.Configuration);

// Ou manualmente
builder.Services.AddClerk(options =>
{
    options.SecretKey = builder.Configuration["Clerk:SecretKey"]!;
});

// Adicionar autenticação
builder.Services.AddAuthentication(ClerkAuthenticationDefaults.AuthenticationScheme)
    .AddClerk(options =>
    {
        options.Authority = "https://clerk.{your-instance}.com";
        options.ValidAudiences = new[] { "your-app-id" };
    });

builder.Services.AddAuthorization();

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

## Proteger Endpoints (Minimal API)

```csharp
// Endpoint protegido
app.MapGet("/api/profile", async (ClaimsPrincipal user, IClerkClient clerk) =>
{
    var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);

    if (userId is null)
        return Results.Unauthorized();

    var clerkUser = await clerk.Users.GetUserAsync(userId);

    return Results.Ok(new
    {
        Id = clerkUser.Id,
        Email = clerkUser.EmailAddresses.FirstOrDefault()?.EmailAddress,
        Name = $"{clerkUser.FirstName} {clerkUser.LastName}"
    });
})
.RequireAuthorization();

// Endpoint público
app.MapGet("/api/public", () => "Hello World!")
    .AllowAnonymous();
```

## Proteger Endpoints (Controllers)

```csharp
[ApiController]
[Route("api/[controller]")]
[Authorize]  // Requer autenticação
public class ProfileController : ControllerBase
{
    private readonly IClerkClient _clerk;

    public ProfileController(IClerkClient clerk)
    {
        _clerk = clerk;
    }

    [HttpGet]
    public async Task<IActionResult> GetProfile()
    {
        var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);

        if (userId is null)
            return Unauthorized();

        var user = await _clerk.Users.GetUserAsync(userId);

        return Ok(new
        {
            user.Id,
            Email = user.EmailAddresses.FirstOrDefault()?.EmailAddress,
            Name = $"{user.FirstName} {user.LastName}"
        });
    }

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

## Blazor Server

```csharp
// Program.cs
builder.Services.AddClerk(builder.Configuration);
builder.Services.AddAuthentication(ClerkAuthenticationDefaults.AuthenticationScheme)
    .AddClerk();

// App.razor
<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(App).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                <NotAuthorized>
                    <RedirectToLogin />
                </NotAuthorized>
            </AuthorizeRouteView>
        </Found>
    </Router>
</CascadingAuthenticationState>

// Components/RedirectToLogin.razor
@inject NavigationManager Navigation

@code {
    protected override void OnInitialized()
    {
        Navigation.NavigateTo($"/sign-in?redirect_url={Uri.EscapeDataString(Navigation.Uri)}", forceLoad: true);
    }
}
```

## Páginas Protegidas

```razor
@* Pages/Dashboard.razor *@
@page "/dashboard"
@attribute [Authorize]

<h1>Dashboard</h1>

<AuthorizeView>
    <Authorized>
        <p>Bem-vindo, @context.User.Identity?.Name!</p>
    </Authorized>
</AuthorizeView>

@* Verificar role específica *@
<AuthorizeView Roles="admin">
    <Authorized>
        <AdminPanel />
    </Authorized>
    <NotAuthorized>
        <p>Acesso restrito a administradores.</p>
    </NotAuthorized>
</AuthorizeView>
```

## Clerk Client para Operações

```csharp
// Buscar usuário
var user = await _clerk.Users.GetUserAsync(userId);

// Listar usuários
var users = await _clerk.Users.GetUserListAsync(new()
{
    Limit = 10,
    Offset = 0
});

// Atualizar metadata
await _clerk.Users.UpdateUserMetadataAsync(userId, new()
{
    PublicMetadata = new Dictionary<string, object>
    {
        ["plan"] = "pro",
        ["company"] = "Acme Inc"
    }
});

// Deletar usuário
await _clerk.Users.DeleteUserAsync(userId);
```

## Webhooks

```csharp
// Controllers/ClerkWebhookController.cs
[ApiController]
[Route("api/webhooks/clerk")]
public class ClerkWebhookController : ControllerBase
{
    private readonly IUserService _userService;

    public ClerkWebhookController(IUserService userService)
    {
        _userService = userService;
    }

    [HttpPost]
    public async Task<IActionResult> HandleWebhook([FromBody] ClerkWebhookPayload payload)
    {
        // Verificar signature (importante em produção!)

        switch (payload.Type)
        {
            case "user.created":
                await _userService.SyncUserAsync(payload.Data);
                break;

            case "user.updated":
                await _userService.UpdateUserAsync(payload.Data);
                break;

            case "user.deleted":
                await _userService.DeleteUserAsync(payload.Data.Id);
                break;
        }

        return Ok();
    }
}
```

## Clerk vs Azure Identity

| Aspecto | Clerk | Azure Identity |
|---------|-------|----------------|
| **Setup** | Mais rápido | Mais complexo |
| **Custo** | Freemium (5k MAU grátis) | Grátis (Azure AD) |
| **Multi-tenant** | Built-in | Requer config |
| **Social login** | 20+ providers | Limitado |
| **UI components** | Pré-construídos | Precisa criar |
| **Ideal para** | SaaS B2C | Enterprise/Azure |

## Documentação de Referência

- [Clerk C# SDK](https://clerk.com/changelog/2025-01-09-csharp-sdk)
- [Clerk.Net GitHub](https://github.com/Hawxy/Clerk.Net)
- [Clerk Documentation](https://clerk.com/docs)
- [Webhooks](https://clerk.com/docs/integrations/webhooks)

## Checklist de Integração

- [ ] Secret Key no Key Vault (não hardcoded)
- [ ] Publishable Key para frontend
- [ ] Authentication scheme configurado
- [ ] Authorization policies definidas
- [ ] Webhook endpoint para sync de usuários
- [ ] Verificação de webhook signature
- [ ] Redirect após login configurado
- [ ] Error handling para sessões expiradas

---

*MORPH-SPEC by Polymorphism Tech*
