
# JavaScript tarayıcı SDK'sı

`@keenauth/js`, KeenAuth'un tarayıcı çekirdeğidir. Kullanıcıları tek sayfalık uygulamanıza giriş yaptırır, belirteçlerini saklar ve API'nizi çağırır. Çalışma zamanı bağımlılığı yoktur ve React bileşenleri bunun üzerine kuruludur. Paket henüz npm'de yayımlanmamıştır.

## Yönlendirme ile giriş yap

SDK, standart bir OpenID Connect genel istemcisidir:

- PKCE (S256) kullanır ve DPoP'a bağlı belirteçler kullanır.
- Geri çağırmada `state`, `nonce` ve `iss` değerlerini kontrol eder.
- ID belirtecinin imzasını ve taleplerini ortamınızın anahtarlarına karşı doğrular.

```ts
import { KeenAuth } from "@keenauth/js";

const auth = new KeenAuth({
  issuer: "https://acme.eu.keenauth.app", // your environment's issuer, exactly
  clientId: "app_01j9zzzzzzzzzzzzzzzzzzzz",
  redirectUri: "https://app.acme.example/callback",
  postLogoutRedirectUri: "https://app.acme.example/",
});

// On your sign-in button:
await auth.signInRedirect({ appState: "/orders" });

// On https://app.acme.example/callback:
const { appState } = await auth.handleRedirectCallback();
```

`handleRedirectCallback` bir yanıtı yalnızca bir kez kabul eder. Şu durumlarda `KeenAuthError` ile başarısız olur:

- `state` veya `iss` eşleşmez;
- bekleyen giriş 10 dakikadan eskidir;
- ID belirteci geçerli değildir.

Sunucunun geri gönderdiği bir hata, OAuth `error` kodunu taşıyan bir `KeenAuthOAuthError` olarak yükseltilir.

## API'nizi çağırın

API'niz için bir erişim belirteci almak üzere, API tanımlayıcısını `resource` olarak geçin (RFC 8707). Bu değer yetkilendirme isteğinde, kod değişiminde ve her yenilemede gönderilir; belirtecin `aud` değeri bu tanımlayıcıdır:

```ts
const auth = new KeenAuth({
  issuer: "https://acme.eu.keenauth.app",
  clientId: "app_01j9zzzzzzzzzzzzzzzzzzzz",
  redirectUri: "https://app.acme.example/callback",
  resource: "https://api.acme.example", // your API's identifier
  scope: "openid profile email offline_access messages:read",
});
```

- Değer, parça (`#`) içermeyen mutlak bir URI olmalıdır. Olduğu gibi gönderilir; bu nedenle API tanımlayıcısını kayıtlı haliyle kullanın. Geçersiz bir değer, yapıcıdan `config_invalid` koduyla bir `KeenAuthError` fırlatır.
- `signInRedirect({ resource })` tek bir giriş için bunu geçersiz kılar.
- `resource` olmadan, uygulama için bir varsayılan API ayarlanmışsa sunucu onu kullanır; aksi halde hiçbir API kapsamı verilmez.
- Seçenek bir dizi de kabul eder; RFC 8707'nin izin verdiği şekilde tekrarlanan `resource` parametreleri olarak gönderilir. KeenAuth erişim belirtecinin tam olarak bir hedef kitlesi (audience) vardır; bu nedenle sunucu birden fazla değeri `invalid_target` ile reddeder (bu bir `KeenAuthOAuthError`'dır).

```ts
const res = await auth.authorizedFetch("https://api.acme.example/v1/orders");

// Or take the token yourself; it is refreshed when it is about to expire.
const token = await auth.getAccessToken();
```

`authorizedFetch`, belirtece bağlı taze bir kanıtla `Authorization: DPoP` gönderir. Sunucu bir DPoP nonce isterse, o nonce ile bir kez yeniden dener. Eşzamanlı çağrılar tek bir yenileme isteğini paylaşır ve her yenileme yenileme belirtecini döndürür. Bu belirteçleri API'nizde doğrulamak için bkz. [API'nizde belirteçleri doğrulama](/docs/sdk/token-verification).

## Çıkış yap

```ts
await auth.signOut();
```

`signOut` belirteçleri siler ve yenileme belirtecini iptal eder. Ardından tarayıcıyı ortamınızın çıkış uç noktasına yönlendirir; bu uç nokta da onu `postLogoutRedirectUri` adresine geri döndürür.

## Belirteçlerin tutulduğu yer

- Erişim, yenileme ve ID belirteçleri yalnızca bellekte tutulur; localStorage, sessionStorage, IndexedDB, çerezler veya URL'de hiçbir zaman tutulmaz. Sayfa yenilendikten sonra kullanıcı yönlendirme ile yeniden giriş yapar; SDK oturumu geri yüklemez.
- sessionStorage yalnızca devam eden bir girişin tek kullanımlık değerlerini tutar.
- DPoP anahtarı, varsayılan olarak bellekte tutulan dışa aktarılamayan bir WebCrypto anahtarıdır.

## Gömülü giriş

Aynı nesne, gömülü bileşenlere bir ön yüz API istemcisi de sağlar:

- giriş adımları için `transactions`;
- hesabın profili ve `user_metadata`, e-postalar (kod doğrulamalı), passkey'ler ve oturumlar için `me`;
- passkey işlemleri için `webauthn`.

Bu çağrılar ortamınızın çerezlerini ve bir CSRF üstbilgisini kullanır. Sunucu bunları yalnızca ortamın kendi giriş kaynağından kabul eder.

