The current user
Reading who is signed in, their administrative flags, and understanding why security never needs checking client-side.
One call gives you the signed-in identity:
const principal = await client.api.principal.get();It is also the call the library uses as its authentication probe, which is why it is the cheapest way to confirm a session is real — see Prerequisites.
Concept — a principal
A principal is an identity known to the security layer: a user or a group. The signed-in user is one, and so is every group they belong to. Documents carry access lists of principals, which is how the engine decides who sees what.
What you get
Prop
Type
Reading it once, at start-up
import { createAtomicClient, expiresSoon, isExpired } from "@sinequa/atomic";
import type { Principal } from "@sinequa/atomic";
const client = createAtomicClient({
app: "<your-app>",
backendUrl: "https://<your-sinequa-server>",
});
await client.initializeConfig();
await client.auth.login();
// Fetch once and keep it: the identity does not change during a session.
const principal: Principal = await client.api.principal.get();
console.log(`signed in as ${principal.name} (${principal.email})`);
if (principal.isAdministrator) enableAdministration();
// Both helpers are exported, and both tolerate an undefined date.
if (isExpired(principal.passwordExpirationDate ?? undefined)) {
console.warn("Your password has expired.");
} else if (expiresSoon(principal.passwordExpirationDate ?? undefined, 7)) {
console.warn("Your password expires within a week.");
}
function enableAdministration() {
console.log("administration available");
}import { Injectable, computed, inject, resource } from "@angular/core";
import { expiresSoon, isExpired } from "@sinequa/atomic";
import { SINEQUA } from "./sinequa";
/**
* The signed-in identity, fetched once. It does not change during a session, so
* there is no reason to re-request it per view.
*/
@Injectable({ providedIn: "root" })
export class UserService {
private readonly client = inject(SINEQUA);
readonly principal = resource({ loader: () => this.client.api.principal.get() });
readonly name = computed(() => this.principal.value()?.name ?? "");
/** Gate administration on the flag, never on a name or an id. */
readonly isAdmin = computed(() => this.principal.value()?.isAdministrator === true);
readonly passwordWarning = computed(() => {
const date = this.principal.value()?.passwordExpirationDate ?? undefined;
if (isExpired(date)) return "Your password has expired.";
if (expiresSoon(date, 7)) return "Your password expires within a week.";
return undefined;
});
}import { expiresSoon, isExpired } from "@sinequa/atomic";
import type { Principal } from "@sinequa/atomic";
import { useEffect, useState } from "react";
import { ready, sinequa } from "./sinequa";
/**
* The signed-in identity, fetched once. It does not change during a session, so
* there is no reason to re-request it per component.
*/
export function useUser() {
const [principal, setPrincipal] = useState<Principal>();
useEffect(() => {
let active = true;
ready
.then(() => sinequa.api.principal.get())
.then((next) => active && setPrincipal(next));
return () => {
active = false;
};
}, []);
const date = principal?.passwordExpirationDate ?? undefined;
return {
principal,
// Gate administration on the flag, never on a name or an id.
isAdmin: principal?.isAdministrator === true,
passwordWarning: isExpired(date)
? "Your password has expired."
: expiresSoon(date, 7)
? "Your password expires within a week."
: undefined,
};
}Security is not your job
This is the part worth internalising: access rights are enforced in the index, per document, from the access lists captured when the document was crawled. Two consequences:
- Results are already filtered. A document the user may not see never appears in
records, and is not counted inrowCount. There is nothing to check, and nothing to hide client-side. - Two users legitimately get different counts for the identical query. That is not a caching bug.
A record's accesslists field describes those rights when you need to display them — an "who can see this"
panel — but never to decide whether to render a result.
Looking up other people
principal.get() is the signed-in user. For anyone else there are two different calls, and picking the wrong
one is a common mistake:
| Call | For |
|---|---|
principal.userid(id) / principal.userids(ids) | Resolving known identifiers to principals |
principal.list({ search, isuser, isgroup }) | Browsing the directory |
principal.search({ offset, limit, search }) | Search-as-you-type, with pagination and cancellation |
The last one is the one an owner-picker wants; see Finding principals.