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

1. SDK'yı yükleyin

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)
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.

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.

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

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:

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.

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);
});
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.revokeddeliveries: a valid one revokes the token, a forged, tampered or stale one gets400 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 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.

Sonraki adımlar