
# Tek sayfalık bir React uygulamasına giriş ekleyin

Bu hızlı başlangıç, Vite ile oluşturulmuş bir React tek sayfalık uygulamasına `@keenauth/js` tarayıcı SDK'sını kullanarak KeenAuth girişi ekler. Uygulama, kullanıcıları yönlendirme ile giriş yaptırır, DPoP ile bağlanmış bir erişim belirteciyle korunan bir API'yi çağırır ve kullanıcıları çıkış yaptırır. Ayrıca bunu çevrimdışı test edersiniz. SDK, [JavaScript tarayıcı SDK'sı](/docs/sdk/js) sayfasında açıklanmıştır.

Tek sayfalık bir uygulamada belirteçler tarayıcıda bulunur. SDK bunları yalnızca bellekte tutar ve DPoP ile dışa aktarılamayan bir anahtara bağlar; böylece çalınan bir belirteç bu anahtar olmadan kullanılamaz. Uygulamanızın bir sunucusu varsa, [Next.js hızlı başlangıcı](/docs/quickstarts/nextjs) bunun yerine tüm belirteçleri sunucuda tutar (arka uç-ön uç, backend for frontend); bu daha güçlü bir seçenektir.

Uygulamanın şu yolları vardır:

| Yol | Ne yapar |
|---|---|
| `/` | Bir giriş düğmesi veya kullanıcı, API'nizi çağıran bir düğme ve bir çıkış düğmesi |
| `/callback` | Yönlendirme URI'si. SDK girişi burada tamamlar ve uygulama kullanıcının başladığı sayfaya geri döner |

## Başlamadan önce

- Bir KeenAuth ortamı. Ortamınızın issuer değeri şuna benzer: `https://your-environment.eu.keenauth.app`.
- Ortamda bir tek sayfalık uygulama ([Uygulama oluşturun](/docs/api/management/applications/create)); `app_type` değeri `spa` ve `token_endpoint_auth_method` değeri `none` olmalıdır (genel istemcinin sırrı yoktur). `development` ortamında şunları kaydedin:
  - yönlendirme URI'si `http://localhost/callback` (loopback yönlendirme URI'si port belirtilmeden kaydedilir ve herhangi bir portla eşleşir);
  - çıkış sonrası yönlendirme URI'si `http://localhost/`;
  - izin verilen web kökeni `http://localhost:5173`, böylece tarayıcı belirteç uç noktasını çağırabilir.
- Ortamda kayıtlı bir API ([API oluşturun](/docs/api/management/apis/create)) ve `messages:read` kapsamı. Örneğin `https://api.example.com` olan `identifier` değeri, erişim belirteçlerinin hedef kitlesidir (audience). [Express hızlı başlangıcı](/docs/quickstarts/express) bu belirteçleri kabul eden bir API oluşturur.
- Node.js 22.18 veya üzeri.

## 1. Kurun

```sh
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install @keenauth/js
```

SDK henüz yayımlanmadı. Yayımlanana kadar örnek, `package.json` dosyasındaki bağlantıyla SDK kaynağına işaret eder. Örnek `vite@8.3.4`, `@vitejs/plugin-react@6.1.2`, `react@19.3.1` ve `react-dom@19.3.1` sürümlerini sabitler.

## 2. Yapılandırın

Uygulama yapılandırmasını yalnızca ortam değişkenlerinden okur. Bunları Vite'ın yüklediği `.env.local` dosyasına koyun ve bu dosyayı asla commit etmeyin. Vite her `VITE_` değişkenini tarayıcının indirdiği JavaScript'e derler; bu yüzden oraya hiçbir gizli bilgi koymayın. Bu uygulamanın gizli bilgisi yoktur.

| Değişken | Zorunlu | Anlamı |
|---|---|---|
| `VITE_KEENAUTH_ISSUER` | evet | Ortamınızın issuer değeri |
| `VITE_KEENAUTH_CLIENT_ID` | evet | Tek sayfalık uygulamanın `client_id` değeri |
| `VITE_KEENAUTH_API_AUDIENCE` | evet | KeenAuth'taki API'nizin identifier değeri |
| `VITE_API_URL` | hayır | API'nizin temel URL'si. Boş bırakılırsa uygulamanın kendi kökeni kullanılır |
| `API_PROXY_TARGET` | hayır | Yalnızca geliştirme: geliştirme sunucusunun `/api` isteklerini yönlendirdiği adres; varsayılan `http://localhost:8080` |

