> ## Documentation Index
> Fetch the complete documentation index at: https://docs.diyanet.pro/llms.txt
> Use this file to discover all available pages before exploring further.

# BuildingBlocks.Security.BotProtection

> Google reCAPTCHA v3 doğrulaması — Polly retry'lı dayanıklı HTTP istemcisi, skor + action + hostname kontrolü.

`BuildingBlocks.Security.BotProtection`, public form ve giriş akışlarını otomatik botlara karşı korumak için **Google reCAPTCHA v3** doğrulaması sağlar. v3 görsel bir challenge sunmaz; her isteğe `0.0–1.0` arası bir **skor** verir. Validator skoru eşikle, beklenen `action` ile ve (opsiyonel) hostname ile karşılaştırır; uyuşmazlıkta exception fırlatır.

## `IRecaptchaValidator`

| Metot           | İmza                                                                                   | Davranış                                                                        |
| --------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `ValidateAsync` | `Task ValidateAsync(string token, string expectedAction, CancellationToken = default)` | Başarılıysa sessizce döner; başarısızsa `RecaptchaValidationException` fırlatır |

`GoogleRecaptchaValidator` implementasyonu Google `siteverify` API'sine `secret` + `response` (token) gönderir ve yanıtı sırayla kontrol eder:

```csharp theme={null}
public async Task ValidateAsync(string token, string expectedAction, CancellationToken ct = default)
{
    if (string.IsNullOrWhiteSpace(token))
        throw new RecaptchaValidationException("Recaptcha token gerekli.");

    var content = new FormUrlEncodedContent(new Dictionary<string, string>
    {
        ["secret"]   = _options.SecretKey,
        ["response"] = token
    });

    var response = await _httpClient.PostAsync(
        "https://www.google.com/recaptcha/api/siteverify", content, ct);
    var result = JsonSerializer.Deserialize<RecaptchaResponse>(
        await response.Content.ReadAsStringAsync(ct));

    if (result is null || !result.Success)
        throw new RecaptchaValidationException("Recaptcha doğrulaması başarısız oldu.");
    if (result.Score < _options.ScoreThreshold)
        throw new RecaptchaValidationException($"Recaptcha puanı çok düşük: {result.Score}");
    if (!string.Equals(result.Action, expectedAction, StringComparison.Ordinal))
        throw new RecaptchaValidationException("Recaptcha işlemi uyuşmazlığı.");
    if (_options.ValidateHostname &&
        !_options.AllowedHostnames.Contains(result.Hostname, StringComparer.OrdinalIgnoreCase))
        throw new RecaptchaValidationException("Hostname mismatch.");
}
```

| Kontrol                    | Hata                                    |
| -------------------------- | --------------------------------------- |
| Token boş                  | `Recaptcha token gerekli.`              |
| `success != true`          | `Recaptcha doğrulaması başarısız oldu.` |
| `score < ScoreThreshold`   | `Recaptcha puanı çok düşük: {score}`    |
| `action != expectedAction` | `Recaptcha işlemi uyuşmazlığı.`         |
| Hostname allowlist dışı    | `Hostname mismatch.`                    |

## Konfigürasyon — `RecaptchaOptions`

`Recaptcha` bölümüne bind edilir.

| Üye                | Tip        | Varsayılan                                        | Açıklama                      |
| ------------------ | ---------- | ------------------------------------------------- | ----------------------------- |
| `Url`              | `string`   | `https://www.google.com/recaptcha/api/siteverify` | Doğrulama endpoint'i          |
| `SecretKey`        | `string`   | —                                                 | reCAPTCHA secret (gizli)      |
| `ScoreThreshold`   | `float`    | `0.6`                                             | Bu skorun altı reddedilir     |
| `ValidateHostname` | `bool`     | `true`                                            | Hostname kontrolü açık/kapalı |
| `AllowedHostnames` | `string[]` | `[]`                                              | İzinli hostname listesi       |

```json theme={null}
{
  "Recaptcha": {
    "SecretKey": "6Lc...secret",
    "ScoreThreshold": 0.6,
    "ValidateHostname": true,
    "AllowedHostnames": ["app.example", "www.example"]
  }
}
```

<Note>
  `SiteKey` frontend tarafına aittir (token üretimi için) ve backend `RecaptchaOptions`'ında yer almaz; backend yalnızca `SecretKey` ile doğrulama yapar.
</Note>

### DI kaydı — `AddRecaptcha`

Validator, 5 sn timeout'lu ve **Polly retry'lı** typed `HttpClient` ile kaydedilir (3 deneme, 200 ms ara):

```csharp theme={null}
public static IServiceCollection AddRecaptcha(this IServiceCollection services, IConfiguration configuration)
{
    services.Configure<RecaptchaOptions>(configuration.GetSection("Recaptcha"));

    services.AddHttpClient<IRecaptchaValidator, GoogleRecaptchaValidator>(client =>
        {
            client.Timeout = TimeSpan.FromSeconds(5);
        })
        .AddTransientHttpErrorPolicy(policy =>
            policy.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(200)));

    return services;
}
```

## Kullanım — public komut içinde doğrulama

```csharp theme={null}
public class SubmitContactFormCommandHandler(IRecaptchaValidator recaptcha)
    : IRequestHandler<SubmitContactFormCommand, Unit>
{
    public async Task<Unit> Handle(SubmitContactFormCommand request, CancellationToken ct)
    {
        // expectedAction frontend'deki grecaptcha.execute({ action: "contact" }) ile eşleşmeli
        await recaptcha.ValidateAsync(request.RecaptchaToken, expectedAction: "contact", ct);

        // doğrulama geçtiyse iş mantığı...
        return Unit.Value;
    }
}
```

`RecaptchaValidationException` ProblemDetails katmanında uygun HTTP yanıtına çevrilir.

<Tip>
  Skor eşiğini akışa göre ayarlayın: düşük riskli public formlarda `0.5–0.6`, login gibi hassas akışlarda `0.7+`. `action` adını her zaman frontend `grecaptcha.execute` çağrısıyla birebir aynı tutun.
</Tip>

## İlgili

<CardGroup cols={2}>
  <Card title="Device Detector" icon="shield-halved" href="/building-blocks/device-detector">
    Risk skoru ile birlikte katmanlı bot savunması.
  </Card>

  <Card title="OTP" icon="key" href="/building-blocks/otp">
    Challenge durumunda ikinci faktör.
  </Card>

  <Card title="Frontend" icon="react" href="/api/frontend">
    React SPA'da reCAPTCHA token üretimi.
  </Card>

  <Card title="Kimlik doğrulama" icon="fingerprint" href="/security/authentication">
    Public giriş akışlarının korunması.
  </Card>
</CardGroup>
