
# Express API'sini koruyun

Bu hızlı başlangıç, bir Express 5 API'sini KeenAuth erişim belirteçleriyle korur. Node.js SDK'sının `expressAuth` ara katmanını eklersiniz, bir rotada bir kapsam zorunlu kılarsınız ve API'yi SDK test kiti ile çevrimdışı test edersiniz. Express, SDK'nın bağımlılığı değildir; ara katman Express 4 ile de çalışır.

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

| Uç nokta | Gereksinim |
|---|---|
| `GET /api/public` | yok |
| `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ı. Yayıncısı `https://your-environment.eu.keenauth.app` gibi görünür.
- Ortamda kayıtlı bir API ([API oluşturun](/docs/api/management/apis/create)). Bunun `identifier` (örneğin `https://api.example.com`), API'nizin kabul ettiği belirteçlerin hedef kitlesidir.
- Bu API üzerinde `messages:read` kapsamı, onu çağıran uygulamaya verilmiş olmalıdır.
- Node.js 22 veya daha yeni bir sürüm.

## 1. SDK'yı yükleyin

```sh
npm install @keenauth/node express@5.2.1
```

SDK henüz yayımlanmadı. Yayımlanana kadar örnek, SDK kaynağını `package.json` içinde bağlantılar.

## 2. Yapılandırın

API yapılandırmasını yalnızca ortam değişkenlerinden okur. Bunları bir `.env` dosyasına koyun; `npm start` bu dosyayı `node --env-file-if-exists=.env` ile yükler ve dosyayı asla commit etmeyin.

| Değişken | Gerekli | Anlamı |
|---|---|---|
| `KEENAUTH_ISSUER` | evet | Ortamınızın yayıncısı |
| `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 | `idp_whsec_` imza sırrı, `token.revoked` teslim eden webhook uç noktasının sırrıdır (belirteçleri döndürürken virgülle ayrılmış iki değer). Bu olmadan API, iptal edilmiş belirteçlerden haberdar olmaz |
| `PORT` | hayır | Dinleme portu, varsayılan `8080` |

## 3. Rotalarınızı koruyun

`createApp` koruyucuyu ve rotaları oluşturur. `expressAuth(protector)` geçerli bir belirteç gerektirir; `{ scopes }` ile birlikte kapsamları da gerektirir. İşleyici, doğrulanmış talepleri `req.auth` üzerinden okur.

```js
export function createApp({ verifier, publicBaseUrl, revocation, webhookSecrets = [] }) {
  const protector = createProtector(verifier, {
    publicBaseUrl,
    revocation,
    // The error never contains the token or claim values; never log the Authorization or DPoP header.
    onError: (err) => console.warn(`rejected: ${err.message}`),
  });

  const app = express();
  app.disable("x-powered-by");

  // No token needed.
  app.get("/api/public", (req, res) => {
    res.json({ message: "Hello from a public endpoint." });
  });

  // Any valid access token for this API.
  app.get("/api/private", expressAuth(protector), (req, res) => {
    res.json({ message: "Hello from a private endpoint.", sub: req.auth.subject });
  });

  // A valid access token that also carries the messages:read scope.
  app.get("/api/messages", expressAuth(protector, { scopes: ["messages:read"] }), (req, res) => {
    res.json({ messages: ["Hello from a scoped endpoint."] });
  });

  // token.revoked deliveries from KeenAuth; the handler reads the raw body itself, so no JSON parser runs before it.
  if (webhookSecrets.length > 0) {
    app.post(WEBHOOK_PATH, ...createWebhookHandler({ secrets: webhookSecrets, revocation }));
  } else {
    console.warn("KEENAUTH_WEBHOOK_SECRET is not set: token.revoked events are not received, so revoked tokens stay valid until they expire");
  }

  return app;
}
```

`server.js` yapılandırmadan gerçek doğrulayıcıyı oluşturur. Doğrulayıcı, bir belirteci ilk gördüğünde imza anahtarlarını ortamınızdan alır.

```js
const issuer = process.env.KEENAUTH_ISSUER;
const audience = process.env.KEENAUTH_AUDIENCE;
// The URL clients call: DPoP proofs are checked against it, never against the Host header.
const publicBaseUrl = process.env.PUBLIC_BASE_URL;
if (!issuer || !audience || !publicBaseUrl) {
  console.error("KEENAUTH_ISSUER, KEENAUTH_AUDIENCE and PUBLIC_BASE_URL are required");
  process.exit(1);
}

