Search by

GlobalTrustID PHP istemcisi — OAuth/OpenID Connect ile giriş, QR ile kimlik doğrulama ve webhook imza doğrulaması.

v1.1.0 2026-09-17 10:06 UTC

This package is auto-updated.

Last update: 2026-09-17 10:16:09 UTC


README

GlobalTrustID'nin resmî PHP istemcisi. Üç işi yapar:

  1. "GlobalTrustID ile giriş" — OAuth 2.0 / OpenID Connect, PKCE (S256) ile.
  2. Kimlik doğrulama — QR oluşturur, kullanıcı telefonuyla onaylar, sonucu okursunuz.
  3. Webhook doğrulama — gelen bildirimin gerçekten bizden geldiğini kanıtlar.
use GlobalTrustID\Client;

$gti = new Client(['client_id' => 'gtid_...', 'redirect_uri' => 'https://siteniz.com/callback']);

header('Location: ' . $gti->loginUrl());   // 1. kullanıcıyı yolla
$user = $gti->handleCallback();            // 2. callback'te profili al
echo $user['sub'];
  • Bağımlılık yok — yalnızca curl, json ve openssl eklentileri.
  • PHP 5.6'dan 8.x'e kadar aynı kod. Eski paylaşımlı hostinglerde de çalışır.
  • Laravel ve CodeIgniter için hazır entegrasyon.

İçindekiler

Kurulum

composer require globaltrustid/php

Gereken: PHP 5.6+, ext-curl, ext-json, ext-openssl.

İki ayrı kimlik bilgisi

Bu, çalışmayan entegrasyonların en sık sebebi. api_key ile client_secret aynı şey değildir:

Nereden alınır Ne için
client_id + client_secret Panel → Entegrasyonlar → OAuth Uygulamaları Giriş (OAuth)
api_key (sk_live_...) Panel → API Keys Kimlik doğrulama oturumları

sk_live_... bir API anahtarıdır ve api_key alanına yazılır — OAuth istemcisinin sırrı değildir. Yalnızca giriş yapacaksanız api_key'e, yalnızca kimlik doğrulaması yapacaksanız client_id/client_secret'a ihtiyacınız yok.

Public istemci kullanıyorsanız client_secret yazmayın. Tarayıcı ya da mobil uygulama sır saklayamaz; akışı PKCE korur. Panelde "Public istemci" kutusu bunun için.

1. GlobalTrustID ile giriş

require __DIR__ . '/vendor/autoload.php';

use GlobalTrustID\Client;
use GlobalTrustID\Exception as GtiException;

$gti = new Client([
    'client_id'     => 'gtid_...',
    'redirect_uri'  => 'https://siteniz.com/gti-callback.php',
    'client_secret' => '...',   // yalnızca confidential istemcide
]);

Yönlendirme sayfası:

// SCOPE_OPENID = yalnızca giriş; hiçbir kişisel veri paylaşılmaz.
header('Location: ' . $gti->loginUrl(Client::SCOPE_OPENID));
exit;

Callback sayfası:

try {
    $user = $gti->handleCallback();
} catch (GtiException $e) {
    http_response_code(400);
    exit($e->getMessage());
}

// 'sub' kullanıcının DEĞİŞMEYEN kimliği. Kendi tablonuzda bunu saklayın,
// e-postayı değil — e-posta değişebilir, sub değişmez.
$gtiSub = $user['sub'];

handleCallback() PKCE verifier'ını ve state'i depodan okur, state'i sabit zamanlı karşılaştırır (CSRF), kodu token'a çevirir ve profili döner. Token'lar $user['_tokens'] altındadır.

loginUrl() çağrılmadan önce oturumun başlamış olması gerekir. Varsayılan depo $_SESSION kullanır ve gerekirse session_start()'ı kendisi çağırır.

2. Kimlik doğrulama (QR)

Kullanıcının kimliğini doğrulatmak için — giriş değil.

$gti = new Client(['api_key' => 'sk_live_...']);