`loadConfig` bu değerleri okur ve denetler. Bir değer eksikse, uygulamayı adını belirterek bir hatayla durdurur:

```ts
export function loadConfig(env: Readonly<Record<string, unknown>>): AppConfig {
  const need = (name: string): string => {
    const v = env[name];
    if (typeof v !== "string" || v.trim() === "") throw new Error(`${name} is required (see .env.example)`);
    return v.trim();
  };
```

## 3. SDK istemcisini oluşturun

```ts
export function createAuth(config: AppConfig, origin: string, inject: Injectables = {}): KeenAuth {
  return new KeenAuth({
    issuer: config.issuer,
    clientId: config.clientId,
    redirectUri: `${origin}${CALLBACK_PATH}`,
    postLogoutRedirectUri: `${origin}/`,
    // The access token is for this API: its aud is the API identifier (RFC 8707).
    resource: config.apiAudience,
    // messages:read is a scope of that API; the example API requires it on /api/messages.
    scope: "openid profile email offline_access messages:read",
    ...inject,
  });
}
```

`resource`, API'nizin identifier değeridir (RFC 8707). SDK bunu yetkilendirme isteğinde, kod değişiminde ve her yenilemede gönderir; erişim belirtecinin hedef kitlesi (aud) API'nizdir. Bu olmazsa erişim belirteci API'niz için düzenlenmemiş olur ve API bunu reddeder. SDK istemci oluşturulurken her seçeneği denetler; geçersiz bir issuer veya `resource`, `config_invalid` koduyla bir `KeenAuthError` fırlatır.

Sayfa için tek bir istemci oluşturun ve bunu bir sağlayıcıya verin:

```tsx
const config = loadConfig(import.meta.env);
const auth = createAuth(config, window.location.origin);

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <AuthProvider auth={auth}>
      <App apiUrl={config.apiUrl} />
    </AuthProvider>
  </StrictMode>,
);
```

## 4. Girişi başlatın ve geri çağrıyı işleyin

Giriş düğmesi yönlendirmeli girişi başlatır: PKCE (S256), `state` ve `nonce` ile yetkilendirme kodu akışı. `appState`, dönülecek yoldur.

```tsx
        <button onClick={() => auth.signInRedirect({ appState: window.location.pathname })}>Sign in</button>
```

KeenAuth sizi `/callback` adresine geri yönlendirdiğinde sağlayıcı girişi tamamlar. SDK `state`, `iss` ve ID belirtecini denetler ve kodu bir DPoP kanıtıyla değiştirir. Bir yetkilendirme yanıtı yalnızca bir kez kullanılabilir ve React'in `StrictMode`'u geliştirmede efektleri iki kez çalıştırdığı için `completeSignIn` ilk sonucu hatırlar:

```ts
export function completeSignIn(auth: KeenAuth, href: string): Promise<string | null> {
  const url = new URL(href);
  if (url.pathname !== CALLBACK_PATH || !(url.searchParams.has("code") || url.searchParams.has("error"))) {
    return Promise.resolve(null);
  }
  let done = completions.get(auth);
  if (!done) {
    done = auth.handleRedirectCallback(href).then((r) => safeReturnPath(r.appState));
    completions.set(auth, done);
  }
  return done;
}
```

Sağlayıcı ardından adres çubuğundan `code` ve `state` parametrelerini kaldırır. Yalnızca uygulamanızın içindeki bir yol dönüş yolu olarak kullanılır; böylece hazırlanmış bir `appState` kullanıcıyı başka bir siteye yönlendiremez:

```ts
export function safeReturnPath(path: string | undefined): string {
  return path !== undefined && /^\/(?![/\\])/.test(path) ? path : "/";
}
```

Üretimde de `/callback` için `index.html` sunun (Vite geliştirme sunucusu bunu zaten yapar).

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

`authorizedFetch`, belirtece bağlı yeni bir kanıtla `Authorization: DPoP <token>` başlığını gönderir ve belirteç süresi dolmak üzereyken onu yeniler:

