> ## 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.Otp

> Numerik OTP üretimi, HMAC-SHA256 stateless doğrulama, throttling ve SMS şablonu.

`BuildingBlocks.Otp`, tek kullanımlık parola (OTP) üretimi ve doğrulaması için altyapı sağlar. Kod **plaintext saklanmaz**; yalnızca HMAC-SHA256 özeti tutulur ve doğrulama stateless karşılaştırma ile yapılır. Paket aynı zamanda rate-limit politikalarını (throttle, günlük limit, deneme sayısı) konfigürasyon olarak taşır.

<Info>
  OTP'nin User aggregate ile entegrasyonu (`UserOtpChallenge`, `AddOtpChallenge`/`VerifyOtp`) ve SMS gönderimi (`OtpSmsService`, NetGSM) için bkz. [Domain](/domain/aggregates) ve [SMS servisi](/services/sms). Bu sayfa paket arayüzlerine odaklanır.
</Info>

## `IOtpService`

Üç yardımcı servisi (generator + hash + formatter) tek fasad altında toplayan orchestrator.

| Metot            | İmza                                  | Amaç                                                     |
| ---------------- | ------------------------------------- | -------------------------------------------------------- |
| `GenerateCode`   | `OtpCode GenerateCode()`              | `CodeLength` uzunluğunda rastgele numerik kod üretir     |
| `Hash`           | `string Hash(OtpCode code)`           | HMAC-SHA256 ile Base64 özet üretir (saklanan değer)      |
| `CreateLoginSms` | `string CreateLoginSms(OtpCode code)` | `LoginSmsTemplate`'teki `{code}`'u gerçek kodla doldurur |

### Alt servisler

| Arayüz                 | İmza                                                                   | İmplementasyon                     |
| ---------------------- | ---------------------------------------------------------------------- | ---------------------------------- |
| `IOtpCodeGenerator`    | `OtpCode GenerateNumericCode(int length)`                              | `NumericOtpCodeGenerator`          |
| `IOtpHashService`      | `string Hash(OtpCode code)` · `bool Verify(OtpCode code, string hash)` | `HmacOtpHashService` (HMAC-SHA256) |
| `IOtpMessageFormatter` | `string Format(string template, OtpCode code)`                         | `DefaultOtpMessageFormatter`       |

## `OtpCode`

`sealed` value object. Kod yalnızca rakamlardan oluşur; **loglama güvenlidir** — `ToString()` her zaman `"******"` döner, gerçek değere sadece `Reveal()` ile erişilir.

```csharp theme={null}
var code = OtpCode.Create("482913");
_logger.LogInformation("OTP: {Code}", code);   // → "OTP: ******"
string actual = code.Reveal();                  // → "482913" (yalnızca hash/SMS için)
```

| Üye                             | Davranış                                                 |
| ------------------------------- | -------------------------------------------------------- |
| `static OtpCode Create(string)` | Boş veya rakam-dışı değerde `ArgumentException` fırlatır |
| `string Reveal()`               | Gerçek kodu döndürür (yalnızca gerektiğinde)             |
| `override string ToString()`    | Daima `"******"` (logging-safe)                          |

<Warning>
  Kodu asla `Reveal()` çıktısıyla loglamayın. Hash daima HMAC-SHA256 + salt ile üretilir; karşılaştırma `OtpSecurity.FixedTimeEqualsBase64` ile **sabit-zamanlı** yapılarak timing attack engellenir.
</Warning>

## Konfigürasyon — `OtpOptions`

`Services:Otp` bölümüne bind edilir.

| Üye                         | Tip      | Varsayılan                             | Açıklama                                 |
| --------------------------- | -------- | -------------------------------------- | ---------------------------------------- |
| `CodeLength`                | `int`    | `6`                                    | Koddaki rakam sayısı                     |
| `ExpireMinutes`             | `int`    | `3`                                    | Kod geçerlilik süresi                    |
| `ThrottleSeconds`           | `int`    | `60`                                   | İki istek (resend) arası minimum süre    |
| `MaxRequestPerDay`          | `int`    | `20`                                   | Günlük maksimum OTP isteği               |
| `MaxVerifyAttempt`          | `int`    | `5`                                    | Tek kod için maksimum doğrulama denemesi |
| `MaxFailedVerifyPerDay`     | `int`    | `50`                                   | Günlük maksimum başarısız deneme         |
| `ChallengeRetentionMinutes` | `int`    | `15`                                   | Challenge state TTL'i                    |
| `AllowResendWhileActive`    | `bool`   | `false`                                | Aktif kod varken yeniden gönderime izin  |
| `HashSecretSalt`            | `string` | —                                      | HMAC anahtarı (gizli)                    |
| `LoginSmsTemplate`          | `string` | `"{code} giriş doğrulama kodunuzdur."` | SMS metni                                |

