Teknik Spesifikasyon: OAuth2 Sunucusuna API Anahtarı Özelliği Eklenmesi
-----------------------------------------------------------------------
### 1\. Giriş
Bu doküman, mevcut OAuth2 yetkilendirme sunucusuna API anahtarı özelliği eklemek için gerekli teknik detayları ve adımları tanımlar. Bu özellik, belirli API isteklerinin kimliğini doğrulamak ve yetkilendirmek için API anahtarlarını kullanmayı sağlar.
### 2\. Gereksinimler
- **OAuth2 Sunucusu:** Mevcut bir OAuth2 sunucusu.
- **Veritabanı:** PostgreSQL.
- **Güvenlik:** API anahtarlarının güvenli bir şekilde oluşturulması, depolanması ve doğrulanması.
- **Gizlilik:** API anahtarlarının yalnızca bir kez gösterilmesi ve bir daha geri döndürülemeyecek şekilde saklanması.
### 3\. Mimarî
#### 3.1. Veri Modeli
API anahtarlarının yönetimi ve rol/yetki tanımları için yeni tablolar eklenmelidir:
**Table: `api_keys`**
- `id` (UUID): Benzersiz kimlik.
- `user_id` (UUID): API anahtarının sahibi olan kullanıcının kimliği.
- `api_key_hash` (VARCHAR): API anahtarının Argon2 hash'i.
- `description` (VARCHAR): API anahtarının ad alanı veya kullanım amacı.
- `created_at` (TIMESTAMP): API anahtarının oluşturulma zamanı.
- `expires_at` (TIMESTAMP, NULLABLE): API anahtarının geçerlilik süresi (opsiyonel).
- `revoked` (BOOLEAN): API anahtarının iptal edilip edilmediği.
**Table: `api_key_roles`**
- `id` (UUID): Benzersiz kimlik.
- `api_key_id` (UUID): İlgili API anahtarının kimliği.
- `role` (VARCHAR): API anahtarının rolü (örneğin, `admin`, `user`).
**Table: `roles_permissions`**
- `id` (UUID): Benzersiz kimlik.
- `role` (VARCHAR): Rolün adı.
- `permission` (VARCHAR): Rolün yetkisi (örneğin, `read`, `write`, `delete`).
#### 3.2. API Endpoints
1. **API Anahtarı Oluşturma**
- **Endpoint:** `POST /api-keys`
- **İstek:**
```json
{
"user_id": "UUID",
"roles": ["role1", "role2"],
"expires_in": 3600,
"description": "API anahtarının kullanım amacı veya adı"
}
```
- **Yanıt:**
```json
{
"api_key": "string",
"expires_at": "timestamp"
}
```
2. **API Anahtarlarını Listeleme**
- **Endpoint:** `GET /api-keys`
- **İstek:** Yok
- **Yanıt:**
```json
{
"api_keys": [
{
"id": "UUID",
"user_id": "UUID",
"description": "API anahtarının kullanım amacı veya adı",
"created_at": "timestamp",
"expires_at": "timestamp",
"revoked": false
}
]
}
```
3. **API Anahtarını İptal Etme**
- **Endpoint:** `DELETE /api-keys/{id}`
- **İstek:**
```json
{
"id": "UUID"
}
```
4. **API Anahtarını İptal Etme (Revocation)**
** **Endpoint:** `POST /revoke-api-key`
** **İstek:**
```json
{
"api_key": "string"
}
```
5. **Rol ve Yetkileri Yönetme**
* **Rol Oluşturma**
** **Endpoint:** `POST /roles`
** **İstek:**
```json
{
"role": "role_name",
"permissions": ["perm1", "perm2"]
}
```
** **Yanıt:**
```json
{
"role": {
"id": "UUID",
"role": "role_name",
"permissions": ["perm1", "perm2"]
}
}`
```
* **Rol Listeleme**
** **Endpoint:** `GET /roles`
** **İstek:** Yok
** **Yanıt:**
```json
{
"roles": [
{
"id": "UUID",
"role": "role_name",
"permissions": ["perm1", "perm2"]
}
]
}
```
6. **API Anahtarı İntrospection**
* **Endpoint:** `POST /introspect-api-key`
* **İstek:**
```json
{
"api_key": "string"
}
```
* **Yanıt:**
```json
{
"active": true,
"user_id": "UUID",
"roles": ["role1", "role2"],
"description": "API anahtarının kullanım amacı veya adı",
"expires_at": "timestamp",
"created_at": "timestamp"
}
```
7. **API Anahtarı Yenileme**
* **Endpoint:** `POST /renew-api-key`
* **İstek:**
```json
{
"old_api_key": "string",
"expires_in": 3600
}
```
* **Yanıt:**
```json
{
"new_api_key": "string",
"expires_at": "timestamp"
}
```
#### 3.3. Kimlik Doğrulama ve Yetkilendirme
API anahtarı doğrulaması ve yetkilendirmesi, mevcut OAuth2 yetkilendirme sürecine entegre edilmelidir. API istekleri şu adımlarla doğrulanacaktır:
1. İstek başlığında `Authorization: ApiKey <api_key>` kontrolü yapılır.
2. `api_keys` tablosunda ilgili API anahtarının hash'i bulunur.
3. API anahtarının iptal edilip edilmediği, süresinin dolup dolmadığı kontrol edilir.
4. `api_key_roles` tablosundan API anahtarının rolleri alınır.
5. `roles_permissions` tablosundan rollerin yetkileri alınır.
6. API isteğinin yetkili olup olmadığı kontrol edilir.
7. Geçerli ise, kullanıcı yetkilendirilir; aksi takdirde, yetkilendirme reddedilir.
#### 3.4. API Anahtarı Oluşturma ve Saklama
API anahtarları yalnızca bir kez gösterilmeli ve ardından hashlenerek saklanmalıdır. Bu hash işlemi için Argon2 algoritması kullanılacaktır.
### 4\. Güvenlik
- API anahtarları yeterince uzun ve rastgele oluşturulmalıdır.
- Anahtarlar, yalnızca bir kullanıcıya özgü olmalıdır.
- API anahtarları, veritabanında plaintext olarak saklanmamalıdır; hash'lenmiş olarak saklanmalıdır.
- Rol ve yetkiler, belirli işlevlerin ve verilerin korunmasını sağlamak için dikkatlice tanımlanmalıdır.
### 5\. Uygulama Adımları
1. **Veritabanı Değişiklikleri:**
- `api_keys`, `api_key_roles`, ve `roles_permissions` tablolarını oluşturmak için gerekli SQL scriptleri yazılır ve uygulanır.
2. **API Endpoint'lerinin Geliştirilmesi:**
- Yeni endpoint'ler için gerekli kontrolcüler ve hizmetler geliştirilir.
3. **Kimlik Doğrulama ve Yetkilendirme Mekanizmasının Güncellenmesi:**
- Mevcut OAuth2 sunucusuna API anahtarı doğrulama ve yetkilendirme yetenekleri eklenir.
4. **Güvenlik Testleri ve Kod İncelemeleri:**
- Uygulanan değişikliklerin güvenlik testleri yapılır ve kod incelemeleri tamamlanır.
### 6\. .NET Core İle API Anahtarı Oluşturma ve Hashleme
#### 6.1. Gerekli Paketlerin Yüklenmesi
Argon2 hash fonksiyonları için gerekli paketi yükleyin:
```bash
dotnet add package Konscious.Security.Cryptography.Argon2
```
#### 6.2. API Anahtarı Oluşturma ve Hashleme
```csharp
using System;
using System.Security.Cryptography;
using System.Text;
using Konscious.Security.Cryptography;
public class ApiKeyService
{
public string GenerateApiKey()
{
using (var rng = new RNGCryptoServiceProvider())
{
var apiKeyBytes = new byte[32]; // 256 bit
rng.GetBytes(apiKeyBytes);
return Convert.ToBase64String(apiKeyBytes);
}
}
public string HashApiKey(string apiKey)
{
var apiKeyBytes = Encoding.UTF8.GetBytes(apiKey);
var salt = GenerateSalt(); // Generate a random salt
var argon2 = new Argon2id(apiKeyBytes)
{
Salt = salt,
DegreeOfParallelism = 8, // Number of threads to use
MemorySize = 65536, // 64 MB
Iterations = 4 // Number of iterations
};
var hashBytes = argon2.GetBytes(32); // Generate a 256-bit hash
var hash = Convert.ToBase64String(hashBytes);
return $"{Convert.ToBase64String(salt)}:{hash}";
}
private byte[] GenerateSalt()
{
using (var rng = new RNGCryptoServiceProvider())
{
var salt = new byte[16]; // 128-bit salt
rng.GetBytes(salt);
return salt;
}
}
public bool VerifyApiKey(string apiKey, string storedHash)
{
var parts = storedHash.Split(':');
var salt = Convert.FromBase64String(parts[0]);
var storedKey = parts[1];
var apiKeyBytes = Encoding.UTF8.GetBytes(apiKey);
var argon2 = new Argon2id(apiKeyBytes)
{
Salt = salt,
DegreeOfParallelism = 8,
MemorySize = 65536,
Iterations = 4
};
var hashBytes = argon2.GetBytes(32);
var hash = Convert.ToBase64String(hashBytes);
return hash == storedKey;
}
}
```