
# Go API'sini koruyun

Bu hızlı başlangıç, bir Go `net/http` API'sini KeenAuth erişim belirteçleriyle korur. Go SDK'nın `protect` ara katmanını eklersiniz, bir rotada bir kapsam zorunlu kılarsınız ve API'yi SDK test kiti ile çevrimdışı test edersiniz. Ara katman yalnızca standart kütüphaneyi kullanır ve bir `http.Handler` kabul eden her yönlendiriciyle çalışır.

API'nin üç uç noktası vardır:

| Uç nokta | Gerektirir |
|---|---|
| `GET /api/public` | hiçbir şey |
| `GET /api/private` | API'niz için geçerli bir erişim belirteci |
| `GET /api/messages` | `messages:read` kapsamına sahip geçerli bir erişim belirteci |

## Başlamadan önce

- Bir KeenAuth ortamı. Veren adresi `https://your-environment.eu.keenauth.app` biçimindedir.
- Ortamda kayıtlı bir API ([API oluşturun](/docs/api/management/apis/create)). Onun `identifier` değeri, örneğin `https://api.example.com`, API'nizin kabul ettiği belirteçlerin hedef kitlesidir.
- Bu API üzerinde, onu çağıran uygulamaya verilmiş `messages:read` kapsamı.
- Go 1.26 veya üzeri.

## 1. SDK'yı yükleyin

```sh
go get github.com/keenauth/keenauth-go
```

SDK henüz yayımlanmadı. Yayımlanana kadar örnek, `go.mod` dosyasında bir `replace` yönergesiyle SDK kaynağına karşı derlenir.

## 2. Yapılandırın

API yapılandırmasını yalnızca ortam değişkenlerinden okur:

