Go
Dış bağımlılığı olmayan Go istemcisi — OAuth ile giriş, QR ile kimlik doğrulama ve webhook imzası.
go-gti, GlobalTrustID'nin resmî Go istemcisi. Üç işi yapar: OAuth ile giriş, QR ile kimlik doğrulama, webhook imzasının doğrulanması.
- Kaynak kod: github.com/GlobalTrustID/go-gti
- Paket: pkg.go.dev/github.com/GlobalTrustID/go-gti
- Lisans: MIT
go get github.com/GlobalTrustID/go-gtiDış bağımlılığı yok — yalnızca standart kütüphane. Her ağ çağrısı context.Context alıyor, hatalar errors.Is / errors.As ile ayırt edilebiliyor.
Hızlı bakış#
import gti "github.com/GlobalTrustID/go-gti"
client, _ := gti.New(gti.Config{
ClientID: "gtid_...",
RedirectURI: "https://siteniz.com/gti/callback",
})
// Yönlendirme
login, _ := client.BeginLogin(gti.ScopeOpenID)
session.Put("gti_state", login.State)
session.Put("gti_verifier", login.Verifier)
http.Redirect(w, r, login.URL, http.StatusFound)
// Callback
tokens, err := client.CompleteLogin(r.Context(), r.URL.Query(),
session.Get("gti_state"), session.Get("gti_verifier"))
user, _ := client.FetchUserInfo(r.Context(), tokens.AccessToken)Oturum saklama neden sizde#
Paket State ve Verifier'ı sizin adınıza saklamıyor: Go'da oturum yönetimi çerçeveye göre değişiyor (çerez, Redis, veritabanı) ve bir soyutlama uydurmak her kurulumda yanlış olurdu.
Buna karşılık CompleteLogin State'i sabit zamanlı karşılaştırıyor ve boş bir beklenen değeri reddediyor — oturumdan okunamamış bir state'i sessizce geçirmek CSRF korumasını tamamen kaldırırdı.
İki ayrı kimlik bilgisi#
APIKey ile ClientSecret aynı şey değildir ve karıştırılmaları çalışmayan entegrasyonların en sık sebebi:
| Nereden alınır | Ne için | |
|---|---|---|
ClientID + ClientSecret | Panel → Entegrasyonlar → OAuth Uygulamaları | Giriş |
APIKey (sk_live_...) | Panel → API Keys | Kimlik doğrulama oturumları |
Public istemci kullanıyorsanız ClientSecret alanını boş bırakın — akışı PKCE korur.
Kimlik doğrulama (QR)#
client, _ := gti.New(gti.Config{APIKey: "sk_live_..."})
session, err := client.CreateSession(ctx, []string{
"identity.first_name", "identity.national_id",
}, "")
fmt.Fprintf(w, `<img src="%s">`, client.QRURL(session.SessionID))
// ...kullanıcı onayladıktan sonra
result, _ := client.FetchSession(ctx, session.SessionID)
if result.Approved() && result.IdentityVerified() {
ad := result.Identity["identity.first_name"]
}IdentityVerified() yalnızca çipten okunabilen alanlara bakar: ad ve e-posta kullanıcının kendi yazdığı profilden de gelebilir, kimlik numarası gelemez.
Webhook#
body, _ := io.ReadAll(io.LimitReader(r.Body, 1<<20))
if err := client.VerifyWebhookRequest(r, body, gti.DefaultWebhookTolerance); err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}Ham gövdeyi doğrulayın: JSON'a çevirip yeniden kodlarsanız boşluk ve anahtar sırası değişir, imza tutmaz. Zaman damgası imzanın içinde olduğu için eski bir teslimatın yeniden gönderilmesi de tutmaz.
Hata yönetimi#
switch {
case errors.Is(err, gti.ErrDenied): // kullanıcı vazgeçti — hata değil
case errors.Is(err, gti.ErrStateMismatch): // CSRF şüphesi, akışı durdurun
case errors.Is(err, gti.ErrConfig): // eksik yapılandırma
case errors.Is(err, gti.ErrTransport): // ağ — yeniden denemek mantıklı
}ErrTransport ile API hatası bilerek ayrı: birincisi bizim ulaşamamamız, ikincisi sunucunun "hayır" demesi ve yeniden denemek aynı cevabı verir.
Dikkat: iki scope henüz istenmemeli#
address ve identity.passport_number keşif belgesinde görünüyor ama mobil uygulama bugün ikisini de dolduramıyor. Onay için istenen alanların tamamının dolu olması gerektiğinden bunları istemek oturumu onaylanamaz hâle getirir.
Örnekler#
Çalışan üç örnek depoda: examples/login, examples/verify, examples/webhook.
GTI_CLIENT_ID=gtid_... go run ./examples/login