Cihaz Kodu Akışı
Klavyesi olmayan cihazlarda giriş.
Cihaz kodu akışı, şifre yazmanın zahmetli olduğu cihazlarda kullanıcıların giriş yapmasını sağlar: akıllı TV'ler, set üstü kutular, kiosk'lar, oyun konsolları, komut satırı araçları. OAuth 2.0 device authorization grant modelini izler (RFC 8628).
- Cihaz ErtisAuth'tan bir kod ister. İki değer alır: ekranda gösterilecek kısa bir kullanıcı kodu (user code) ve kendinde tutacağı uzun bir cihaz kodu (device code).
- Kullanıcı, telefonunda ya da bilgisayarında zaten giriş yapmış olduğu web sitenizi veya uygulamanızı açar ve kullanıcı kodunu girer.
- Siteniz hangi cihazın istekte bulunduğunu gösterir ve kullanıcının onu onaylamasına ya da reddetmesine izin verir.
- Bu arada cihaz koduyla sürekli sorgulama yapan cihaz, o kullanıcı için bir token çifti alır.
Cihaz ErtisAuth Siteniz (kullanıcı girişli)
│ POST /codes │ │
│─────────────────────────────────────────▶ │ │
│ { user_code: "K7Q2XD9M", │ │
│ device_code: "…", interval: 5 } │ │
│◀───────────────────────────────────────── │ │
│ ekranda K7Q2-XD9M │ kullanıcı K7Q2XD9M girer │
│ │ GET /codes/K7Q2XD9M (cihaz bilgisi) │
│ │ ◀──────────────────────────────────────── │
│ │ POST /codes/K7Q2XD9M/approve │
│ │ ◀──────────────────────────────────────── │
│ POST /codes/token { device_code } │ │
│ (her `interval` saniyede) │ │
│─────────────────────────────────────────▶ │ │
│ 201 { access_token, … } │ │
│◀───────────────────────────────────────── │ │Neden iki kod? Kullanıcı kodunu ekranı görebilen herkes görür; bu yüzden yalnızca isteği tanımlar. Token yalnızca cihazdan hiç çıkmayan cihaz koduyla alınabilir.
Kod politikaları#
Kullanıcı kodlarının biçimini bir kod politikası (code policy) belirler; her membership kullandığı politikayı code_policy alanında belirtir.
{
"_id": "66f1c0d2a4b5c6d7e8f90160",
"name": "TV Codes",
"slug": "tv-codes",
"description": "8 characters, easy to read on a TV",
"length": 8,
"contains_letters": true,
"contains_digits": true,
"expires_in": 300,
"membership_id": "66f1c0d2a4b5c6d7e8f90123"
}| Alan | Zorunlu | Açıklama |
|---|---|---|
name | evet | Görünen ad. |
slug | hayır | Verilmezse addan türetilir. Membership'in code_policy alanı bunu gösterir. |
length | evet | Her kullanıcı kodunun karakter sayısı; her karakter kümesi için 5 ile 12 arası. |
contains_letters, contains_digits | en az biri | Karakter kümesi: harfler, rakamlar ya da ikisi birden. İkisini birden içeren kodlarda kolayca karıştırılan karakterler (0, O, 1, I) kullanılmaz. |
expires_in | evet | Bir kodun onaylanıp kullanılabileceği süre, saniye cinsinden; 1 ile 1800 (30 dakika) arası. |
Bu sınırların dışındaki bir politika, oluşturulurken ya da güncellenirken 400 ModelValidationError ile reddedilir.
Uzunluğu ekranı düşünerek seçin: bir TV'de 5 ile 8 karakter rahatça okunur ve yazılır. Kullanıcı kodu tek başına token veremediği için kısa bir kod güvenlidir; daha uzun bir kod çoğunlukla, onay bekleyen başka birinin kodunu tahmin etmeyi zorlaştırır.
Endpoint'ler#
Tüm route'lar /memberships/{membershipId} altındadır.
| Metot | Route | Yetki |
|---|---|---|
GET | /code-policies/{id} | code-policies.read.{id} |
GET | /code-policies | code-policies.read |
POST | /code-policies/_query | code-policies.read |
POST | /code-policies | code-policies.create |
PUT | /code-policies/{id} | code-policies.update.{id} |
DELETE | /code-policies/{id} | code-policies.delete.{id} |
DELETE | /code-policies | code-policies.delete |
Membership'in kullandığı bir politika silinemez (409 TokenCodePolicyInUse). Tekrarlanan bir slug 409 TokenCodePolicyAlreadyExists, değişiklik içermeyen bir güncelleme 409 IdenticalDocumentError döner.
Akışı açma#
- Bir politika oluşturun:
curl -X POST https://auth.example.com/memberships/<membership_id>/code-policies \ -H 'Authorization: Bearer <access_token>' \ -H 'Content-Type: application/json' \ -d '{ "name": "TV Codes", "length": 8, "contains_letters": true, "contains_digits": true, "expires_in": 300 }' - Membership'te
"code_policy": "tv-codes"değerini ayarlayın.
Token kodları#
Tüm route'lar /memberships/{membershipId} altındadır.
| Metot | Route | Açıklama | Kim çağırır | Yetki |
|---|---|---|---|---|
POST | /codes | Kod üretme | cihaz (ya da backend'i) | tokens.create |
GET | /codes/{user_code} | Bir kodun cihazını gösterme | onay sayfanız | tokens.create, Bearer token |
POST | /codes/{user_code}/approve | Kodu onaylama | onay sayfanız | tokens.create, Bearer token |
POST | /codes/{user_code}/deny | Kodu reddetme | onay sayfanız | tokens.create, Bearer token |
POST | /codes/token | Cihaz koduyla token alma | cihaz | yok |
1. Bir kod üretin#
POST /memberships/{membershipId}/codes
Authorization: Basic <application_id>:<secret>
X-IpAddress: 203.0.113.42
X-UserAgent: LivingRoomTV/2.4 (Tizen 7.0)Cihaz bunu backend'iniz üzerinden ya da rolünde yalnızca tokens.create yetkisi olan, cihazlara ayrılmış bir uygulamanın kimlik bilgileriyle çağırır. Bir cihaza gömülen kimlik bilgileri çıkarılabilir; bu yüzden onlara başka hiçbir yetki vermeyin.
X-IpAddress ve X-UserAgent cihazı tanımlar; eksiklerse isteğin adresi ve user agent'ı kullanılır. Onaydan önce kullanıcıya gösterilir ve oturumla birlikte saklanır.
Yanıt 201 Created
{
"_id": "66f1c0d2a4b5c6d7e8f90170",
"user_code": "K7Q2XD9M",
"device_code": "Qm9xZ1pWc2F0aE5vV2xvUjNjZ2dXbFFmZ3NtWm9Kdw",
"status": "pending",
"expires_in": 300,
"interval": 5,
"created_at": "2026-01-01T12:00:00Z",
"expire_time": "2026-01-01T12:05:00Z",
"client_info": { "ip_address": "203.0.113.42", "user_agent": "LivingRoomTV/2.4 (Tizen 7.0)" },
"membership_id": "66f1c0d2a4b5c6d7e8f90123"
}| Alan | Kullanımı |
|---|---|
user_code | Onay sayfanızın adresiyle birlikte ekranda gösterin. Okunabilirlik için bölebilirsiniz (K7Q2-XD9M): kullanıcı girerken büyük/küçük harf, tire ve boşluklar yok sayılır. |
device_code | Cihazın belleğinde tutun ve token almak için kullanın. Yalnızca bu yanıtta döner; ErtisAuth yalnızca hash'ini saklar. Asla göstermeyin ya da loglamayın. |
interval | İki token isteği arasında beklenecek saniye. |
expire_time | Bu zamandan sonra kod kullanılamaz; yenisini isteyin. |
| Hata | Ne zaman |
|---|---|
404 TokenCodePolicyNotFound | Membership'in code_policy değeri yok ya da politika mevcut değil |
503 TokenCodeCouldNotBeGenerated | Kullanılmayan bir kullanıcı kodu bulunamadı; tekrar deneyin (politikanın çok az olası kodu yoksa pek olası değil) |
2. Cihazı kullanıcıya gösterin#
Onay sayfanızda giriş yapmış kullanıcı kodu girer. Onaylamadan önce ona neye giriş yapmak üzere olduğunu gösterin:
GET /memberships/{membershipId}/codes/K7Q2XD9M
Authorization: Bearer <user_access_token>Yanıt 200 OK: cihaz kodu olmadan kod; status (pending, approved ya da denied), created_at ve client_info ile birlikte:
203.0.113.42 adresinden, 1 dakika önce istenen LivingRoomTV/2.4 (Tizen 7.0) girişini onaylıyor musunuz?
| Hata | Ne zaman |
|---|---|
404 TokenCodeNotFound | Bilinmeyen ya da süresi dolmuş kod |
400 TokenTypeNotSupported | Token bir Bearer token değil |
Bu adım kullanıcıları kod oltalamasına (code phishing) karşı korur: bir saldırgan kendi cihazında bir kod üretir ve kurbanı bu kodu girmeye kandırır ("ödülünüzü almak için bu kodu girin"). Kullanıcı tanımadığı bir cihaz görürse isteği reddeder.
3. Onaylayın ya da reddedin#
POST /memberships/{membershipId}/codes/K7Q2XD9M/approve
Authorization: Bearer <user_access_token>POST /memberships/{membershipId}/codes/K7Q2XD9M/deny
Authorization: Bearer <user_access_token>Token bir kullanıcının Bearer token'ı olmalıdır: cihaz bu kullanıcı olarak giriş yapacaktır. Bir uygulamanın Basic token'ı 400 TokenTypeNotSupported ile reddedilir. Kullanıcının rolünde tokens.create yetkisi olmalıdır.
Yanıt 200 OK: artık approved ya da denied durumundaki kod ve user_id alanında kullanıcının id'si. Bir TokenCodeApproved ya da TokenCodeDenied olayı kaydedilir.
Bir kod yalnızca bir kez ve yalnızca beklemedeyken onaylanabilir ya da reddedilebilir: iki kişi aynı anda denerse yalnızca biri başarılı olur.
Scoped token ile onaylama. Onay bir scoped token ile yapıldığında (scope'ları tokens.create yetkisini kapsamalıdır) cihaz aynı scope'larla sınırlı bir token alır: bir cihaz, onu onaylayan oturumdan asla fazlasını alamaz. Böyle bir token membership'in scoped_token_expires_in süresi boyunca (varsayılan olarak 12 saat) geçerlidir ve yenilendiğinde scope'larını korur. Sıradan bir access token ile yapılan onay cihaza sıradan bir token verir.
| Hata | Ne zaman |
|---|---|
400 TokenTypeNotSupported | Token bir Bearer token değil |
401 TokenCodeExpired | Kodun süresi dolmuş |
404 TokenCodeNotFound | Bilinmeyen kod |
409 TokenCodeAlreadyAuthorized | Kod zaten onaylanmış ya da reddedilmiş |
4. Token'ı alın#
Cihaz bir token alana ya da kodun süresi dolana kadar her interval saniyede sorgulama yapar:
POST /memberships/{membershipId}/codes/token
Content-Type: application/json
{ "device_code": "Qm9xZ1pWc2F0aE5vV2xvUjNjZ2dXbFFmZ3NtWm9Kdw" }Token gerekmez. Cihaz kodu, erişim log'larına düşmesin diye URL'de değil, gövdede gönderilir.
| Yanıt | Anlamı | Cihazın yapacağı |
|---|---|---|
Token çiftiyle 201 Created | Onaylandı | Token'ları saklar ve sorgulamayı bırakır |
401 UnauthorizedTokenCode | Henüz onaylanmadı | interval saniye bekler ve yeniden sorgular |
400 TokenCodeSlowDown | interval saniye geçmeden sorgulandı | Bir sonraki sorgudan önce daha uzun bekler |
401 TokenCodeDenied | Kullanıcı isteği reddetti | Durur; bir mesaj gösterir, yeni bir kod önerir |
401 TokenCodeExpired | Kodun süresi doldu | Yeni bir kod ister |
401 UserInactive | Onaylayan kullanıcı bu arada devre dışı bırakıldı | Yeni bir kod ister |
401 InvalidToken | Bilinmeyen ya da zaten kullanılmış cihaz kodu | Yeni bir kod ister |
- Token çifti cihaz onu aldığında üretilir; bu yüzden geçerlilik süresi o anda başlar ve bir kez verilir: kod o anda silinir.
- Oturum cihazın IP adresini ve user agent'ını kaydeder; böylece kullanıcının oturumları arasında kolayca tanınır.
- Bundan sonra cihaz token'ını diğer istemciler gibi yeniler.
Örnek cihaz döngüsü#
const code = await post(`/memberships/${membershipId}/codes`); // through your backend
showOnScreen(code.user_code.replace(/(.{4})/, "$1-"), "https://example.com/tv");
let interval = code.interval;
while (Date.now() < Date.parse(code.expire_time)) {
await sleep(interval * 1000);
const response = await post(`/memberships/${membershipId}/codes/token`, { device_code: code.device_code });
if (response.status === 201) return signIn(response.body);
const { errorCode } = response.body;
if (errorCode === "TokenCodeSlowDown") interval += 5;
else if (errorCode !== "UnauthorizedTokenCode") break; // denied, expired, …
}
offerANewCode();Güvenlik notları#
- Kullanıcı kodu bir sır değildir; cihaz kodu ise sırdır. Cihaz kodunu yalnızca bellekte tutun ve HTTPS kullanın.
- Onaydan önce her zaman cihaz bilgisini gösterin ve kullanıcıların tanımadıkları cihazları reddetmesine izin verin.
- Cihazların kullandığı kimlik bilgilerine yalnızca
tokens.createyetkisi verin. POST /codesvePOST /codes/tokenendpoint'lerine gateway'inizde rate limiting uygulayın.
Dokümantasyonda bir hata mı buldunuz? Issue açın