```ts
export async function fetchMessages(auth: KeenAuth, apiUrl: string): Promise<unknown> {
  const res = await auth.authorizedFetch(`${apiUrl}/api/messages`, { headers: { Accept: "application/json" } });
  if (!res.ok) throw new Error(`The API answered ${res.status}`);
  return res.json();
}
```

Geliştirmede Vite geliştirme sunucusu `/api` isteklerini API'nize iletir; böylece tarayıcı API'yi uygulamanın kendi kökeninden çağırır ve API'nin CORS ayarına ihtiyaç duymaz:

```ts
      proxy: { "/api": env.API_PROXY_TARGET || "http://localhost:8080" },
```

API, DPoP kanıtını tarayıcının çağırdığı URL'ye göre denetler. Express hızlı başlangıcının API'sini bu proxy arkasında çalıştırdığınızda, `PUBLIC_BASE_URL` değerini `http://localhost:5173` olarak ayarlayın. Üretimde API'yi aynı kökenden sunun ya da uygulamanızın kökeni için API'de CORS'u etkinleştirin ve `VITE_API_URL` değerini ayarlayın.

## 6. Çıkış yapın

```tsx
      <button onClick={() => auth.signOut()}>Sign out</button>
```

`signOut` belirteçleri siler, yenileme belirtecini iptal eder ve tarayıcıyı ortamınızın çıkış uç noktasına gönderir; bu uç nokta tarayıcıyı `/` adresine geri döndürür.

## 7. Uygulamayı çalıştırın

Express hızlı başlangıcındaki API'yi `PUBLIC_BASE_URL=http://localhost:5173` ve `KEENAUTH_AUDIENCE` değeri API'nizin identifier değeri olacak şekilde başlatın, ardından uygulamayı çalıştırın:

```sh
cp .env.example .env.local   # then fill it in
npm run dev
```

`http://localhost:5173` adresini açın, **Sign in** seçeneğini seçin, KeenAuth'ta giriş yapın ve **Call the API** seçeneğini seçin.

SDK belirteçleri yalnızca bellekte tutar. Sayfa yeniden yüklendikten sonra kullanıcı uygulamada çıkış yapmış olur ve yeniden giriş yapar; KeenAuth oturumu genellikle bunu bir istem olmadan, yalnızca bir yönlendirmeye dönüştürür.

## 8. Ağ olmadan test edin

Testler ağ bağlantısı ve bir KeenAuth hesabı gerektirmez. Sahte bir `fetch`, yetkilendirme sunucusunu ve API'yi taklit eder; ID belirteci test için üretilmiş bir anahtarla imzalanır. Node, TypeScript testlerini doğrudan çalıştırır.

```ts
  assert.deepEqual(await fetchMessages(s.auth, ""), { messages: ["hello"] });
  const call = s.seen.find((c) => c.url === `${ORIGIN}/api/messages`)!;
  assert.equal(call.headers.get("Authorization"), "DPoP access-token-1");
```

```sh
npm test
npm run typecheck
npm run deps    # pinned versions, lockfile sources and licences
npm run build
```

Testler şunları denetler: yapılandırmanın doğrulandığını, SDK'nın geçersiz bir issuer veya `resource` değerini reddettiğini, girişin PKCE S256 ve API'nizi `resource` olarak gönderdiğini, geri çağrının bir kez tamamlandığını ve `resource` değerini kodla birlikte gönderdiğini, sahte bir `state` değerinin herhangi bir belirteç isteğinden önce reddedildiğini, girişten sonra yalnızca uygulama içindeki bir yolun kullanıldığını, hiçbir belirtecin depolamaya yazılmadığını ve API çağrısının DPoP ile bağlanmış belirteci ve bu belirtecin kanıtını taşıdığını.

Gerçek bir tarayıcıyla tam giriş için bir KeenAuth ortamı gerekir. Bu, çevrimdışı testlerin parçası değildir.

## Sonraki adımlar

- [JavaScript tarayıcı SDK'sı](/docs/sdk/js): her seçenek, belirteçlerin nerede tutulduğu ve mevcut sınırlar.
- [React SDK'sı](/docs/sdk/react): gömülü giriş, hesap oluşturma ve hesap bileşenleri.
- [Express API'sini koruyun](/docs/quickstarts/express): bu uygulamanın çağırdığı API.
