İçeriğe geç
Docs

Kimlik Doğrulama ve Kapsamlar

Kapsamlar, kurulum token’ı (tcfi_), 60 sn’lik oturum JWT’si, X-Tecof-Signature imzalı istekler ve istemci sırrı döndürme.

Son güncelleme:

Partner uygulamaları üç kimlik kanalı kullanır: sunucunuzun Tecof API'sine eriştiği kurulum token'ı, panelin iframe arayüzünüze verdiği oturum token'ı ve Tecof'un size gönderdiği isteklerdeki imza. Üçünün de anahtarı istemci sırrınızdır (tcfs_…); bu yüzden sır yalnız sunucuda durur.


Kimlik Bilgileri#

KimlikBiçimÖmürNot
İstemci kimliğitcfa_…KalıcıHerkese açık; tarayıcı koduna girebilir
İstemci sırrıtcfs_…Döndürülene kadarYalnız uygulama oluşturma ve sır döndürme yanıtında bir kez gösterilir; Tecof tarafında şifreli saklanır
Kurulum token'ıtcfi_…1 saatBir kuruluma (mağaza + uygulama) bağlıdır; /api/v1/* için Bearer
Oturum token'ıJWT, HS25660 saniyePanel → iframe kimlik kanıtı; anahtarı istemci sırrı
Geliştirici (CLI) token'ıtcfd_…Cihaz oturumu 30 gün (14 gün kullanılmazsa düşer); kapsamlı CI token'ı 1–365 günCLI, /api/developer uçları ve alanlar (Spaces); bkz. Güvenlik ve CI

Kapsamlar#

Mağaza, kurulum onay ekranında uygulamanın istediği kapsamların tamamını görür ve onaylar. Uygulamaya açık kapsamlar Developer API kapsamlarının alt kümesidir:

KapsamNe verir
orders:readSipariş okuma (/api/v1/orders), order.* webhook'ları, sipariş ekranı aksiyonları, sipariş metafield'ları
orders:writeSipariş yazma işlemleri (Tools API araçları)
customers:readMüşteri okuma (/api/v1/customers), customer.* webhook'ları, müşteri ekranı aksiyonları, müşteri metafield'ları
customers:writeMüşteri yazma işlemleri
products:readÜrün ve envanter okuma (/api/v1/products, /api/v1/inventory), product.*, inventory.*, review.* webhook'ları, ürün ekranı aksiyonları, ürün metafield'ları
products:writeÜrün yazma; envanter dahil (ör. apply_stock_count aracı). status: "active" ürün anında vitrine çıkar
marketing:readPazarlama verisi okuma, subscriber.created webhook'u
marketing:writePazarlama yazma işlemleri
settings:read / settings:writeMağaza ayarlarını okuma / yazma
cms:readCMS koleksiyon ve içerik okuma, cms.item.published ve form.submitted webhook'ları
cms:writeCMS içerik yazma (taslak)
pages:read / pages:writeSayfa okuma / taslak yazma
media:read / media:writeMedya kütüphanesi okuma / dosya ekleme
analytics:readAnalitik ve mağaza özeti okuma
domains:readAlan adı ve DNS kayıtlarını okuma

Uygulamaya kapalı kapsamlar#

Aşağıdaki kapsamlar uygulamalara verilemez; tecof.app.json içinde istenirse yapılandırma reddedilir. Bunlar ya canlı siteyi doğrudan değiştirir ya da kredi harcar ya da mağaza sahibinin kişisel asistan verisidir.

KapsamDeveloper API'deki etkisi
code:read, code:writeTema deposu okuma/yazma; push üretim yayınını tetikler
sandbox:runBulut IDE'de kod çalıştırma; kredi düşer
deploy:writeÜretim yeniden dağıtımı, canlı temayı değiştirme
memory:read, memory:writeMağaza sahibinin asistana yazdığı kalıcı talimatlar
domains:writeDNS ve nameserver değişikliği; canlı siteyi etkiler
ai:generateAI görsel üretimi; kredi düşer

Kapsam istemeyen veriler#