| Değişken | Zorunlu | Anlamı |
|---|---|---|
| `KEENAUTH_ISSUER` | evet | Ortamınızın veren adresi |
| `KEENAUTH_AUDIENCE` | evet | API'nizin tanımlayıcısı |
| `PUBLIC_BASE_URL` | evet | İstemcilerin çağırdığı URL (bkz. [Proxy arkasında](#proxy-arkasında)) |
| `KEENAUTH_WEBHOOK_SECRET` | önerilir | `token.revoked` gönderen webhook uç noktasının `idp_whsec_` imza sırrı (döndürme sırasında virgülle ayrılmış iki değer). Bu olmadan API, iptal edilmiş belirteçleri asla öğrenmez |
| `ADDR` | hayır | Dinleme adresi, varsayılan `:8080` |

## 3. Rotalarınızı koruyun

`NewHandler` ara katmanı ve rotaları oluşturur. `m.Handler` geçerli bir belirteç ister; `m.Require` ayrıca kapsam da ister. İşleyici, doğrulanmış talepleri `protect.ClaimsFrom` ile okur.

```go
// NewHandler builds the API. The verifier is a parameter so that tests can pass the offline test kit's verifier;
// main passes a real one. The revocation cache is fed from the token.revoked webhook.
func NewHandler(v protect.AccessTokenVerifier, publicBaseURL string, revocation protect.RevocationChecker) (http.Handler, error) {
	m, err := protect.New(v, protect.Options{
		PublicBaseURL: publicBaseURL,
		Revocation:    revocation,
		OnError: func(r *http.Request, err error) {
			// The error never contains the token or claim values; never log the Authorization or DPoP header.
			log.Printf("rejected %s %s: %v", r.Method, r.URL.Path, err)
		},
	})
	if err != nil {
		return nil, err
	}

	mux := http.NewServeMux()
	// No token needed.
	mux.HandleFunc("GET /api/public", func(w http.ResponseWriter, _ *http.Request) {
		writeJSON(w, map[string]string{"message": "Hello from a public endpoint."})
	})
	// Any valid access token for this API.
	mux.Handle("GET /api/private", m.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		claims, _ := protect.ClaimsFrom(r.Context())
		writeJSON(w, map[string]string{"message": "Hello from a private endpoint.", "sub": claims.Subject})
	})))
	// A valid access token that also carries the messages:read scope.
	mux.Handle("GET /api/messages", m.Require(protect.Requirement{Scopes: []string{"messages:read"}})(
		http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
			writeJSON(w, map[string]any{"messages": []string{"Hello from a scoped endpoint."}})
		})))
	return mux, nil
}
```

`main` yapılandırmadan gerçek doğrulayıcıyı oluşturur. Doğrulayıcı, bir belirteç gördüğü ilk seferde imza anahtarlarını ortamınızdan getirir.

```go
func main() {
	cfg, err := ConfigFromEnv(os.Getenv)
	if err != nil {
		log.Fatal(err)
	}
	// verify.New refuses an issuer under the reserved .invalid domain, so a test token can never pass here.
	v, err := verify.New(verify.Config{Issuer: cfg.Issuer, Audience: cfg.Audience})
	if err != nil {
		log.Fatal(err)
	}
	revocation := protect.NewMemoryRevocationCache(0, nil)
	api, err := NewHandler(v, cfg.PublicBaseURL, revocation)
	if err != nil {
		log.Fatal(err)
	}
	h := api
	if len(cfg.WebhookSecrets) > 0 {
		wv, err := webhook.NewVerifier(cfg.WebhookSecrets) // the error never contains the secret
		if err != nil {
			log.Fatalf("KEENAUTH_WEBHOOK_SECRET: %v", err)
		}
		h = WithWebhook(api, NewWebhookHandler(wv, revocation, nil))
	} else {
		log.Print("KEENAUTH_WEBHOOK_SECRET is not set: token.revoked events are not received, so revoked tokens stay valid until they expire")
	}
	srv := &http.Server{Addr: cfg.Addr, Handler: h, ReadHeaderTimeout: 10 * time.Second}
	log.Printf("listening on %s", cfg.Addr)
	log.Fatal(srv.ListenAndServe())
}
```

## 4. API'yi çalıştırın ve çağırın

```sh
export KEENAUTH_ISSUER=https://your-environment.eu.keenauth.app
export KEENAUTH_AUDIENCE=https://api.example.com
export PUBLIC_BASE_URL=https://api.example.com
go run .
```

İlk çağrı için, ortamınızdaki API üzerinde `messages:read` kapsamına sahip bir makineden makineye uygulamanın istemci kimlik bilgileri akışıyla bir belirteç alın:

```sh
ACCESS_TOKEN=$(curl -s -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=client_credentials \
  -d resource=https://api.example.com \
  -d scope=messages:read \
  https://your-environment.eu.keenauth.app/oauth2/token | jq -r .access_token)

curl -i http://localhost:8080/api/messages -H "Authorization: Bearer $ACCESS_TOKEN"
```

## 5. Ağ olmadan test edin

Test kiti paketi `keenauthtest` genel test anahtarıyla imzalanmış belirteçler üretir ve yalnızca bu belirteçleri kabul eden bir doğrulayıcı oluşturur. Testler bu doğrulayıcıyı `NewHandler` işlevine iletir; bu yüzden ağ bağlantısına ve KeenAuth hesabına ihtiyaç duymazlar. Bkz. [Çevrimdışı test kiti](/docs/sdk/testing).

```go
func newTestAPI(t *testing.T) (http.Handler, *protect.MemoryRevocationCache) {
	t.Helper()
	v, err := keenauthtest.NewVerifier(verify.Config{Audience: audience})
	if err != nil {
		t.Fatal(err)
	}
	cache := protect.NewMemoryRevocationCache(0, nil)
	h, err := NewHandler(v, baseURL, cache)
	if err != nil {
		t.Fatal(err)
	}
	return h, cache
}
```

```sh
go test ./...
```

Örneğin testleri, geçerli bir belirtecin kabul edildiğini; eksik, süresi dolmuş veya yanlış hedef kitleli belirtecin, eksik kapsamın, URL sorgusundaki bir belirtecin ve iptal edilmiş bir belirtecin aşağıdaki durum kodlarıyla reddedildiğini denetler. Ayrıca `PUBLIC_BASE_URL` için bir kanıtla birlikte DPoP'a bağlı bir belirteç gönderirler. Testler ayrıca imzalı `token.revoked` teslimatları gönderir: geçerli bir teslimat belirteci iptal eder; sahte, değiştirilmiş veya eski bir teslimat `400` yanıtı alır ve hiçbir şeyi iptal etmez; tekrarlanan bir teslimatın etkisi tek olur.

## Yanıtlar

| İstek | Durum | Gövdede `code` |
|---|---|---|
| `Authorization` başlığı olmayan | `401` | `missing_token` |
| Süresi dolmuş belirteç, başka bir API için belirteç, hatalı imza, iptal edilmiş belirteç | `401` | `invalid_token` |
| Gerekli kapsamı olmayan belirteç | `403` | `insufficient_scope` |
| URL sorgusundaki belirteç (`?access_token=`) | `400` | `invalid_request` |

Gövde `application/problem+json` ve `WWW-Authenticate` başlığı standart meydan okumayı taşır. Tam tablo [ara katmanla API'nizi koruma](/docs/sdk/protect-api#yanıtlar) sayfasındadır.

## Proxy arkasında

İstemciler DPoP kullanıyorsa, ara katman her kanıtın istemcinin çağırdığı URL için yapıldığını denetler. `PUBLIC_BASE_URL` değerini, istemcilerin çağırdığı dış URL'ye ayarlayın, örneğin `https://api.example.com`; proxy veya yük dengeleyici arkasında da böyledir. Zorunludur ve `https` kullanmalıdır (`http` yalnızca `localhost`, `127.0.0.1` veya `[::1]` için); bu olmadan uygulama başlamayı reddeder. Ara katman `X-Forwarded-*` başlıklarına asla güvenmez.

## İptal edilmiş belirteçler

Erişim belirteci süresi dolana kadar geçerli kalır; bu yüzden KeenAuth, belirteçler iptal edildiğinde API'nize bildirir. Örnek, ara katmana bellek içi bir iptal önbelleği verir ve bunu besleyen bir `token.revoked` webhook'unu `POST /webhooks/keenauth` adresinde (`main.go, webhook.go`) sunar.

1. Panelde, `https://<your host>/webhooks/keenauth` URL'sine sahip ve `token.revoked` olaylarına abone olan bir webhook uç noktası oluşturun ve imza sırrını `KEENAUTH_WEBHOOK_SECRET` olarak dışa aktarın.
2. İşleyici, imzayı ham gövde üzerinde SDK ile doğrular ve geçersizse ya da zaman damgası beş dakikadan fazla sapmışsa `400` yanıtı verir. Olayın seçicisini (`access_token`, `session`, `user`, `client`, `org`, `org_user`, `client_user`) bir iptal işaretçisine eşler ve `204` yanıtı verir; ara katman bundan sonra `not_before` öncesinde verilmiş eşleşen tüm belirteçleri reddeder.
3. KeenAuth en az bir kez teslim eder, bu yüzden işleyici her `webhook-id` değerini bir kez uygular. İşaretçi kaydedilemezse `503` yanıtı verir ve KeenAuth olayı yeniden teslim eder. Diğer olay türlerini ve hiçbir erişim belirtecinin eşleşemeyeceği seçicileri (`refresh_family`, `api`) yok sayar.

İşleyici olay kimliğini, seçici türünü ve nedeni kaydeder; gövdeyi, imzayı veya sırrı asla kaydetmez. Her süreç kendi önbelleğine sahiptir; birden fazla örnekle çalışırken işaretçileri, aynı `Revoke` yöntemi arkasında paylaşılan bir depoya koyun. Bkz. [İptal önbelleği](/docs/sdk/protect-api#iptal-önbelleği-tokenrevoked).

## Sonraki adımlar

- [Ara katmanla API'nizi koruma](/docs/sdk/protect-api): adım yukarı gereksinimleri, DPoP ve her seçenek.
- [Belirteçleri doğrulama](/docs/sdk/token-verification): ara katmanın altındaki doğrulayıcı.
- [Çevrimdışı test kiti](/docs/sdk/testing).