// createVerifier refuses an issuer under the reserved .invalid domain, so a test token can never pass here.
const verifier = createVerifier({ issuer, audience });
const revocation = createMemoryRevocationCache();
// The signing secret (idp_whsec_...) of the webhook endpoint that delivers token.revoked; comma separated while rotating.
const webhookSecrets = (process.env.KEENAUTH_WEBHOOK_SECRET ?? "").split(",").map((s) => s.trim()).filter(Boolean);
const app = createApp({ verifier, publicBaseUrl, revocation, webhookSecrets });

const port = Number(process.env.PORT || 8080);
app.listen(port, () => console.log(`listening on port ${port}`));
```

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

```sh
npm start
```

İlk çağrı için, API'nizde `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

`@keenauth/node/testing`, herkese açık bir test anahtarıyla imzalanmış belirteçler üretir ve `createTestVerifier` yalnızca bu belirteçleri kabul eder. Testler bu doğrulayıcıyı `createApp` aktarır ve uygulamayı yerel bir bağlantı noktasında çağırır; bu nedenle ağ ya da KeenAuth hesabı gerekmez. Bkz. [Çevrimdışı test kiti](/docs/sdk/testing).

```js
test("scope check: 403 insufficient_scope without messages:read, 200 with it", async () => {
  await expectProblem(await get("/api/messages", bearer(mintAccessToken(AUDIENCE))), 403, "insufficient_scope", /scope="messages:read"/);
  const ok = await get("/api/messages", bearer(mintAccessToken(AUDIENCE, { claims: { scope: "profile messages:read" } })));
  assert.equal(ok.status, 200);
});
```

```sh
npm test
```

Örneğin testleri, geçerli bir belirtecin kabul edildiğini; eksik, süresi dolmuş ya da yanlış hedef kitleli belirteçlerin, eksik kapsamın, URL sorgusundaki belirtecin ve iptal edilmiş belirtecin aşağıdaki durum kodlarıyla reddedildiğini denetler. Ayrıca `PUBLIC_BASE_URL` için bir kanıtla DPoP'a bağlı bir belirteç gönderirler ve `server. The tests also send signed `token.revoked` deliveries: a valid one revokes the token, a forged, tampered or stale one gets `400` and revokes nothing, and a repeated one has one effect.js` test yayıncısıyla başlatılmayı reddettiğini kontrol ederler.

## Yanıtlar

| İstek | Durum | `code` gövdesinde |
|---|---|---|
| `Authorization` başlığı yok | `401` | `missing_token` |
| Süresi dolmuş belirteç, başka bir API için belirteç, geçersiz 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` biçimindedir 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, örneğin `https://api.example.com`, istemcilerin çağırdığı harici URL'ye ayarlayın; proxy veya yük dengeleyici arkasında da böyle yapın. Bu 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, Express'in `trust proxy` ayarı ne olursa olsun `X-Forwarded-*` başlıklarına asla güvenmez.

## İptal edilmiş belirteçler

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

1. Kontrol panelinde, `https://<your host>/webhooks/keenauth` URL'si ile bir webhook uç noktası oluşturun, `token.revoked` olaylarına abone olun 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çersiz olduğunda ya da zaman damgası beş dakikadan fazla saptığında `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` tarihinden önce verilmiş eşleşen her belirteci reddeder.
3. KeenAuth en az bir kez teslim eder; bu nedenle işleyici her `webhook-id` olayını yalnızca bir kez uygular. İşaretçi saklanamazsa `503` yanıtı verir ve KeenAuth olayı yeniden teslim eder. Diğer olay türlerini ve hiçbir erişim belirteciyle eşleşemeyecek seçicileri (`refresh_family`, `api`) yok sayar.

İşleyici; olay kimliğini, seçici türünü ve nedeni kaydeder, gövdeyi, imzayı ya da sırrı asla kaydetmez. Her süreç kendi önbelleğine sahiptir; birden çok örnekte, 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): Fastify, adım yükseltme gereksinimleri, DPoP ve tüm seçenekler.
- [Belirteç doğrulama](/docs/sdk/token-verification): ara katmanın altındaki doğrulayıcı.
- [Çevrimdışı test kiti](/docs/sdk/testing).
