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ı 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ı 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

1. Kurun

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:

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

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:

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.

        <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:

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:

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:

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:

      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

      <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:

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.

  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");
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