Uygulamanın kendi verisi kapsam istemez; kurulumun kendisi yetkidir:

  • Ayarlar (/api/v1/app/settings)
  • Sırlar / Vault (/api/v1/app/secrets)
  • merchant sahipli metafield'lar (diğer sahip tipleri, kaynağın okuma kapsamını ister)
  • Uygulama olayları (/api/v1/app/events)

Kapsam değişikliği#

  • Artış: Yeni yapılandırma ek kapsam isterse mevcut kurulumlara pendingScopes yazılır. Panel "Ek izin istiyor" uyarısı gösterir; mağaza onaylayana kadar kurulum eski kapsamla çalışır. Onayda grantedScopes güncellenir, kurulumun token'ları iptal edilir ve app.scopes_updated webhook'u gönderilir. Yayındaki uygulamada kapsam artışı yeniden inceleme ister.
  • Azalma: Anında uygulanır.

Yetersiz kapsamla yapılan çağrı 403 insufficient-scope döner; data alanı { required: [...], missing: [...] } içerir.


Kurulum Token'ı (Sunucu → Tecof API)#

Sunucunuz, istemci kimliği ve sırrıyla bir kurulum için kısa ömürlü erişim token'ı alır (OAuth 2.0 client_credentials benzeri). Kullanıcıdan bağımsızdır: webhook işleyicilerinde ve arka plan işlerinde de çalışır.

İstek#

Terminal
curl -X POST https://api.tecof.com/api/apps/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "tcfa_xxxxxxxxxxxxxxxx",
    "client_secret": "tcfs_xxxxxxxxxxxxxxxx",
    "installation_id": "6720aa00bb11cc22dd33ee55"
  }'

application/x-www-form-urlencoded gövde de kabul edilir. installation_id yerine merchant_id verilebilir; bu durumda o mağazadaki bu uygulamanın kurulumu kullanılır.

Yanıt#

JSON
{
  "access_token": "tcfi_xxxxxxxxxxxxxxxx",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "orders:read customers:read",
  "installation_id": "6720aa00bb11cc22dd33ee55",
  "merchant_id": "66f0c0ffee0000000000abcd"
}

Hatalar#

Hatalar OAuth biçimindedir:

HTTPGövdeNeden
400{ "error": "invalid_request" }Eksik ya da hatalı alan
401{ "error": "invalid_client" }İstemci kimliği/sırrı yanlış
400{ "error": "invalid_grant", "error_description": "installation-not-found" }Kurulum yok ya da bu uygulamaya ait değil
400{ "error": "invalid_grant", "error_description": "installation-inactive" }Kurulum aktif değil (kaldırılmış, askıda…)

Kurallar#

  • Token 1 saat geçerlidir. Kapsam değiştiğinde ya da uygulama askıya alındığında anında iptal edilir. Kaldırmada token'lar app.uninstalled teslimatı bittikten sonra iptal edilir; bu sayede kaldırma işleyiciniz API'yi hâlâ çağırabilir.
  • Token Tecof'ta sha256 özeti olarak saklanır; token isteği ve yanıtı istek günlüklerine yazılmaz.
  • installation_id değerini şuralardan alırsınız: app.installed webhook'u, her webhook'taki X-Tecof-Installation-Id başlığı, doğrulanmış oturum token'ındaki iid iddiası.
  • Uygulama token'ında paket özelliği kapısı (apiAccess) uygulanmaz: uygulama erişimi mağazanın paketine bağlı değildir.
  • Hız sınırı kurulum başınadır: okuma 240/dk, yazma 60/dk.
  • /api/v1 uçlarında geçersiz token 401 döner; messageCode: token-invalid, token-revoked, token-expired ya da app-suspended (uygulama askıda). Ayrıntılar: API ile Veri Erişimi.

SDK ile#

SDK token'ı alır, önbelleğe alır ve süresi dolmadan yeniler:

TypeScript
import { tecof } from "@/lib/tecof";

const api = tecof.client(installationId);
const orders = await api.orders.list({ paymentStatus: "paid", limit: 50 });

