@sinequa/atomic

Session lifecycle

Knowing whether you are signed in, signing out cleanly, and reacting to both through typed events.

Three questions, three different answers — and picking the wrong one is how an application either asks for credentials it already has, or navigates away when it meant to check.

Prop

Type

Use isAuthenticated() to render, hasSession() to decide, login() to act.

Observing the session

Five typed events cover the lifecycle. Subscribe once and the whole application can react:

EventFires whenPayload
authenticatedA sign-in completes — or fails{ authenticated: boolean }
loggedOutlogout() finished its local cleanup, before the network call{}
tokenRefreshedA token is stored, including the sliding refresh{ token }
unauthorizedA request came back 401, just before it throws{ endpoint, error }
requestFailedAny request failed{ endpoint, status, error }
A session state the whole application can read
src/session-state.ts
import { createAtomicClient } from "@sinequa/atomic";

const client = createAtomicClient({
  app: "<your-app>",
  backendUrl: "https://<your-sinequa-server>",
});

const stops = [
  // Fires on success AND failure — read the payload, do not assume.
  client.auth.on("authenticated", ({ authenticated }) => {
    document.body.dataset.signedIn = String(authenticated);
  }),

  // Also fires on the sliding refresh, so it is chatty by design.
  client.auth.on("tokenRefreshed", () => {
    console.debug("session extended");
  }),

  client.auth.on("loggedOut", () => {
    window.location.assign("/");
  }),
];

await client.initializeConfig();
await client.auth.login();

// Render from the local check; it is synchronous.
if (client.auth.isAuthenticated()) {
  const principal = await client.api.principal.get();
  console.log("signed in as", principal.name);
}

// On teardown, so a reload does not stack handlers.
export function dispose() {
  for (const stop of stops) stop();
}

The free-function equivalent is onAuthEvent(name, handler), which subscribes on the default client. Both return the unsubscribe function.

A DOM event is still emitted, for compatibility

An authenticated CustomEvent is dispatched on document — bubbling to window — alongside the typed channel. Prefer on(): it is typed, and it works with no DOM at all, which the DOM event obviously cannot do in Node, in a worker or under SSR.

Signing out

logout() does more than clear a token, and the order matters:

logout() clear the stored token,emit authenticated: false forget the credentials marker,clear the redirect guard mark this client signed outin memory emit loggedOut delete the session cookieunless impersonating return the server's logoutUrl,if it gave one

The returned URL is how a provider-based deployment finishes signing out at the identity provider. It is undefined when the server did not supply one.

logout() versus clearSession()

They are not interchangeable, and the difference is deliberate:

logout()clearSession()
Clears the stored tokenyesyes
Deletes the session cookieyes (a request)no
Forgets the credentials markeryesno
Clears the provider redirect guardyesno
Marks the client signed outyesno

clearSession() also runs as a pre-flight reset immediately before every login(). That is why it must not touch the last two: clearing the redirect guard there would defeat the loop protection, and forgetting the marker would make an ambiguous mode unresolvable.

Pitfalls

What's next

On this page