@sinequa/atomic

How it works

The cookie, the CSRF token, the sliding refresh — the four moving parts behind login(), and the two that leak into your code.

Read this once and every mode-specific page becomes shorter. Four server endpoints and one response header account for the entire authentication layer.

The pieces

EndpointWhat it does
api/v1/security.webtokenTrades credentials, or a configured token, for a session
api/v1/challengeFetches a CSRF token for an existing session, or deletes the session cookie
api/v1/security.oauth · .samlReturns the URL to redirect the browser to
Response header sinequa-jwt-refreshPresent on any response: a refreshed CSRF token

Signing in produces two things

POST security.webtoken (user, password) Set-Cookie JWT, HttpOnly — body carries csrfToken POST api/v1/query, with Sinequa-csrf-token 200 + header sinequa-jwt-refresh the JWT is never readable by JavaScript any response may refresh the token Your application Sinequa server

Two consequences shape everything else:

  • The JWT lives in an HTTP-only cookie. Your code cannot read it, and does not need to. This is why requests are sent with credentials: "include" and why a cross-origin deployment needs CORS configured with credentials.
  • What the library stores is the CSRF token, sent back as the Sinequa-csrf-token header. That is what getToken() returns — not the JWT, despite what some older names suggest.

Why the cookie matters even in bearer mode

An <iframe> showing a document preview, an <img> thumbnail, a download link — none of them can carry an Authorization header. They authenticate with the cookie or not at all. That is why even token-based sign-in tries to obtain a session cookie; see Bearer token.

The sliding refresh

Any response may carry sinequa-jwt-refresh. When it does, the library stores the new token and emits tokenRefreshed. An active user therefore never sees the session expire: each request pushes the expiry forward.

The corollary is the failure you will see: a session that expires while the user was idle, surfacing as a 401 on their next action. Recovering from that is Handling a 401.

The two probes, and why they are different

This distinction explains most of the mode-specific behaviour, and it is not obvious:

Prop

Type

Ambient SSO only authenticates on the second one. A sign-in that used the first and concluded "not authenticated" is how a token-less SSO session used to become unrecoverable.

What login() does

One function, three branches, in this order:

yes no yes no session found none credentials oauth · saml sso unknown login(credentials?) credentials passed? post security.webtokenstore the CSRF token mode is bearer? exchange the token for a cookie,then validate the token itself hasSession(): stored token,else getCsrfToken() resolve the mode, emit authenticated mode return false —the form takes over redirect to the providerthe page is left tryAutoAuthentication() tryAutoAuthentication(),then fall back to credentials

Note what login() returns in each case, because it is not uniform:

SituationResult
Session already presenttrue
Credentials acceptedtrue
Credentials refusedthrows UnauthorizedError
credentials mode, no argumentsfalse — render the form
oauth/saml, no sessionnavigates away; false if it did not
sso, handshake failedfalse, and the mode is left alone
bearer, token missing or refusedthrows — there is no fallback screen for it

Where the session is kept

WhatWhereWhy it matters
The JWTAn HTTP-only cookieInvisible to your code; carries <iframe> and <img> requests
The CSRF tokensessionStorage, under the client's storagePrefixCleared by closing the tab
"Authenticated, no token"sessionStorageToken-less ambient SSO has no token to check, so a flag stands in
"Last sign-in was a form"localStorageShared across tabs of an origin; used to resolve an ambiguous mode

All keys are prefixed by the client's storagePrefix, which is what lets two clients on one page keep separate sessions. See Multiple backends.

Storage is injectable, and that is not just for tests

auth.storage accepts your own implementation, and the library falls back to an in-memory store when window is absent. That is what makes it work in Node, in a worker, and in SSR — and what makes tests deterministic without mocking modules.

Pitfalls

What's next

On this page