Zwykła przeglądarka
Jedna instancja klienta, jedna trasa powrotna i token dostępu do każdego żądania, które wysyła Twoja aplikacja.
Dla aplikacji jednostronicowej bez naszego modułu: Vite i czysty TypeScript,
React, Svelte, Solid albo framework, o którym nigdy nie słyszeliśmy.
@kleora-io/browser to klient authorization code z PKCE i nie wie nic o
Twoim frameworku.
npm install @kleora-io/browser
Bez zależności w czasie działania, wyłącznie moduły ES.
1. Utwórz klienta raz
Jedna instancja na całą aplikację, we własnym module. Druga instancja to najprostszy sposób, by dwie połowy tej samej strony miały dwa różne zdania o tym, kto jest zalogowany.
// src/kleora.ts
import { createKleora } from '@kleora-io/browser'
export const kleora = createKleora({
issuer: import.meta.env.VITE_KLEORA_ISSUER,
clientId: import.meta.env.VITE_KLEORA_CLIENT_ID,
redirectUri: location.origin + '/callback',
storage: 'session',
})
issuer, clientId i redirectUri są wymagane; reszta ma wartości domyślne.
scope to domyślnie openid profile email offline_access — zostaw w nim
offline_access, bo inaczej nie zostanie wydany token odświeżania i nic nie
odnowi się po cichu.
2. Obsłuż powrót
Issuer odsyła przeglądarkę na /callback z kodem autoryzacyjnym. Wymień go tam
i odstaw człowieka tam, skąd wyszedł:
import { kleora } from './kleora'
if (location.pathname === '/callback') {
const { appState } = await kleora.handleRedirectCallback()
const returnTo = (appState as { returnTo?: string })?.returnTo ?? '/'
history.replaceState(null, '', returnTo)
}
handleRedirectCallback() sprawdza state i iss, wymienia kod z użyciem
weryfikatora PKCE, weryfikuje ID token względem zestawu kluczy issuera,
zapisuje tokeny i usuwa dane logowania z paska adresu.
Twój serwer deweloperski musi oddawać HTML aplikacji także pod /callback, nie
tylko pod /. Serwer deweloperski Vite robi to dla nieznanych ścieżek sam;
zwykły serwer plików statycznych nie robi i odpowiada 404 na jedyną nawigację,
która ma tu znaczenie.
3. Zaloguj i wiedz, kiedy jesteś zalogowany
if (!(await kleora.checkSession())) {
await kleora.loginWithRedirect({ returnTo: location.pathname })
}
const user = kleora.getUser() // sub, email, name, tenant, tenant_slug…
checkSession() próbuje po kolei: świeżego tokenu z pamięci, zapisanego tokenu
odświeżania, a dopiero na końcu cichego prompt=none w ukrytej ramce.
returnTo przechodzi przez przekierowanie i wraca jako appState.returnTo —
czyli dokładnie to, co czyta krok 2.
Zamiast odpytywać stan, można na niego reagować:
const unsubscribe = kleora.onAuthChange(({ user, isAuthenticated }) => {
render(isAuthenticated ? user : null)
})
Jest jeszcze kleora.hasLikelySession(), które mówi, czy ta przeglądarka
dokończyła kiedyś logowanie, po którym nie było wylogowania. To podpowiedź do
renderowania — pozwala pokazać szkielet interfejsu zamiast mignięcia ekranem
logowania, kiedy weryfikacja jeszcze trwa. Nigdy nie jest decyzją autoryzacyjną
ani dowodem żywej sesji; dowodem jest tylko checkSession() albo udane
wywołanie API.
4. Wołaj własne API
const token = await kleora.getAccessToken()
await fetch('/api/invoices', {
headers: { Authorization: `Bearer ${token}` },
})
getAccessToken() oddaje token z pamięci, dopóki do wygaśnięcia zostało więcej
niż 30 sekund, a potem odświeża; równoległe wywołania dzielą jedno odświeżenie.
Tokeny odświeżania rotują przy każdym użyciu. Jeśli Twoje API odpowie 401 na
token, w który ten klient wciąż wierzy, ponów raz z
getAccessToken({ force: true }) — to wyrzuca token z pamięci, zamiast wysyłać
po raz drugi ten sam odrzucony.
Sprawdzanie tego tokenu opisuje strona o
Express — albo verify_access_token,
jeśli Twoje API jest w Pythonie.
5. Wyloguj
await kleora.logout({ returnTo: location.origin + '/' })
returnTo jest wymagane i musi być jednym z zarejestrowanych adresów po
wylogowaniu, porównywanym dokładnie. Stan lokalny czyszczony jest przed
jakimkolwiek pobraniem, więc wylogowanie kończy się lokalnie nawet wtedy, gdy
issuer jest nieosiągalny.