Oturum Token'ı (Panel → iframe)#

Panel, gömülü uygulamanızı açarken kurulum için 60 saniyelik bir JWT basar. iframe arayüzünüz bu token'ı kendi sunucunuza Authorization: Bearer olarak gönderir; sunucunuz istemci sırrınızla ek bir çağrı yapmadan doğrular.

İddialar (claims)#

JSON
{
  "iss": "https://api.tecof.com",
  "aud": "tcfa_xxxxxxxxxxxxxxxx",
  "sub": "66f0aa00bb11cc22dd33ee66",
  "mid": "66f0c0ffee0000000000abcd",
  "iid": "6720aa00bb11cc22dd33ee55",
  "locale": "tr",
  "jti": "a1b2c3d4",
  "iat": 1760000000,
  "nbf": 1759999995,
  "exp": 1760000060
}
İddiaAnlamı
isshttps://api.tecof.com
audUygulamanızın istemci kimliği
subPaneli kullanan kullanıcının kimliği
midMağaza kimliği
iidKurulum kimliği
localePanel dili
jtiToken kimliği
iat / nbf / expBasım zamanı / iat - 5 / iat + 60

Başlık: { "alg": "HS256", "typ": "JWT" }.

Açılış (launch) adresi#

Panel uygulamayı ilk kez yüklerken iframe adresini şöyle kurar: appUrl + path + sorgu dizesi. Test mağazasında devUrl (CLI tüneli) appUrl'in yerine geçer.

Metin
https://isbasi.apps.tecof.site/invoices?installationId=6720aa00bb11cc22dd33ee55&merchantId=66f0c0ffee0000000000abcd&locale=tr&embedded=1&host=aHR0cHM6Ly9hcHAudGVjb2YuY29t&session=eyJhbGciOiJIUzI1NiIs...
ParametreAnlamı
installationIdKurulum kimliği
merchantIdMağaza kimliği
localePanel dili
embedded1 — panel içinde açıldı
hostPanel origin'inin base64url kodlaması; App Bridge yalnız bu origin'den gelen mesajı kabul eder
sessionİlk oturum token'ı
actionRunId, actionKey, placement, idsYalnız iframe aksiyonlarında. Bkz. Aksiyonlar

İlk token 60 saniyede dolar. Sonraki istekler için App Bridge getSessionToken() ile yeni token alın; SDK bitişe 10 saniye kala kendiliğinden yeniler. Bkz. App Bridge.

Oturum token'ı doğrulama kuralları#

  1. İmza HS256, anahtar istemci sırrı. Panel token'ı her zaman güncel sırla imzalar; sır döndürmeden sonraki 24 saat boyunca doğrulamada eski sır da denenir (döndürme anından hemen önce basılmış token'lar için).
  2. aud = istemci kimliğiniz.
  3. exp ve nbf ±10 saniye toleransla.
  4. iss = https://api.tecof.com.

SDK ile doğrulama#

TypeScript
// app/api/orders/route.ts
import { TecofAuthError } from "@tecof/app-sdk";
import { tecof } from "@/lib/tecof";

export async function GET(req: Request) {
  let session;
  try {
    // Authorization: Bearer <oturum token'ı>
    session = tecof.sessionFromRequest(req);
  } catch (err) {
    if (err instanceof TecofAuthError) {
      return Response.json({ error: "unauthorized" }, { status: 401 });
    }
    throw err;
  }

  // session = { merchantId, installationId, userId, locale, exp }
  const api = tecof.client(session.installationId);
  const orders = await api.orders.list({ limit: 20 });
  return Response.json({ orders });
}

Token'ı başka bir kaynaktan aldıysanız tecof.verifySessionToken(token) aynı iddiaları döner ya da TecofAuthError fırlatır.

Oturum token'ını elle doğrulama (Node.js)#

SDK kullanmayan bir sunucu için eşdeğer doğrulama:

TypeScript
import { createHmac, timingSafeEqual } from "node:crypto";