### DI kaydı — `AddOtp`

```csharp theme={null}
public static IServiceCollection AddOtp(this IServiceCollection services, IConfiguration configuration)
{
    services.Configure<OtpOptions>(configuration.GetSection("Services:Otp"));

    services.AddSingleton<IOtpCodeGenerator, NumericOtpCodeGenerator>();
    services.AddSingleton<IOtpHashService, HmacOtpHashService>();
    services.AddSingleton<IOtpMessageFormatter, DefaultOtpMessageFormatter>();
    services.AddSingleton<IOtpService, OtpService>();   // orchestrator

    return services;
}
```

```json theme={null}
{
  "Services": {
    "Otp": {
      "CodeLength": 6,
      "ExpireMinutes": 3,
      "ThrottleSeconds": 60,
      "MaxRequestPerDay": 20,
      "MaxVerifyAttempt": 5,
      "MaxFailedVerifyPerDay": 50,
      "ChallengeRetentionMinutes": 15,
      "AllowResendWhileActive": false,
      "HashSecretSalt": "min-32-karakter-gizli-salt",
      "LoginSmsTemplate": "{code} giriş doğrulama kodunuzdur."
    }
  }
}
```

## Kullanım — stateless üretim ve doğrulama

OTP **plaintext olarak tutulmaz**. Üretimde sadece hash saklanır; doğrulamada girilen kodun hash'i saklanan hash ile karşılaştırılır.

```csharp theme={null}
public class OtpLoginService(IOtpService otp, IOtpHashService hasher, IDistributedCache cache)
{
    // 1) Üret → SMS gönder → hash'i sakla (kodun kendisini DEĞİL)
    public async Task RequestAsync(string phone)
    {
        OtpCode code = otp.GenerateCode();
        string hash  = otp.Hash(code);                 // HMAC-SHA256 + salt
        string sms   = otp.CreateLoginSms(code);       // "482913 giriş doğrulama kodunuzdur."

        await cache.SetStringAsync($"otp:{phone}", hash,
            new() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(3) });

        await _smsService.SendAsync(phone, sms);       // OtpSmsService → NetGSM
    }

    // 2) Doğrula — girilen kodu hash'le, saklanan hash ile sabit-zamanlı karşılaştır
    public async Task<bool> VerifyAsync(string phone, string entered)
    {
        string? savedHash = await cache.GetStringAsync($"otp:{phone}");
        if (savedHash is null) return false;           // süresi dolmuş / hiç istenmemiş

        OtpCode code = OtpCode.Create(entered);
        return hasher.Verify(code, savedHash);         // HMAC eşitliği (FixedTimeEquals)
    }
}
```

<Tip>
  Domain tarafında bu mantık `User.AddOtpChallenge` / `User.VerifyOtp` ile aggregate içine alınır; challenge state'i `UserOtpChallenge` child entity'sinde tutulur ve `UserOtpGeneratedDomainEvent` ile SMS/Email handler'larına yayılır.
</Tip>

## İlgili

<CardGroup cols={2}>
  <Card title="SMS servisi" icon="comment-sms" href="/services/sms">
    NetGSM üzerinden OTP SMS gönderimi.
  </Card>

  <Card title="OTP akışı" icon="shield-halved" href="/playbooks/otp-flow">
    Uçtan uca giriş + doğrulama senaryosu.
  </Card>

  <Card title="Authenticator (TOTP)" icon="mobile-screen" href="/services/authenticator">
    RFC6238 tabanlı authenticator alternatifi.
  </Card>

  <Card title="Domain" icon="cube" href="/domain/aggregates">
    UserOtpChallenge ve VerifyOtp metotları.
  </Card>
</CardGroup>