## Yönlendirme olmadan giriş yap

Giriş kaynağınızın dışındaki bir sayfada, işlemi `clientId`'nizle başlatın. SDK bunu çerezler yerine kendi DPoP anahtarına bağlar:

```ts
let tx = await auth.transactions.create({ client_id: "app_01j9...", organization: "acme" });
tx = await auth.transactions.step("identifier", { identifier: "ada@acme.example" });
tx = await auth.transactions.step("password", { password });
if (tx.state === "completed") await auth.complete(tx); // { kind: "signed_in" }
```

- Uygulama birinci taraf bir uygulama olmalı ve sayfa kaynağı, izin verilen web kaynaklarından biri olmalıdır.
- Her çağrı bir DPoP kanıtı taşır ve çerez taşımaz. Sunucu bir DPoP nonce isterse SDK o nonce ile bir kez yeniden dener.
- SDK PKCE doğrulayıcısını, `state` ve `nonce` değerlerini kendisi oluşturur ve bellekte tutar. `scope` ve `resource` seçeneklerden gelir; `create`'e `organization` ve `uiLocales` geçebilirsiniz. Burada tek bir `resource` değeri kullanın.
- `complete` yanıtı kontrol eder, yetkilendirme kodunu aynı DPoP anahtarıyla değiştirir ve belirteçleri bellekte tutar. Sayfa yönlendirilmez ve kod hiçbir zaman kodunuza döndürülmez.
- Her `step` çağrısı, `client_id` ile veya onsuz, tarayıcının saat dilimini `Idp-Zoneinfo` üstbilgisinde gönderir. Adım girişi tamamladığında ve kullanıcı bir saat dilimi seçmediyse KeenAuth bunu, barındırılan giriş sayfasının yaptığı gibi profilin `zoneinfo` alanına kaydeder. Tarayıcı kullanılabilir bir bölge bildirmezse üstbilgi eklenmez ve adım değişmez.
- Giriş, kullanıcının görmesi gereken bir sayfaya ihtiyaç duyarsa (onay, yasal şartlar, kuruluş seçimi), son adım `authorization_failed` kodlu bir `KeenAuthProblemError` ile başarısız olur. Bu kullanıcı için `signInRedirect` kullanın.

## Kendi kaynağınızdan hesap çağrıları

Kullanıcı giriş yaptıktan ve SDK bir yenileme belirteci tuttuktan sonra (varsayılan kapsam `offline_access` ister), `me` ve `organizations` çağrıları artık giriş sunucusunun oturum çerezine ihtiyaç duymaz:

- SDK, `resource=<issuer>/fapi` ile yenileme yapar ve ön yüz API'si için kısa ömürlü bir erişim belirteci alır. Bu belirteç DPoP'a bağlıdır ve diğer belirteçler gibi yalnızca bellekte tutulur.
- Her hesap çağrısı `Authorization: DPoP <token>` ve o çağrıya ait bir DPoP kanıtı gönderir; çerez göndermez. Sunucu bir DPoP nonce isterse SDK o nonce ile bir kez yeniden dener; belirteç artık geçerli değilse yeni bir tane alır ve bir kez daha dener.
- Yenileme belirteci her yenilemede döner; bu yüzden SDK yenilemeleri birbiri ardına çalıştırır.
- Bu yalnızca, izin verilen web kaynakları arasında sayfanızın kaynağı bulunan birinci taraf bir uygulama için çalışır.
- Belirteç yoksa SDK, daha önce olduğu gibi oturum çerezini kullanır.

Sunucu kuralları için bkz. [Ön yüz API erişim belirteci](/docs/api/fapi/front-api-token).

## Kuruluşu değiştirme

`organizations.switch(orgId)`, giriş yapmış kullanıcıyı sayfa yenilemeden başka bir kuruluşa taşır. `organizations.listMine()` çıktısından bir kuruluşun `org_…` kimliğini geçin:

```js
const { data } = await auth.organizations.listMine();
await auth.organizations.switch(data[1].id);
```

- Yukarıdaki hesap çağrılarıyla aynı gereksinimlere ihtiyaç duyar: bir yenileme belirteci, `redirectUri` ve sayfanızın kaynağının izin verilen web kaynakları arasında bulunduğu birinci taraf bir uygulama.
- SDK, ön yüz API belirteciyle bir [geçiş isteği](/docs/api/fapi/transactions/create) gönderir, kodu aynı anahtarın kanıtıyla değiştirir ve `org_id` değeri kuruluş olan yeni belirteçleri tutar. Sahip olduğunuz belirteçler, bunların tümü başarılı olana kadar kalır.
- Kuruluş, kullanıcının görmesi gereken bir sayfa (MFA, SSO, onay) gerektiriyorsa çağrı `authorization_failed` kodlu bir `KeenAuthProblemError` ile başarısız olur. Bu kullanıcı için `signInRedirect({ organization })` kullanın.

## Bugünkü sınırlar

- Profil yazmaları (`me.update`, `me.updateMetadata`), son profil okuma veya yazmanın `ETag` değeriyle `If-Match` gönderir. Profil bu arada başka bir yerde değiştiyse, yazma `precondition_failed` kodlu bir `KeenAuthProblemError` ile başarısız olur; yeniden denemeden önce `me.get()` çağırın.
- Telefon numaraları henüz kullanılamıyor: `me.phones` değeri `undefined`'dır.