type SessionClaims = {
  iss: string; aud: string; sub: string; mid: string; iid: string;
  locale: string; jti: string; iat: number; nbf: number; exp: number;
};

function b64urlToBuffer(input: string): Buffer {
  return Buffer.from(input.replace(/-/g, "+").replace(/_/g, "/"), "base64");
}

export function verifyTecofSessionToken(
  token: string,
  opts: { clientId: string; secrets: string[]; leewaySec?: number },
): SessionClaims {
  const leeway = opts.leewaySec ?? 10;
  const parts = token.split(".");
  if (parts.length !== 3) throw new Error("malformed-token");
  const [headerB64, payloadB64, signatureB64] = parts;

  const header = JSON.parse(b64urlToBuffer(headerB64).toString("utf8"));
  if (header.alg !== "HS256") throw new Error("unsupported-alg");

  const signature = b64urlToBuffer(signatureB64);
  const signed = `${headerB64}.${payloadB64}`;
  // secrets: [güncel sır, (döndürmeden sonraki 24 saat içinde) eski sır]
  const valid = opts.secrets.some((secret) => {
    const expected = createHmac("sha256", secret).update(signed).digest();
    return expected.length === signature.length && timingSafeEqual(expected, signature);
  });
  if (!valid) throw new Error("invalid-signature");

  const claims = JSON.parse(b64urlToBuffer(payloadB64).toString("utf8")) as SessionClaims;
  const now = Math.floor(Date.now() / 1000);
  if (claims.aud !== opts.clientId) throw new Error("invalid-audience");
  if (claims.iss !== "https://api.tecof.com") throw new Error("invalid-issuer");
  if (now > claims.exp + leeway) throw new Error("token-expired");
  if (now < claims.nbf - leeway) throw new Error("token-not-yet-valid");
  return claims;
}

İmzalı İstekler (Tecof → Uygulama)#

Webhook teslimatları ve API aksiyonu çağrıları aynı imzayı taşır:

HTTP
X-Tecof-Timestamp: 1760000000
X-Tecof-Signature: t=1760000000,v1=5f2c…e91a
Metin
v1 = HEX( HMAC_SHA256( clientSecret, `${t}.${rawBody}` ) )

Sır döndürmeden sonraki 24 saat boyunca başlık iki imza taşır: t=…,v1=<yeni sırla>,v1=<eski sırla>.

İmza doğrulama kuralları#

  1. t değeri ile şimdiki zaman arasındaki fark en fazla 300 saniye olmalı (tekrar saldırısı penceresi).
  2. Başlıktaki v1 değerlerinden herhangi biri beklenen imzayla eşleşirse istek geçerlidir.
  3. Karşılaştırma sabit zamanlı yapılmalıdır (timingSafeEqual).
  4. İmza ham gövde üzerinden hesaplanır. Next.js'te await req.text() kullanın; req.json() ile okuyup yeniden serileştirmek imzayı bozar.

SDK ile (tek satır)#

TypeScript
const event = tecof.verifyWebhook(rawBody, req.headers);           // webhook
const action = tecof.verifyActionRequest(rawBody, req.headers);    // API aksiyonu

İkisi de imza geçersizse ya da zaman damgası pencere dışındaysa hata fırlatır.

İmzayı elle doğrulama (Node.js)#

TypeScript
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyTecofSignature(
  rawBody: string,
  signatureHeader: string | null,
  secrets: string[],          // [güncel sır, (24 saat içinde) eski sır]
  toleranceSec = 300,
): boolean {
  if (!signatureHeader) return false;

  const parts = signatureHeader.split(",").map((p) => p.trim());
  const t = parts.find((p) => p.startsWith("t="))?.slice(2);
  const candidates = parts.filter((p) => p.startsWith("v1=")).map((p) => p.slice(3));
  if (!t || candidates.length === 0) return false;

  const ts = Number(t);
  const now = Math.floor(Date.now() / 1000);
  if (!Number.isFinite(ts) || Math.abs(now - ts) > toleranceSec) return false;

  for (const secret of secrets) {
    const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
    for (const candidate of candidates) {
      const given = Buffer.from(candidate, "hex");
      if (given.length === expected.length && timingSafeEqual(given, expected)) return true;
    }
  }
  return false;
}