$session = $gti->verify(['identity.first_name', 'identity.last_name', 'identity.national_id']);

echo '<img src="' . htmlspecialchars($gti->qrUrl($session['session_id'])) . '">';

Kullanıcı QR'ı GlobalTrustID uygulamasıyla okutup onayladıktan sonra:

$result = $gti->result($session['session_id']);

if ($result['status'] === 'approved') {
    $ad = $result['identity']['identity.first_name'];
}

"Bu kişi kimliğini gerçekten doğruladı mı?"

if ($gti->isIdentityVerified($result)) {
    // Kimlik BELGESİ NFC ile okundu.
}

Ad ve e-posta kullanıcının kendi yazdığı profilden de gelebilir; kimlik numarası ve belge numarası gelemez — onlar yalnızca çipten çıkar. Bu metot tam olarak buna bakar.

Sonuçları beklemek yerine size gönderilmesini isterseniz verify()'a bir webhook adresi verin.

3. Webhook

$gti = new Client(['webhook_secret' => 'whsec_...']);

// HAM gövdeyi ÖNCE okuyun.
$raw = file_get_contents('php://input');

if (!$gti->verifyWebhook($raw)) {
    http_response_code(401);
    exit;
}

$payload = json_decode($raw, true);

Bu adımı atlamayın. Webhook adresiniz herkese açıktır; imzayı doğrulamadan gövdeye güvenirseniz, size sahte bir approved gönderen herkes kimlik doğrulamasını atlatmış olur.

Gövdeyi JSON'a çevirip yeniden kodlarsanız boşluk ve anahtar sırası değişir, imza tutmaz. Ham baytları doğrulayın.

verifyWebhook() ayrıca zaman damgasına bakar: imza geçerli olsa bile 5 dakikadan eski bir istek reddedilir (replay koruması). Kuyruğa alınmış webhook'ları sonradan işliyorsanız $tolerance parametresini 0 yapın.

Laravel

composer require globaltrustid/php

Paket auto-discovery ile bulunur; config/app.php'ye bir şey eklemeniz gerekmez.

GTI_CLIENT_ID=gtid_...
GTI_REDIRECT_URI=https://siteniz.com/gti/callback
GTI_API_KEY=sk_live_...
GTI_WEBHOOK_SECRET=whsec_...
class GtiController extends Controller
{
    public function __construct(private \GlobalTrustID\Client $gti) {}

    public function redirect()
    {
        return redirect()->away($this->gti->loginUrl());
    }

    public function callback(Request $request)
    {
        $profile = $this->gti->handleCallback($request->query());
        // ...
    }
}

İstemciyi kapsayıcıdan alın, elle new Client(...) yazmayın: sağlayıcı onu Laravel oturumuyla kuruyor. Elle kurarsanız PKCE verifier'ı $_SESSION'a yazılır ve Laravel oturumu Redis/veritabanı sürücüsündeyse callback'te geri okunamaz.

Facade da var:

use GlobalTrustID\Laravel\GlobalTrustIDFacade as GlobalTrustID;

return redirect()->away(GlobalTrustID::loginUrl(['email']));

Tam örnek: examples/laravel/.

CodeIgniter 4

composer require globaltrustid/php

CI4'te kendini bağlayan bir sağlayıcı yok; istemciyi bir Services girdisiyle kurarsınız. Hazır dosyalar: examples/codeigniter/.

$gti = service('gti');
return $this->response->redirect($gti->loginUrl());

Alanlar (scope)

Kullanıcı onay ekranında tam olarak ne istediğinizi görür. İstemediğiniz hiçbir alan size gelmez.

Scope Ne döner
openid Yalnızca giriş — hiçbir kişisel veri paylaşılmaz
identity.first_name / identity.last_name Ad / soyad
identity.birth_date Doğum tarihi
identity.gender Cinsiyet
identity.national_id TC kimlik numarası — yalnızca çipten
identity.document_number Belge numarası — yalnızca çipten
identity.nationality Uyruk — yalnızca çipten
age.over_18 Doğum tarihi olmadan yalnızca "true" / "false"
email E-posta
phone Telefon
avatar Profil görselinin adresi

