Hoş geldin,
geliştirici.
Bu site, Avar Teknolojileri ürünlerinin — Marmarotay dahil — teknik ve kullanıcı belgelerini barındırır. Soldan bir kategori seç ya da arama kutusunu kullan.
Avar nedir?
Avar Teknolojileri, seyahat ve gündelik hayatı kolaylaştırmaya odaklanmış bağımsız bir yazılım girişimidir. Şu an aktif iki ürün yayındadır: Marmarotay (toplu taşıma asistanı) ve Marmarotay Sohbet (beta).
Bu dokümantasyon
Üç ana bölümden oluşur:
- Genel — Avar ve Marmarotay hakkında kullanıcı rehberleri
- API — Geliştirici entegrasyonu için endpoint ve kimlik doğrulama belgeleri
- Gizlilik & Yasal — KVKK, GDPR ve kullanım koşulları
Hızlı bağlantılar
Hızlı Başlangıç
Marmarotay'ı kurup ilk durak bildirimini almak yaklaşık 5 dakika sürer.
1. Uygulamayı indir
Marmarotay şu an yalnızca Android'de kullanılabilir. Google Play'den indir:
2. İlk kurulum
-
1Uygulamayı aç ve izinleri ver
Konum izni (arka planda) ve bildirim izni sorulacak. İkisi de durak bildiriminin çalışması için gereklidir.
-
2Şehri ve hattı seç
Ana ekranda şehrini ve binmek istediğin hattı seç. İstanbul için metro, tramvay ve otobüs hatları mevcuttur.
-
3Hedef durağı belirle
İneceğin durağı seç. Uygulama seni kaç durak önce uyaracağını da ayarlayabilirsin (varsayılan: 1 durak).
-
4Seyahati başlat
Araca bin ve "Seyahati Başlat"a dokun. Telefonu cebine koyabilirsin — Marmarotay arka planda çalışır.
3. İlk bildirimi al
Hedef durağa yaklaştığında titreşim + ses bildirimi alırsın. Seyahat bittiğinde veya durağa indiğinde takip otomatik durur.
Bileşen Referansı
Aşağıdaki bileşenleri kopyalayıp kendi sayfalarına yapıştırabilirsin. Her biri CSS'te zaten tanımlıdır, ekstra bir şey gerekmez.
Callout kutuları
Beş türde callout mevcuttur: info, warning, danger, success, tip.
class="callout info"class="callout warning"class="callout danger"class="callout success"class="callout tip"<div class="callout info">
<span class="callout-icon">ℹ️</span>
<div class="callout-body">
<strong>Başlık</strong>
Açıklama metni buraya.
</div>
</div>
Badge / etiket
Mavi Aktif Beta Tehlike Yakında Premium
<span class="badge blue">Mavi</span> <span class="badge green">Aktif</span> <span class="badge orange">Beta</span> <span class="badge red">Tehlike</span> <span class="badge gray">Yakında</span> <span class="badge purple">Premium</span>
Adım listesi
- 1İlk adım başlığı
Adım açıklaması buraya gelir.
- 2İkinci adım
Gerektiği kadar adım ekleyebilirsin.
- 3Son adım
Son adımın altında çizgi görünmez.
<ul class="steps">
<li class="step">
<div class="step-num">1</div>
<div class="step-body">
<strong>Başlık</strong>
<p>Açıklama.</p>
</div>
</li>
</ul>
Kod bloğu
// Örnek API isteği
const res = await fetch('https://api.avartech.net/v1/stops', {
headers: { 'Authorization': 'Bearer YOUR_API_KEY' }
});
const data = await res.json();
<div class="code-block">
<div class="code-block-header">
<span class="code-lang">JavaScript</span>
<button class="code-copy" onclick="copyCode(this)">Kopyala</button>
</div>
<pre>// kod buraya</pre>
</div>
Özellik tablosu
| Parametre | Tür | Açıklama |
|---|---|---|
| stop_id | string | Durak benzersiz kimliği |
| lat | float | Durak enlemi |
| lng | float | Durak boylamı |
| line | string | Hat kodu (ör. M1) |
<table class="prop-table">
<thead><tr>
<th>Parametre</th><th>Tür</th><th>Açıklama</th>
</tr></thead>
<tbody>
<tr>
<td>stop_id</td>
<td><span class="badge gray">string</span></td>
<td>Durak benzersiz kimliği</td>
</tr>
</tbody>
</table>
Sekmeler
fetch('https://api.avartech.net/v1/stops')import requests
requests.get('https://api.avartech.net/v1/stops')curl https://api.avartech.net/v1/stops
<div class="tabs" id="MY_TABS">
<div class="tab-nav">
<button class="tab-btn active"
onclick="switchTab('MY_TABS','panel-1',this)">Sekme 1</button>
<button class="tab-btn"
onclick="switchTab('MY_TABS','panel-2',this)">Sekme 2</button>
</div>
<div class="tab-panel active" id="panel-1">İçerik 1</div>
<div class="tab-panel" id="panel-2">İçerik 2</div>
</div>
Genişleyen kutu
<div class="expand">
<button class="expand-trigger" onclick="toggleExpand(this)">
Başlık <span class="expand-chevron">›</span>
</button>
<div class="expand-body">İçerik burada.</div>
</div>
Kart grid
<div class="card-grid">
<div class="doc-card" onclick="showPage('hedef-sayfa')">
<div class="doc-card-icon">📦</div>
<div class="doc-card-title">Başlık</div>
<div class="doc-card-desc">Açıklama.</div>
</div>
</div>
Yeni sayfa eklemek
Yeni bir sayfa eklemek için iki şey yapman gerekiyor:
-
1Sidebar'a link ekle
İlgili
sidebar-sectioniçine birsidebar-itemekle.data-pageveonclickdeğerleri aynı ID'yi kullanmalı. -
2Sayfayı oluştur
<div class="doc-page" id="page-SAYFA_ADI" data-toc='...'>ile yeni sayfanı ekle.data-tocsağ taraf başlık navigasyonunu otomatik doldurur.
<!-- sidebar içine -->
<a class="sidebar-item" onclick="showPage('benim-sayfam')" data-page="benim-sayfam">
<span class="si-icon">📄</span> Sayfa Adı
</a>
<div class="doc-page" id="page-benim-sayfam" data-toc='[
{"id":"bolum-1","text":"Bölüm 1","level":2},
{"id":"bolum-2","text":"Bölüm 2","level":2}
]'>
<div class="doc-meta">
<span class="doc-category">KATEGORİ</span>
<span class="doc-sep">·</span>
<span class="doc-updated">Son güncelleme: GÜN AY YIL</span>
</div>
<h1>Sayfa <em>Başlığı</em></h1>
<p class="doc-lead">Kısa açıklama.</p>
<h2 id="bolum-1">Bölüm 1</h2>
<p>İçerik...</p>
</div>
data-toc JSON'ını doğru doldurursan sağdaki başlık navigasyonu otomatik oluşur. level:2 için h2, level:3 için h3 girintilemesi yapılır.
Kimlik Doğrulama
Avar API'si Bearer token tabanlı kimlik doğrulama kullanır. Tüm isteklerde geçerli bir API anahtarı gönderilmesi zorunludur.
API anahtarı alma
Geliştirici portalında hesap oluşturduktan sonra Ayarlar → API Anahtarları bölümünden anahtarını oluşturabilirsin. Anahtarı güvenli bir yerde sakla — bir kez gösterilir.
İstek başlığı
Tüm isteklerde Authorization header'ı gönderilmelidir:
const response = await fetch('https://api.avartech.net/v1/stops', {
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
}
});import requests
headers = {"Authorization": "Bearer YOUR_API_KEY"}
r = requests.get("https://api.avartech.net/v1/stops", headers=headers)curl -H "Authorization: Bearer YOUR_API_KEY" \ https://api.avartech.net/v1/stops
Rate limiting
| Plan | İstek / dakika | İstek / gün |
|---|---|---|
| Ücretsiz | 60 | 1.000 |
| İkili / Aile | 300 | 10.000 |
Avar ID Hakkında
Bu sayfada yer alan bilgileri kullanarak Avar ID hakkında daha fazla bilgi edinebilir ve projelerinde Avar ID teknolojisini kullanabilirsin.
Avar ID, Avar Teknolojileri tarafından geliştirilen ve servisini kullanan üyelerin oturum işlemlerini kolaylaştırmak için hazırlanmış, Türk yapımı bir kullanıcı kimlik doğrulama çözümüdür.
Avar ID Nedir
Avar ID, birden fazla Avar Teknolojileri ürününde tek bir hesapla oturum açmana olanak tanıyan merkezi bir kimlik doğrulama altyapısıdır. Kullanıcılar her servis için ayrı bir hesap oluşturmak yerine yalnızca bir Avar ID hesabıyla tüm entegre ürünlere erişebilir.
Sistem; OAuth 2.0 ve OpenID Connect gibi endüstri standardı protokollerle inşa edildiğinden, mevcut altyapına kolayca entegre edilebilir ve güvenli bir oturum yönetimi katmanı sunar.
Nasıl Çalışır
Avar ID, kullanıcıyı doğrudan uygulamanın içinde değil merkezi bir kimlik sunucusu üzerinden doğrular. Bu sayede kullanıcı şifresi hiçbir zaman üçüncü taraf uygulamayla paylaşılmaz; yalnızca güvenli bir oturum jetonu iletilir.
-
1
Yönlendirme
Kullanıcı, uygulamandaki "Avar ID ile Giriş Yap" düğmesine tıklar ve Avar ID oturum açma sayfasına yönlendirilir.
-
2
Kimlik Doğrulama
Kullanıcı, kimlik bilgilerini Avar ID altyapısı üzerinde girer. Eğer kullanıcı isterse, Google Firebase üzerinden sağladığımız Google ile giriş yapma gibi seçenekleri de kullanabilir. Kullanıcı isterse Avar'a ait bir uygulamadan sekiz hanelik kod girerek servisine giriş yapabilir.
-
3
Yetkilendirme
Başarılı girişin ardından kullanıcı, bir yetkilendirme kodu ile uygulamana geri yönlendirilir.
-
4
Jeton (Token) Değişimi
Kullanıcı işlemi bitirdiğinde, geriye erişim tokeni iletilir. Bu tokeni kendi sistemin içine entegre ederek (örneğin MongoDB veya PostgreSQL üzerinden) kullanıcı oturumunu yönetebilirsin. Kullanıcının e-postası ve şifresi Avar'ın yönetiminde olduğundan bu işlemler için Avar Auth'a tekrar bir POST isteğinde bulunmalısın.
Neden Avar ID
Pek çok kimlik doğrulama çözümü yurt dışı altyapısına bağımlıdır ve Türkiye'deki veri yerleşikliği gereksinimlerini karşılamakta güçlük çekebilir. Avar ID, bu boşluğu doldurmak amacıyla yerli bir alternatif olarak tasarlanmıştır.
Hızlı Entegrasyon (5 dk)
Aşağıdaki tek dosyalık yardımcıyı projenize ekleyerek Avar ID girişini hemen kullanabilirsiniz.
/api/avar-id/auth/login, /api/avar-id/auth/refresh, /api/avar-id/auth/logout, /api/avar-id/me.
const AVAR_BASE = 'https://api.avartech.net/api';
const AvarAuthHelper = {
get token() {
return localStorage.getItem('avar_token');
},
get refreshToken() {
return localStorage.getItem('avar_refresh_token');
},
saveAuth(data) {
localStorage.setItem('avar_token', data.token);
localStorage.setItem('avar_refresh_token', data.refresh_token || '');
localStorage.setItem('avar_user', JSON.stringify(data.user || null));
},
clearAuth() {
localStorage.removeItem('avar_token');
localStorage.removeItem('avar_refresh_token');
localStorage.removeItem('avar_user');
},
async login(email, password) {
const res = await fetch(`${AVAR_BASE}/avar-id/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
if (!res.ok) throw new Error('Login failed');
const data = await res.json();
this.saveAuth(data);
return data;
},
async refresh() {
const res = await fetch(`${AVAR_BASE}/avar-id/auth/refresh`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refresh_token: this.refreshToken }),
});
if (!res.ok) throw new Error('Refresh failed');
const data = await res.json();
this.saveAuth(data);
return data;
},
async logout() {
await fetch(`${AVAR_BASE}/avar-id/auth/logout`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refresh_token: this.refreshToken }),
});
this.clearAuth();
},
async getMe() {
const res = await fetch(`${AVAR_BASE}/avar-id/me`, {
headers: { Authorization: `Bearer ${this.token}` },
});
if (res.status === 401) {
await this.refresh();
return this.getMe();
}
if (!res.ok) throw new Error('getMe failed');
return res.json();
},
};
Minimum kullanım akışı:
- 1. Kullanıcı giriş formunda
login(email, password)çağır. - 2. Korumalı bir sayfada
getMe()ile oturum doğrula. - 3. Çıkışta
logout()çağır ve local state temizle.
Node Backend Quickstart
Sunucu tarafında token doğrulama yapmak için minimum Express middleware örneği:
import express from 'express';
const app = express();
const AVAR_BASE = 'https://api.avartech.net/api';
async function verifyWithAvarId(accessToken) {
const res = await fetch(`${AVAR_BASE}/avar-id/me`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (!res.ok) {
throw new Error('Invalid token');
}
return res.json();
}
async function requireAvarAuth(req, res, next) {
const header = req.headers.authorization || '';
if (!header.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Missing bearer token' });
}
const token = header.slice(7);
try {
const me = await verifyWithAvarId(token);
req.avarUser = me.user;
return next();
} catch {
return res.status(401).json({ error: 'Unauthorized' });
}
}
app.get('/protected', requireAvarAuth, (req, res) => {
return res.json({ ok: true, user: req.avarUser });
});
Refresh fallback pattern (backend-to-backend):
- 1.
/avar-id/meyanıtı401ise stored refresh token ile/avar-id/auth/refreshçağır. - 2. Yeni
tokenverefresh_tokendeğerlerini atomik olarak kaydet. - 3. Başarısız refresh durumunda kullanıcıyı yeniden login akışına yönlendir.
Sık Sorulan Sorular
OAuth 2.0 Başlangıç
Avar ID, endüstri standartı OAuth 2.0 ve PKCE protokollerini kullanarak güvenli, merkezi kimlik doğrulama hizmeti sunar. Ayrıca kullanım şekli sayesinde projelerine kolayca eklemen mümkün.
Avar ID OAuth Nedir
Avar ID OAuth, üçüncü taraf uygulamaların kullanıcı verilerinin (email, profil, chat geçmişi gibi) güvenli erişim sağlamasına olanak tanıyan bir yetkilendirme sistemidir. Kullanıcılar:
- Kullanıcı bilgilerinizi Avar üzerinden yöneterek sunucu yükünüzü azaltır
- Sizin yerinize gizlilik saydamlığı sağlar
- Uygulamanızdaki ayarları Avar üzerinden de yönetebilirler.
Temel Özellikler
| Özellik | Açıklama |
|---|---|
| PKCE Flow | SHA256 şifreli kodlar ile güvenli giriş yapma akışı sağlar |
| Rızalı Yönetimi | Kullanıcıların sistemine otomatik olarak bağlanabilir |
| App İkonu | Kendi markanı gösterebilirsin |
| Tek Aşamalı Kodlar | Kullanıcılar Avarapp veya Marmarotay üzerinden şifre girmek zorunda kalmadan giriş yapabilir. |
| Kayıtlar | Tüm yetkilendirme olayları otomatik olarak kayıt altınır |
| Webhook'lar | Giriş yapma sistemini Discord, Telegram gibi sosyal medyalarda enterge edebilirsin. |
İlk Adımlar
-
1Developer Portalında Kayıt Ol
chat.avartech.net adresine git ve hesabın yoksa hesap oluştur. Eğer hesabın varsa Ayarlar sekmesinden Developer seçeneğine gel ve Avar Developer Portal'ına giriş yap.
-
2Yeni Uygulama Oluştur
Açılan sekmede "Create App" butonuna tıkla. Projenin adını gir ve varsa ikonun lokasyonunu (mesela imgur linki) gir. Ortamını ihtiyacına göre ayarla ve Avar'ın arkakodundan hangi verileri isteyeceğini seç. Bunların beyanında bulunduktan sonra oluşturulan uygulamadan tek seferlik API anahtarını almayı unutma.
-
3Client ID ve Secret'i Kaydet
Oluşturulan uygulamada bulunan
client_idveclient_secret'ı (API) güvenli bir yerde sakla. Gerekirse bir kağıdın üzerine veya çok güvenli sanal bir ortama sakla. Google Drive gibi bulut hizmetlerini kullanmaktan kaçın. -
4PKCE Akışını Uygula
"PKCE Akışı" sayfasını okuyup koduna entegre et. Kod örnekleri mevcuttur.
Neden OAuth
PKCE Erişim Kodu Akışı
PKCE (Proof Key for Code Exchange), giriş kodlarının tamamıyla kesilmesini ve yeniden kullanılmasını önlemek için tasarlanmış güvenli bir uzantıdır. Tüm yeni integrasyonlar için önerilir.
PKCE Nedir
PKCE, RFC 7636 tarafından tanımlanan ve özellikle mobil uygulamalar için tasarlanan bir güvenlik mekanizmasıdır. Çalışma prensibini şu şekilde kavrayabilirsin:
- Uygulama code_verifier adında 128 karakterlik rastgele bir string üretir
- Bu stringin SHA256 hash'ini code_challenge olarak kodlar (base64url)
authorizationisteğine challenge'ı ekler- Daha sonra token isteminde orijinal
code_verifier'ı gönderir - Sunucu
code_verifier'ını hash'ler ve kaydedilen challenge'la karşılaştırır - Eğer bu kod bir şekilde çiftiyle eşleşirse (ve kötü niyetli biri kesilmiş code'u almışsa), onu kullanamaz.
code_challenge_method=plain metodunu asla kullanma. Metodların her zaman S256 (SHA256) kullandığından emin ol.
Akış Diyagramı
┌─────────────┐ ┌──────────────┐
│ Tarayıcı │ │ Avar ID │
│ (Senin App)│ │ Sunucusu │
└──────┬──────┘ └──────┬───────┘
│ │
│ 1. PKCE parametreleri üret │
│ - code_verifier (128 char) │
│ - code_challenge (SHA256) │
│ │
│ 2. /authorize'a yönlendir │
├─────────────────────────────────────────────→ │
│ GET /api/avar-id/oauth/authorize │
│ ?client_id=...&scope=...&state=... │
│ &code_challenge=...&code_challenge_method=S256 │
│ │
│ 3. Rıza ekranı göster │
│ ← ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ HTTP 302 + session_id │
│ │
│ 4. Kullanıcı rıza ver │
│ │
│ 5. Geri döndür │
│ ← ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ HTTP 302 ?code=...&state=... │
│ │
│ 6. Backend'e token isteğinde bulun │
├─────────────────────────────────────────────→ │
│ POST /api/avar-id/oauth/token │
│ grant_type=authorization_code │
│ code=... │
│ code_verifier=... (orijinal!) │
│ client_id=...&client_secret=... │
│ │
│ 7. Token'ı al │
│ ← ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ { "access_token": "...", ... } │
│ │
│ 8. User profile'ı al │
├─────────────────────────────────────────────→ │
│ GET /api/avar-id/user/profile │
│ Authorization: Bearer │
│ │
│ 9. Profili al │
│ ← ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ { "user_id": "...", "email": "..." } │
│ │
Adım Adım Uygulama
1. PKCE Parametreleri Üret
const crypto = require('crypto');
function generatePKCE() {
// 128 karakterlik random string
const verifier = crypto.randomBytes(96)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
// SHA256 hash
const challenge = crypto.createHash('sha256')
.update(verifier)
.digest('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
return { verifier, challenge };
}
const { verifier, challenge } = generatePKCE();
console.log('Verifier:', verifier);
console.log('Challenge:', challenge);import secrets
import hashlib
import base64
def generate_pkce():
verifier = base64.urlsafe_b64encode(
secrets.token_bytes(96)
).decode('utf-8').rstrip('=')
challenge = base64.urlsafe_b64encode(
hashlib.sha256(verifier.encode()).digest()
).decode('utf-8').rstrip('=')
return verifier, challenge
verifier, challenge = generate_pkce()
print(f'Verifier: {verifier}')
print(f'Challenge: {challenge}')2. Authorization Endpoint'ine Yönlendir
const state = crypto.randomBytes(32).toString('hex');
const { verifier, challenge } = generatePKCE();
// Session'da sakla (prod'da Redis/DB kullan)
session.pkceVerifier = verifier;
session.state = state;
const params = new URLSearchParams({
client_id: process.env.CLIENT_ID,
redirect_uri: process.env.REDIRECT_URI,
response_type: 'code',
scope: 'openid email profile',
state: state,
code_challenge: challenge,
code_challenge_method: 'S256'
});
const authorizeUrl = `https://api.avartech.net/api/avar-id/oauth/authorize?${params}`;
res.redirect(authorizeUrl);
3. Callback'i Ele Al
app.get('/oauth/callback', (req, res) => {
const { code, state, error } = req.query;
// Hata kontrolü
if (error) {
return res.status(400).send(
`Hata: ${error} - ${req.query.error_description}`
);
}
// State kontrolü (CSRF)
if (state !== session.state) {
return res.status(403).send('State mismatch!');
}
// Token exchange'e git
exchangeCodeForToken(code, session.pkceVerifier);
});
4. Token Exchange
async function exchangeCodeForToken(code, codeVerifier) {
try {
const response = await fetch(
'https://api.avartech.net/api/avar-id/oauth/token',
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
code_verifier: codeVerifier, // ← PKCE için zorunlu
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
redirect_uri: process.env.REDIRECT_URI
})
}
);
const tokenData = await response.json();
if (!response.ok) {
throw new Error(tokenData.error_description);
}
// Token'ları güvenli bir şekilde kaydet
session.accessToken = tokenData.access_token;
session.refreshToken = tokenData.refresh_token;
} catch (error) {
console.error('Token exchange failed:', error);
}
}
5. User Profile
const profile = await fetch(
'https://api.avartech.net/api/avar-id/user/profile',
{
headers: {
'Authorization': `Bearer ${session.accessToken}`
}
}
).then(r => r.json());
console.log(profile);
// {
// user_id: '507f1f77bcf86cd799439011',
// username: 'coffee_lover',
// email: 'user@example.com',
// display_name: 'Coffee Lover',
// avatar_url: '...',
// ...
// }
PKCE Argümanları
Legacy Password Flow
İsteyen fakat geleneksel uygulamalar için, direct username/password POST de kullanılabilir:
POST /api/avar-id/oauth/authorize HTTP/1.1
Host: api.avartech.net
Content-Type: application/json
{
"username": "user@example.com",
"password": "securepass",
"client_id": "app_....",
"scope": "openid email profile"
}
Marmarotay Genel Bakış
Bu sayfa yakında tamamlanacak.
Rızalı Yönetimi
Avar ID rıza sistemi, kullanıcıların hangi uygulamaların hangi verilerine erişebileceğini kontrol etmelerine olanak tanır. Kararlarını hatırlamalar ve otomatik onay özelliği de vardır.
Rıza Ekranı
Bir kullanıcı uygulamaya ilk giriş yapıyorsa, rıza ekranını görür:
- Uygulama adı ve ikonu
- İstenen vergiler (scopes'lar Türkçe açıklamalarıyla)
- "Kararımı bu uygulama için hatırla" checkbox'ı
- "Evet" ve "Hayır" düğmeleri
Kararımı Hatırla Özelliği
Kullanıcı "Kararımı bu uygulama için hatırla" seçeneğini işaretlerse:
- Avar ID, bu oturum için onayı kaydeder (
OAuthConsentGrantmodeli) - Sonraki isteklerde aynı uygulama ve aynı scopes için rıza ekranı gösterilmez
- Otomatik onay gerçekleşir ve kullanıcı doğrudan uygulamaya yönlendirilir
Scope Subset Kontrolü
Önemli: Otomatik onay sadece daha önceki scopes'ların alt kümesiyse çalışır.
| Durum | Sonuç |
|---|---|
Önce: openid email profile → Sonra: openid email | ✅ Auto-approve |
Öncе: openid email → Sonra: openid email profile | ❌ Yeni rıza gerekli |
Öncę: openid email profile → Sonra: openid email profile | ✅ Auto-approve |
Öncę: openid → Sonra: openid chat:write | ❌ Yeni rıza gerekli |
Auto-Approval Kontrol Endpoint'i
Rıza ekranından önce, ön-uçta auto-approval olasılığını kontrol edebilirsin:
GET /api/avar-id/oauth/session/sess_789abc/consent-check HTTP/1.1 Host: api.avartech.net Authorization: Bearer
Yanıt:
{
"can_auto_approve": true,
"previous_scopes": ["openid", "email", "profile"],
"message": "User hasiously consented to these scopes"
}
Eğer can_auto_approve: true ise, doğrudan onay işlemi başlatabilirsin:
async function checkAndAutoApprove(sessionId, userToken) {
const check = await fetch(
`https://api.avartech.net/api/avar-id/oauth/session/${sessionId}/consent-check`,
{
headers: { 'Authorization': `Bearer ${userToken}` }
}
).then(r => r.json());
if (!check.can_auto_approve) {
// Rıza ekranı göster
return displayConsentScreen();
}
// Auto-approve
const approval = await fetch(
`https://api.avartech.net/api/avar-id/oauth/session/${sessionId}/approve`,
{
method: 'POST',
headers: { 'Authorization': `Bearer ${userToken}` },
body: JSON.stringify({ remember: true })
}
).then(r => r.json());
// Redirect
window.location = approval.redirect_uri;
}
Rıza İptal Etme
Kullanıcılar account settings'te komodulun uygulamalarını bulup rıza iptal edebilirler:
- Account Settings → Connected Apps
- Uygulamayı seçip "Revoke Access"
- Tüm access token'lar derhal geçersiz hale gelir
- Sonraki istekte yeniden rıza verilmesi gerekir
OAuth Endpoint'leri
Avar ID API'sinin OAuth ile ilgili tüm endpoint'lerinin teknik referansı.
Authorization Endpoint
GET /api/avar-id/oauth/authorize
| Parametre | Tür | Zorunlu | Açıklama |
|---|---|---|---|
| client_id | string | ✓ | App client ID'si |
| redirect_uri | string (URI) | ✓ | Callback URL (registered'dan eşit olmalı) |
| response_type | string | ✓ | Daima "code" |
| scope | string | ✓ | Space-separated scopes (e.g., openid email profile) |
| state | string | ✓ | CSRF token (sunucu geri gönderir) |
| code_challenge | string | ✓ | PKCE challenge (base64url SHA256) |
| code_challenge_method | string | ✓ | Daima "S256" |
Başarılı Yanıt (HTTP 302)
HTTP/1.1 302 Found Location: https://api.avartech.net/api/avar-id/oauth/authorize?session_id=sess_... Set-Cookie: session_id=sess_...; Path=/; HttpOnly; Secure
Token Endpoint
POST /api/avar-id/oauth/token
Authorization code'u access token'a dönüştürür.
POST /api/avar-id/oauth/token HTTP/1.1 Host: api.avartech.net Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=authcode_xyz...&code_verifier=verifier_128_chars...&client_id=app_...&client_secret=secret_...&redirect_uri=https://app.example.com/callback
Başarılı Yanıt (200 OK)
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "refresh_token_xyz...",
"scope": "openid email profile"
}
User Profile Endpoint
GET /api/avar-id/user/profile
GET /api/avar-id/user/profile HTTP/1.1 Host: api.avartech.net Authorization: Bearer
Başarılı Yanıt (200 OK)
{
"user_id": "507f1f77bcf86cd799439011",
"username": "coffee_lover_42",
"email": "user@example.com",
"display_name": "Coffee Lover",
"avatar_url": "https://example.com/avatars/user_507f.jpg",
"bio": "Coffee enthusiast",
"location": "Seattle, WA",
"created_at": "2024-01-15T10:30:00Z",
"verified": true
}
Session Management Endpoint'leri
POST /api/avar-id/oauth/session/{sessionId}/approve
POST /api/avar-id/oauth/session/sess_789/approve HTTP/1.1 Host: api.avartech.net Authorization: BearerContent-Type: application/json { "remember": true }
Başarılı Yanıt
{
"status": "approved",
"authorization_code": "authcode_xyz123...",
"code_expires_at": "2026-03-27T10:40:00Z",
"redirect_uri": "https://app.example.com/callback?code=authcode_xyz...&state=xyz..."
}
Developer App Management Endpoint'leri
POST /api/avar-id/dev/apps — Yeni app oluştur
POST /api/avar-id/dev/apps HTTP/1.1 Host: api.avartech.net Authorization: BearerContent-Type: application/json { "name": "Coffee Club", "description": "Share coffee recipes", "website": "https://coffeeclub.example.com", "icon_url": "https://cdn.example.com/icon.png", "redirect_uris": ["https://coffeeclub.example.com/callback"], "scopes": ["openid", "email", "profile", "chat:read"] }
201 Created
{
"app_id": "app_xyz...",
"client_id": "app_xyz...",
"client_secret": "secret_abc123...",
"name": "Coffee Club",
"scopes": ["openid", "email", "profile", "chat:read"],
"created_at": "2026-03-27T10:30:00Z"
}
OAuth Scopes
Scopes, uygulamanın kullanıcı tarafından erişmesine izin verilen verileri tanımlarlar. Minimum gerekeni iste!
Identity Scopes
| Scope | Açıklama | Veri |
|---|---|---|
openid | Kullanıcı kimliği (zorunlu) | Benzersiz user_id |
profile | Profil bilgileri | username, avatar, bio, location, website |
email | Email adresi | Email + verified flag |
Chat Scopes
| Scope | Açıklama |
|---|---|
chat:read | Mesaj ve kanalları oku |
chat:write | Mesaj gönder, kanal oluştur |
chat:manage | Kanal ayarlarını yönet |
Social Scopes
| Scope | Açıklama |
|---|---|
friends:read>td> | Arkadaş listesi görüntüle |
friends:write | Arkadaş ekle / çıkar |
Moderation Scopes
| Scope | Açıklama |
|---|---|
moderation:read | Moderation loglarını oku |
moderation:write | Moderation aksiyonu al (ban, warn, vb.) |
Best Practices
Hata Kodları
Avar ID belirli, standardize hata kodları döndürür. Debugging için request_id'yi kullanabilirsin.
Hata Formatı
Authorization endpoint'de (HTTP 302 redirect):
https://app.example.com/callback? error=invalid_scope& error_description=Scope+%27chat%3Awrite%27+not+allowed& state=xyz123
Token endpoint'de (JSON 400):
{
"error": "invalid_code",
"error_description": "Authorization code has expired",
"timestamp": "2026-03-27T10:35:00Z",
"request_id": "req_xyz123_abcdef"
}
Hata Kodları Listesi
| Kod | Durum | Açıklama | Çözüm |
|---|---|---|---|
invalid_client | 400 | Unknown client_id veya geçersiz client_secret | Dev Portal'ında client_id/secret'i doğrula |
invalid_scope | 302* | Scope izni verilmemiş | Dev Portal'da scope'u enable et |
invalid_code | 400 | Code geçersiz/expired (10 min) | Authorization akışını yeniden başlat |
invalid_redirect_uri | 302 | redirect_uri registered'dan farklı | URL'i tam olarak eşle veya Dev Portal'da kaydet |
access_denied | 302 | Kullanıcı rızayı reddetted | Normal — tekrar istemeyi dene |
expired_token | 401 | Access token expired (1 hour) | refresh_token ile yeni token al |
invalid_token | 401 | Token geçersiz/revoked | Kullanıcısını yeniden login et |
* Yalnızca authorization endpoint'de HTTP 302 redirection'ında
Debugging
request_id field'ini support'a vererek, sunucu tarafındaki detaylı logları alabilirsin.
Güvenlik Best Practices
OAuth uygulanırken yaygın güvenlik hatalarını ve bu hataları önleme yollarını öğren.
PKCE Güvenliği — Code Challenge
code_verifier'ı tahmini rastgele kaynaklara (saat, sayaca dayalı) güvenmeyin. Daima crypto.randomBytes() gibi güvenli RNG kullan.
PKCE akışında yanlışlıklar:
| ❌ Yanlış | ✅ Doğru |
|---|---|
code_challenge_method=plain | code_challenge_method=S256 |
const v = Math.random().toString() | crypto.randomBytes(96).toString('base64') |
| code_challenge = base64(verifier) | code_challenge = base64url(sha256(verifier)) |
| Token istemeinde code_verifier'ı göndermeme | Token istemeinde code_verifier'ı gönderme |
State Parameter — CSRF Koruması
state parameter'i her istek için benzersiz olmalıdır:
// ✅ Doğru — Her seferinde yeni
const state = crypto.randomBytes(32).toString('hex');
session.state = state;
// ❌ Yanlış — Fixed
const state = 'my_fixed_state';
// ❌ Yanlış — Tahmin edilebilir
const state = Math.rand().toString();
Token Storage — Secure Depolama
| Yöntem | Güvenlik | Platform |
|---|---|---|
| HttpOnly Cookie | ✅ Çok iyi | Web app |
| Secure Storage (OS-level) | ✅ Çok iyi | Mobile/Desktop |
| localStorage | ❌ Kötü (XSS) | Web app |
| sessionStorage | ❌ Kötü (XSS) | Web app |
// ✅ HttpOnly Cookie (XSS'e karşı)
res.cookie('access_token', tokenData.access_token, {
httpOnly: true, // JavaScript erişemiyor
secure: true, // HTTPS-only
sameSite: 'strict', // CSRF koruması
maxAge: 3600000 // 1 hour
});
Client Secret — Sırrı Koru
Client secret asla şu yerlerde bulunmamalı:
- Frontend JavaScript kodu
- Git repository'si
- Error logs (hata bildiriminin içinde)
- İnsan-okunabilir config dosyaları
Client secret sadece:
- Backend'de, environment variables'ta (
.env) .envfİle .gitignore'da kayıtlıysa- Secure vaults (Docker Secret, AWS Secrets Manager, vault)
HTTPS — Şifreli Kanal
Kod Örnekleri
Farklı dillerde tam çalışan OAuth entegrasyon örnekleri.
Node.js + Express Örneği
import express from 'express';
import fetch from 'node-fetch';
import crypto from 'crypto';
require('dotenv').config();
const app = express();
const sessions = new Map();
function generatePKCE() {
const v = crypto.randomBytes(96).toString('base64').replace(/[^a-zA-Z0-9_-]/g, '');
const c = crypto.createHash('sha256').update(v).digest('base64').replace(/[^a-zA-Z0-9_-]/g, '');
return { verifier: v, challenge: c };
}
app.get('/login', (req, res) => {
const { verifier, challenge } = generatePKCE();
const state = crypto.randomBytes(32).toString('hex');
const sid = 'sess_' + Date.now();
sessions.set(sid, { verifier, state });
const url = new URL('https://api.avartech.net/api/avar-id/oauth/authorize');
url.searchParams.set('client_id', process.env.CLIENT_ID);
url.searchParams.set('redirect_uri', process.env.REDIRECT_URI);
url.searchParams.set('response_type', 'code');
url.searchParams.set('scope', 'openid email profile');
url.searchParams.set('state', state);
url.searchParams.set('code_challenge', challenge);
url.searchParams.set('code_challenge_method', 'S256');
res.redirect(url.toString());
});
app.get('/oauth/callback', async (req, res) => {
const { code, state, error } = req.query;
if (error) return res.status(400).send(`Error: ${error}`);
const sid = Array.from(sessions.keys())[0];
const session = sessions.get(sid);
if (state !== session.state) return res.status(403).send('State mismatch');
try {
const tokenRes = await fetch('https://api.avartech.net/api/avar-id/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
code_verifier: session.verifier,
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
redirect_uri: process.env.REDIRECT_URI
})
});
const tokenData = await tokenRes.json();
if (!tokenRes.ok) throw new Error(tokenData.error);
const userRes = await fetch('https://api.avartech.net/api/avar-id/user/profile', {
headers: { Authorization: `Bearer ${tokenData.access_token}` }
});
const user = await userRes.json();
res.json({ success: true, user });
} catch (err) {
res.status(400).json({ error: err.message });
}
});
app.listen(3000, () => console.log('Server running on :3000'));
Python + Flask Örneği
from flask import Flask, redirect, request, session, jsonify
import requests
import os
import secrets
import hashlib
import base64
app = Flask(__name__)
app.secret_key = os.environ.get('SECRET', 'dev')
def generate_pkce():
v = base64.urlsafe_b64encode(secrets.token_bytes(96)).decode().rstrip('=')
c = base64.urlsafe_b64encode(hashlib.sha256(v.encode()).digest()).decode().rstrip('=')
return v, c
@app.route('/login')
def login():
verifier, challenge = generate_pkce()
state = secrets.token_hex(32)
session['pkce_verifier'] = verifier
session['state'] = state
params = {
'client_id': os.environ['CLIENT_ID'],
'redirect_uri': os.environ['REDIRECT_URI'],
'response_type': 'code',
'scope': 'openid email profile',
'state': state,
'code_challenge': challenge,
'code_challenge_method': 'S256'
}
url = 'https://api.avartech.net/api/avar-id/oauth/authorize'
return redirect(f"{url}?{'&'.join(f'{k}={v}' for k,v in params.items())}")
@app.route('/oauth/callback')
def callback():
code = request.args.get('code')
state = request.args.get('state')
error = request.args.get('error')
if error:
return jsonify({'error': error}), 400
if state != session.get('state'):
return jsonify({'error': 'State mismatch'}), 403
try:
token_res = requests.post(
'https://api.avartech.net/api/avar-id/oauth/token',
data={
'grant_type': 'authorization_code',
'code': code,
'code_verifier': session['pkce_verifier'],
'client_id': os.environ['CLIENT_ID'],
'client_secret': os.environ['CLIENT_SECRET'],
'redirect_uri': os.environ['REDIRECT_URI']
}
)
tokenData = token_res.json()
if not token_res.ok:
raise Exception(tokenData.get('error_description'))
user_res = requests.get(
'https://api.avartech.net/api/avar-id/user/profile',
headers={'Authorization': f"Bearer {tokenData['access_token']}"}
)
user = user_res.json()
return jsonify({'success': True, 'user': user})
except Exception as err:
return jsonify({'error': str(err)}), 400
if __name__ == '__main__':
app.run(debug=True)
Frontend JavaScript (React örneği)
import { useEffect } from 'react';
function AvarLoginButton() {
const login = async () => {
// PKCE üret
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
let verifier = '';
for (let i = 0; i < 128; i++) verifier += chars[Math.floor(Math.random() * chars.length)];
const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));
const challenge = btoa(String.fromCharCode(...new Uint8Array(hash))).replace(/[^a-zA-Z0-9_-]/g, '');
const state = Array.from(crypto.getRandomValues(new Uint8Array(32)))
.map(x => x.toString(16).padStart(2, '0')).join('');
// Session'da sakla
sessionStorage.setItem('pkce_verifier', verifier);
sessionStorage.setItem('state', state);
// Yönlendir
const url = new URL('https://api.avartech.net/api/avar-id/oauth/authorize');
url.searchParams.set('client_id', process.env.REACT_APP_CLIENT_ID);
url.searchParams.set('redirect_uri', process.env.REACT_APP_REDIRECT_URI);
url.searchParams.set('response_type', 'code');
url.searchParams.set('scope', 'openid email profile');
url.searchParams.set('state', state);
url.searchParams.set('code_challenge', challenge);
url.searchParams.set('code_challenge_method', 'S256');
window.location = url.toString();
};
return ;
}
export default AvarLoginButton;
OAuth Testing
OAuth akışını test etmek için adım adım rehber ve hazır test araçları.
Manual Testing Checklist
-
1Dev Portal'da app registre et
dev-portal'da yeni uygulama oluştur, client_id ve secret'i kopyala.
-
2Authorize endpoint'i test et
Tarayıcıda direkt olarak GET authorize URL'sini aç, rıza ekranını gör.
-
3Rıza ekranında onay ver
Callback URL'sinde code parametresi almalısın.
-
4Token exchange yap
Code'u POST token endpoint'ine gönder, access_token'ı al.
-
5User profile'ı çek
GET /user/profile'a access_token ile ister, profil bilgilerini al.
Postman Collection
Aşağıdaki JSON'ı Postman'de import et:
{
"info": { "name": "Avar ID OAuth", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" },
"item": [
{
"name": "1. Authorize", "request": {
"method": "GET",
"url": "https://api.avartech.net/api/avar-id/oauth/authorize?client_id={{CLIENT_ID}}&redirect_uri={{REDIRECT_URI}}&response_type=code&scope=openid%20email&state={{STATE}}&code_challenge={{CODE_CHALLENGE}}&code_challenge_method=S256"
}
},
{
"name": "2. Token Exchange", "request": {
"method": "POST",
"url": "https://api.avartech.net/api/avar-id/oauth/token",
"body": { "mode": "urlencoded", "urlencoded": [
{ "key": "grant_type", "value": "authorization_code" },
{ "key": "code", "value": "{{AUTH_CODE}}" },
{ "key": "code_verifier", "value": "{{CODE_VERIFIER}}" },
{ "key": "client_id", "value": "{{CLIENT_ID}}" },
{ "key": "client_secret", "value": "{{CLIENT_SECRET}}" },
{ "key": "redirect_uri", "value": "{{REDIRECT_URI}}" }
]}
}
},
{
"name": "3. User Profile", "request": {
"method": "GET",
"url": "https://api.avartech.net/api/avar-id/user/profile",
"header": [{ "key": "Authorization", "value": "Bearer {{ACCESS_TOKEN}}" }]
}
}
],
"variable": [
{ "key": "CLIENT_ID", "value": "" },
{ "key": "CLIENT_SECRET", "value": "" },
{ "key": "REDIRECT_URI", "value": "http://localhost:3000/oauth/callback" },
{ "key": "STATE", "value": "xyz123abc456" },
{ "key": "CODE_VERIFIER", "value": "" },
{ "key": "CODE_CHALLENGE", "value": "" },
{ "key": "AUTH_CODE", "value": "" },
{ "key": "ACCESS_TOKEN", "value": "" }
]
}
cURL ile Test
# 1. Generate PKCE VERIFIER=$(openssl rand -base64 96 | tr -d '\n' | sed 's/+/-/g; s/\//_/g; s/=//g') CHALLENGE=$(echo -n "$VERIFIER" | openssl dgst -sha256 -binary | base64 | tr -d '\n' | sed 's/+/-/g; s/\//_/g; s/=//g') STATE=$(openssl rand -hex 32) echo "Verifier: $VERIFIER" echo "Challenge: $CHALLENGE" echo "State: $STATE" # 2. Get Auth Code (tarayıcıda açabilirsin) curl -G "https://api.avartech.net/api/avar-id/oauth/authorize" \ --data-urlencode "client_id=YOUR_CLIENT_ID" \ --data-urlencode "redirect_uri=http://localhost:3000/callback" \ --data-urlencode "response_type=code" \ --data-urlencode "scope=openid email" \ --data-urlencode "state=$STATE" \ --data-urlencode "code_challenge=$CHALLENGE" \ --data-urlencode "code_challenge_method=S256" # 3. Exchange Code (AUTH_CODE'u callback'ten kopyala) curl -X POST "https://api.avartech.net/api/avar-id/oauth/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=authorization_code" \ -d "code=AUTH_CODE_HERE" \ -d "code_verifier=$VERIFIER" \ -d "client_id=YOUR_CLIENT_ID" \ -d "client_secret=YOUR_CLIENT_SECRET" \ -d "redirect_uri=http://localhost:3000/callback" # 4. Get User Profile curl "https://api.avartech.net/api/avar-id/user/profile" \ -H "Authorization: Bearer ACCESS_TOKEN_HERE"
Aile Takibi
Bu sayfa yakında tamamlanacak.
Durak Bildirimleri
Bu sayfa yakında tamamlanacak.
API Endpoint'leri
Bu sayfa yakında tamamlanacak. OAuth endpoint'leri için OAuth API → Endpoint'ler bölümünü ziyaret et.
Hata Kodları
Bu sayfa yakında tamamlanacak. OAuth hata kodları için OAuth API → Hata Kodları bölümünü ziyaret et.
Gizlilik Politikası
Tam metin için avartech.net/gizlilik adresini ziyaret et.
Kullanım Koşulları
Bu sayfa yakında tamamlanacak.
KVKK Haklarım
6698 sayılı Kanun kapsamındaki haklarını kullanmak için iletisim@avartech.net adresine başvurabilirsin. Talepler 30 gün içinde yanıtlanır.