// Kullanım (Next.js route handler):
// const raw = await req.text();
// const ok = verifyTecofSignature(raw, req.headers.get("x-tecof-signature"), [process.env.TECOF_CLIENT_SECRET!]);

Terminalde imza üretmek#

Yerel testte aynı imzayı openssl ile üretebilirsiniz:

Terminal
T=$(date +%s)
BODY='{"id":"evt_test","event":"order.paid","test":true}'
SIG=$(printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "$TECOF_CLIENT_SECRET" | awk '{print $NF}')
echo "X-Tecof-Signature: t=$T,v1=$SIG"

Sır Döndürme#

Terminal
tecof app secret rotate

ya da POST /api/developer/apps/:id/secret/rotate → { clientSecret, previousValidUntil }.

Döndürmeden sonraki 24 saat boyunca:

KanalEski sırrın durumu
Token ucu (/api/apps/oauth/token)Eski sır da kabul edilir
Webhook / API aksiyonu imzasıBaşlık iki v1 taşır (yeni + eski)
Oturum token'ıPanel geçiş boyunca eski sırla imzalar (yeni sırra henüz geçmemiş sunucu da doğrular); 24 saat sonra yeni sırla. SDK her iki sırrı da dener (previousClientSecret)
  • Tecof barındırması: Vercel ortam değişkenleri güncellenir (TECOF_CLIENT_SECRET = yeni sır, TECOF_PREVIOUS_CLIENT_SECRET = eski_sır|bitiş_zamanı) ve yeniden dağıtım kendiliğinden tetiklenir. SDK eski sırrı bitiş zamanından sonra kendiliğinden yok sayar.
  • Kendi sunucunuz: Yeni sırrı TECOF_CLIENT_SECRET'a, eski sırrı geçiş süresince TECOF_PREVIOUS_CLIENT_SECRET'a yazın (isterseniz tcfs_…|2026-10-11T20:00:00Z biçiminde bitişle — SDK bitişten sonra yok sayar); 24 saat içinde dağıtın. Geçiş boyunca oturum token'ları eski sırla imzalandığı için yeni sırra geçene kadar arayüz kesintisiz çalışır.
TypeScript
export const tecof = new TecofApp({
  clientId: process.env.TECOF_CLIENT_ID!,
  clientSecret: process.env.TECOF_CLIENT_SECRET!,                    // yeni (güncel) sır
  previousClientSecret: process.env.TECOF_PREVIOUS_CLIENT_SECRET,    // isteğe bağlı: 24 saatlik geçişte eski sır
});

Güvenlik Kontrol Listesi#

  • İstemci sırrı yalnız sunucu ortam değişkeninde; NEXT_PUBLIC_ önekli değişkende, repoda, istemci paketinde yok.
  • Her webhook ve API aksiyonu isteği ham gövde üzerinden imza + zaman damgası ile doğrulanıyor.
  • Yetki kararları sorgu dizesine göre değil, doğrulanmış oturum token'ının mid / iid değerlerine göre veriliyor.
  • Oturum token'ı ve kurulum token'ı günlüğe, URL'e, hata mesajına yazılmıyor.
  • Yalnız gerçekten gereken kapsamlar isteniyor.
  • Mağazadan panel parolası, panel JWT'si ya da mağaza secretKey'i istenmiyor; platform bunları uygulamalara vermez.
  • Üçüncü taraf API anahtarları (ör. muhasebe yazılımı parolası) Vault'ta saklanıyor. Bkz. Vault.
  • app.uninstalled geldiğinde o kuruluma ait kendi kopyalarınız siliniyor; teslimattan sonra Tecof token'ları iptal eder, ayarları ve sırları siler.
  • App Bridge mesajları yalnız host parametresindeki origin'le alınıp veriliyor (SDK bunu yapar).
  • Sır döndürme prosedürü (24 saatlik geçiş) belgelenmiş.