"Yalnızca çipten" işaretli alanlar kullanıcının profilinden doldurulamaz; istemek, NFC ile belge okumasının yapılmış olmasını şart koşar. Listeyi kodda Client::chipOnlyScopes() ile alabilirsiniz.

age.over_18, yaş sınırı denetlemek için doğum tarihi istemenin yerine geçer: kullanıcı doğum gününü paylaşmadan reşit olduğunu kanıtlar.

address ve identity.passport_number HENÜZ İSTEMEYİN. Sunucunun keşif belgesinde (/.well-known/openid-configuration) görünüyorlar ama mobil uygulama bugün ikisini de dolduramıyor — kimlik cüzdanı özelliğiyle gelecekler. Onay için istenen ALANLARIN TAMAMININ dolu olması gerektiğinden, bu ikisini istemek oturumu onaylanamaz hâle getirir: kullanıcı "Onayla"ya basamaz ve sebebini anlamaz.

API

new Client(array $config)

Anahtar Açıklama
client_id OAuth uygulamanız (gtid_...)
client_secret Yalnızca confidential istemcide
redirect_uri Kayıtlı dönüş adresiniz (tam eşleşme)
api_key sk_live_... — kimlik doğrulama oturumları için
webhook_secret Webhook imzasını doğrulamak için
api_base Varsayılan https://api.globaltrust.id
issuer Varsayılan https://globaltrust.id
timeout Saniye, varsayılan 15
verify_ssl Varsayılan true. Kapatmayın.
store StoreInterface — PKCE verifier ve state'in yazılacağı yer

OAuth

  • loginUrl($scope = 'openid'): string
  • handleCallback($query = null): array
  • exchangeCode($code, $codeVerifier): array
  • refresh($refreshToken): array
  • userInfo($accessToken): array

Kimlik doğrulama

  • verify($scope, $webhookUrl = ''): array
  • result($sessionId): array
  • isApproved($sessionId): bool
  • isIdentityVerified($result): bool
  • qrUrl($sessionId): string
  • Client::chipOnlyScopes(): array

Webhook

  • verifyWebhook($rawBody, $signature = null, $timestamp = null, $tolerance = 300): bool

Hatalar GlobalTrustID\Exception olarak fırlatılır.

Composer olmadan

Eski bir kurulumda Composer yoksa dosyaları doğrudan dahil edebilirsiniz:

require_once 'src/Exception.php';
require_once 'src/Store/StoreInterface.php';
require_once 'src/Store/SessionStore.php';
require_once 'src/Client.php';

$gti = new GlobalTrustID\Client([...]);

Sık karşılaşılanlar

"client_secret gerekli" diyor ama public istemcim var. client_secret alanını hiç yazmayın. Boş bir dize göndermek sunucuya "sırrım var ama boş" demektir.

"state eşleşmedi (CSRF şüphesi)". Giriş başka bir tarayıcıda başlatılmış, oturum süresi dolmuş ya da oturum deposu iki istek arasında paylaşılmıyor. Laravel/CI4 kullanıyorsanız oturum deposunu doğru bağladığınızdan emin olun.

"oturum bulunamadı". loginUrl() ile handleCallback() farklı oturumlarda çalışıyor. Yük dengeleyici arkasındaysanız oturumların sunucular arasında paylaşıldığından emin olun.

Webhook imzası tutmuyor. Ham gövdeyi doğruluyor musunuz? $_POST, $request->all() ya da getJSON() gövdeyi çözüp yeniden kodlar ve imza artık tutmaz.

identity boş geliyor. Sonuçlar kalıcı saklanmaz, kısa süre sonra silinir. Onay gelir gelmez okuyun ya da webhook kullanın.

Destek

Lisans

MIT — bkz. LICENSE.